sync
[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         // see if target is one of the 'cards'...
2258         var ctarget = false;
2259         var cards = [];
2260         for (var i = 0;i< this.items.length;i++) {
2261             if (this.items[i].xtype != 'Card') {
2262                 continue;
2263             }
2264             cards.push(this.items[i].el.dom);
2265             if (target == this.items[i].el.dom) {
2266                 ctarget = target;
2267             }
2268         }
2269         
2270         if (!ctarget) {
2271             ctarget = cards[cards.length-1] || this.el.dom;
2272         }
2273         
2274         
2275         Roo.log(['getTargetFromEvent', ctarget]);
2276         return ctarget;
2277     },
2278     
2279     onNodeEnter : function(n, dd, e, data){
2280         return false;
2281     },
2282     onNodeOver : function(n, dd, e, data)
2283     {
2284         var pt = this.getDropPoint(e, n, dd);
2285         // set the insert point style on the target node
2286         //var dragElClass = this.dropNotAllowed;
2287         if (pt) {
2288             Roo.log(pt);
2289         }
2290         return false; //dragElClass;
2291     },
2292     onNodeOut : function(n, dd, e, data){
2293         //this.removeDropIndicators(n);
2294     },
2295     onNodeDrop : function(n, dd, e, data)
2296     {
2297         return false;
2298         
2299         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
2300                 return false;
2301         }
2302         var pt = this.getDropPoint(e, n, dd);
2303         var insertAt = (n == this.bodyEl.dom) ? this.items.length : n.nodeIndex;
2304         if (pt == "below") {
2305             insertAt++;
2306         }
2307         for (var i = 0; i < this.items.length; i++) {
2308             var r = this.items[i];
2309             //var dup = this.store.getById(r.id);
2310             if (dup && (dd != this.dragZone)) {
2311                     Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
2312             } else {
2313                 if (data.copy) {
2314                     this.store.insert(insertAt++, r.copy());
2315                 } else {
2316                     data.source.isDirtyFlag = true;
2317                     r.store.remove(r);
2318                     this.store.insert(insertAt++, r);
2319                 }
2320                 this.isDirtyFlag = true;
2321             }
2322         }
2323         this.dragZone.cachedTarget = null;
2324         return true;
2325     },
2326     
2327     /** Decide whether to drop above or below a View node. */
2328     getDropPoint : function(e, n, dd)
2329     {
2330         if (n == this.bodyEl.dom) {
2331                 return "above";
2332         }
2333         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2334         var c = t + (b - t) / 2;
2335         var y = Roo.lib.Event.getPageY(e);
2336         if(y <= c) {
2337                 return "above";
2338         }else{
2339                 return "below";
2340         }
2341     },
2342     onToggleCollapse : function(e)
2343     {
2344         if (this.collapsed) {
2345             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2346             this.el.select('.roo-collapsable').addClass('show');
2347             this.collapsed = false;
2348             return;
2349         }
2350         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2351         this.el.select('.roo-collapsable').removeClass('show');
2352         this.collapsed = true;
2353         
2354         
2355     }
2356
2357     
2358 });
2359
2360 /*
2361  * - LGPL
2362  *
2363  * Card header - holder for the card header elements.
2364  * 
2365  */
2366
2367 /**
2368  * @class Roo.bootstrap.CardHeader
2369  * @extends Roo.bootstrap.Element
2370  * Bootstrap CardHeader class
2371  * @constructor
2372  * Create a new Card Header - that you can embed children into
2373  * @param {Object} config The config object
2374  */
2375
2376 Roo.bootstrap.CardHeader = function(config){
2377     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2378 };
2379
2380 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2381     
2382     
2383     container_method : 'getCardHeader' 
2384     
2385      
2386     
2387     
2388    
2389 });
2390
2391  
2392
2393  /*
2394  * - LGPL
2395  *
2396  * Card header - holder for the card header elements.
2397  * 
2398  */
2399
2400 /**
2401  * @class Roo.bootstrap.CardImageTop
2402  * @extends Roo.bootstrap.Element
2403  * Bootstrap CardImageTop class
2404  * @constructor
2405  * Create a new Card Image Top container
2406  * @param {Object} config The config object
2407  */
2408
2409 Roo.bootstrap.CardImageTop = function(config){
2410     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2411 };
2412
2413 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2414     
2415    
2416     container_method : 'getCardImageTop' 
2417     
2418      
2419     
2420    
2421 });
2422
2423  
2424
2425  /*
2426  * - LGPL
2427  *
2428  * image
2429  * 
2430  */
2431
2432
2433 /**
2434  * @class Roo.bootstrap.Img
2435  * @extends Roo.bootstrap.Component
2436  * Bootstrap Img class
2437  * @cfg {Boolean} imgResponsive false | true
2438  * @cfg {String} border rounded | circle | thumbnail
2439  * @cfg {String} src image source
2440  * @cfg {String} alt image alternative text
2441  * @cfg {String} href a tag href
2442  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2443  * @cfg {String} xsUrl xs image source
2444  * @cfg {String} smUrl sm image source
2445  * @cfg {String} mdUrl md image source
2446  * @cfg {String} lgUrl lg image source
2447  * 
2448  * @constructor
2449  * Create a new Input
2450  * @param {Object} config The config object
2451  */
2452
2453 Roo.bootstrap.Img = function(config){
2454     Roo.bootstrap.Img.superclass.constructor.call(this, config);
2455     
2456     this.addEvents({
2457         // img events
2458         /**
2459          * @event click
2460          * The img click event for the img.
2461          * @param {Roo.EventObject} e
2462          */
2463         "click" : true
2464     });
2465 };
2466
2467 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
2468     
2469     imgResponsive: true,
2470     border: '',
2471     src: 'about:blank',
2472     href: false,
2473     target: false,
2474     xsUrl: '',
2475     smUrl: '',
2476     mdUrl: '',
2477     lgUrl: '',
2478
2479     getAutoCreate : function()
2480     {   
2481         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2482             return this.createSingleImg();
2483         }
2484         
2485         var cfg = {
2486             tag: 'div',
2487             cls: 'roo-image-responsive-group',
2488             cn: []
2489         };
2490         var _this = this;
2491         
2492         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2493             
2494             if(!_this[size + 'Url']){
2495                 return;
2496             }
2497             
2498             var img = {
2499                 tag: 'img',
2500                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2501                 html: _this.html || cfg.html,
2502                 src: _this[size + 'Url']
2503             };
2504             
2505             img.cls += ' roo-image-responsive-' + size;
2506             
2507             var s = ['xs', 'sm', 'md', 'lg'];
2508             
2509             s.splice(s.indexOf(size), 1);
2510             
2511             Roo.each(s, function(ss){
2512                 img.cls += ' hidden-' + ss;
2513             });
2514             
2515             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2516                 cfg.cls += ' img-' + _this.border;
2517             }
2518             
2519             if(_this.alt){
2520                 cfg.alt = _this.alt;
2521             }
2522             
2523             if(_this.href){
2524                 var a = {
2525                     tag: 'a',
2526                     href: _this.href,
2527                     cn: [
2528                         img
2529                     ]
2530                 };
2531
2532                 if(this.target){
2533                     a.target = _this.target;
2534                 }
2535             }
2536             
2537             cfg.cn.push((_this.href) ? a : img);
2538             
2539         });
2540         
2541         return cfg;
2542     },
2543     
2544     createSingleImg : function()
2545     {
2546         var cfg = {
2547             tag: 'img',
2548             cls: (this.imgResponsive) ? 'img-responsive' : '',
2549             html : null,
2550             src : 'about:blank'  // just incase src get's set to undefined?!?
2551         };
2552         
2553         cfg.html = this.html || cfg.html;
2554         
2555         cfg.src = this.src || cfg.src;
2556         
2557         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2558             cfg.cls += ' img-' + this.border;
2559         }
2560         
2561         if(this.alt){
2562             cfg.alt = this.alt;
2563         }
2564         
2565         if(this.href){
2566             var a = {
2567                 tag: 'a',
2568                 href: this.href,
2569                 cn: [
2570                     cfg
2571                 ]
2572             };
2573             
2574             if(this.target){
2575                 a.target = this.target;
2576             }
2577             
2578         }
2579         
2580         return (this.href) ? a : cfg;
2581     },
2582     
2583     initEvents: function() 
2584     {
2585         if(!this.href){
2586             this.el.on('click', this.onClick, this);
2587         }
2588         
2589     },
2590     
2591     onClick : function(e)
2592     {
2593         Roo.log('img onclick');
2594         this.fireEvent('click', this, e);
2595     },
2596     /**
2597      * Sets the url of the image - used to update it
2598      * @param {String} url the url of the image
2599      */
2600     
2601     setSrc : function(url)
2602     {
2603         this.src =  url;
2604         
2605         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2606             this.el.dom.src =  url;
2607             return;
2608         }
2609         
2610         this.el.select('img', true).first().dom.src =  url;
2611     }
2612     
2613     
2614    
2615 });
2616
2617  /*
2618  * - LGPL
2619  *
2620  * image
2621  * 
2622  */
2623
2624
2625 /**
2626  * @class Roo.bootstrap.Link
2627  * @extends Roo.bootstrap.Component
2628  * Bootstrap Link Class
2629  * @cfg {String} alt image alternative text
2630  * @cfg {String} href a tag href
2631  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
2632  * @cfg {String} html the content of the link.
2633  * @cfg {String} anchor name for the anchor link
2634  * @cfg {String} fa - favicon
2635
2636  * @cfg {Boolean} preventDefault (true | false) default false
2637
2638  * 
2639  * @constructor
2640  * Create a new Input
2641  * @param {Object} config The config object
2642  */
2643
2644 Roo.bootstrap.Link = function(config){
2645     Roo.bootstrap.Link.superclass.constructor.call(this, config);
2646     
2647     this.addEvents({
2648         // img events
2649         /**
2650          * @event click
2651          * The img click event for the img.
2652          * @param {Roo.EventObject} e
2653          */
2654         "click" : true
2655     });
2656 };
2657
2658 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
2659     
2660     href: false,
2661     target: false,
2662     preventDefault: false,
2663     anchor : false,
2664     alt : false,
2665     fa: false,
2666
2667
2668     getAutoCreate : function()
2669     {
2670         var html = this.html || '';
2671         
2672         if (this.fa !== false) {
2673             html = '<i class="fa fa-' + this.fa + '"></i>';
2674         }
2675         var cfg = {
2676             tag: 'a'
2677         };
2678         // anchor's do not require html/href...
2679         if (this.anchor === false) {
2680             cfg.html = html;
2681             cfg.href = this.href || '#';
2682         } else {
2683             cfg.name = this.anchor;
2684             if (this.html !== false || this.fa !== false) {
2685                 cfg.html = html;
2686             }
2687             if (this.href !== false) {
2688                 cfg.href = this.href;
2689             }
2690         }
2691         
2692         if(this.alt !== false){
2693             cfg.alt = this.alt;
2694         }
2695         
2696         
2697         if(this.target !== false) {
2698             cfg.target = this.target;
2699         }
2700         
2701         return cfg;
2702     },
2703     
2704     initEvents: function() {
2705         
2706         if(!this.href || this.preventDefault){
2707             this.el.on('click', this.onClick, this);
2708         }
2709     },
2710     
2711     onClick : function(e)
2712     {
2713         if(this.preventDefault){
2714             e.preventDefault();
2715         }
2716         //Roo.log('img onclick');
2717         this.fireEvent('click', this, e);
2718     }
2719    
2720 });
2721
2722  /*
2723  * - LGPL
2724  *
2725  * header
2726  * 
2727  */
2728
2729 /**
2730  * @class Roo.bootstrap.Header
2731  * @extends Roo.bootstrap.Component
2732  * Bootstrap Header class
2733  * @cfg {String} html content of header
2734  * @cfg {Number} level (1|2|3|4|5|6) default 1
2735  * 
2736  * @constructor
2737  * Create a new Header
2738  * @param {Object} config The config object
2739  */
2740
2741
2742 Roo.bootstrap.Header  = function(config){
2743     Roo.bootstrap.Header.superclass.constructor.call(this, config);
2744 };
2745
2746 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
2747     
2748     //href : false,
2749     html : false,
2750     level : 1,
2751     
2752     
2753     
2754     getAutoCreate : function(){
2755         
2756         
2757         
2758         var cfg = {
2759             tag: 'h' + (1 *this.level),
2760             html: this.html || ''
2761         } ;
2762         
2763         return cfg;
2764     }
2765    
2766 });
2767
2768  
2769
2770  /*
2771  * Based on:
2772  * Ext JS Library 1.1.1
2773  * Copyright(c) 2006-2007, Ext JS, LLC.
2774  *
2775  * Originally Released Under LGPL - original licence link has changed is not relivant.
2776  *
2777  * Fork - LGPL
2778  * <script type="text/javascript">
2779  */
2780  
2781 /**
2782  * @class Roo.bootstrap.MenuMgr
2783  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
2784  * @singleton
2785  */
2786 Roo.bootstrap.MenuMgr = function(){
2787    var menus, active, groups = {}, attached = false, lastShow = new Date();
2788
2789    // private - called when first menu is created
2790    function init(){
2791        menus = {};
2792        active = new Roo.util.MixedCollection();
2793        Roo.get(document).addKeyListener(27, function(){
2794            if(active.length > 0){
2795                hideAll();
2796            }
2797        });
2798    }
2799
2800    // private
2801    function hideAll(){
2802        if(active && active.length > 0){
2803            var c = active.clone();
2804            c.each(function(m){
2805                m.hide();
2806            });
2807        }
2808    }
2809
2810    // private
2811    function onHide(m){
2812        active.remove(m);
2813        if(active.length < 1){
2814            Roo.get(document).un("mouseup", onMouseDown);
2815             
2816            attached = false;
2817        }
2818    }
2819
2820    // private
2821    function onShow(m){
2822        var last = active.last();
2823        lastShow = new Date();
2824        active.add(m);
2825        if(!attached){
2826           Roo.get(document).on("mouseup", onMouseDown);
2827            
2828            attached = true;
2829        }
2830        if(m.parentMenu){
2831           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
2832           m.parentMenu.activeChild = m;
2833        }else if(last && last.isVisible()){
2834           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
2835        }
2836    }
2837
2838    // private
2839    function onBeforeHide(m){
2840        if(m.activeChild){
2841            m.activeChild.hide();
2842        }
2843        if(m.autoHideTimer){
2844            clearTimeout(m.autoHideTimer);
2845            delete m.autoHideTimer;
2846        }
2847    }
2848
2849    // private
2850    function onBeforeShow(m){
2851        var pm = m.parentMenu;
2852        if(!pm && !m.allowOtherMenus){
2853            hideAll();
2854        }else if(pm && pm.activeChild && active != m){
2855            pm.activeChild.hide();
2856        }
2857    }
2858
2859    // private this should really trigger on mouseup..
2860    function onMouseDown(e){
2861         Roo.log("on Mouse Up");
2862         
2863         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
2864             Roo.log("MenuManager hideAll");
2865             hideAll();
2866             e.stopEvent();
2867         }
2868         
2869         
2870    }
2871
2872    // private
2873    function onBeforeCheck(mi, state){
2874        if(state){
2875            var g = groups[mi.group];
2876            for(var i = 0, l = g.length; i < l; i++){
2877                if(g[i] != mi){
2878                    g[i].setChecked(false);
2879                }
2880            }
2881        }
2882    }
2883
2884    return {
2885
2886        /**
2887         * Hides all menus that are currently visible
2888         */
2889        hideAll : function(){
2890             hideAll();  
2891        },
2892
2893        // private
2894        register : function(menu){
2895            if(!menus){
2896                init();
2897            }
2898            menus[menu.id] = menu;
2899            menu.on("beforehide", onBeforeHide);
2900            menu.on("hide", onHide);
2901            menu.on("beforeshow", onBeforeShow);
2902            menu.on("show", onShow);
2903            var g = menu.group;
2904            if(g && menu.events["checkchange"]){
2905                if(!groups[g]){
2906                    groups[g] = [];
2907                }
2908                groups[g].push(menu);
2909                menu.on("checkchange", onCheck);
2910            }
2911        },
2912
2913         /**
2914          * Returns a {@link Roo.menu.Menu} object
2915          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
2916          * be used to generate and return a new Menu instance.
2917          */
2918        get : function(menu){
2919            if(typeof menu == "string"){ // menu id
2920                return menus[menu];
2921            }else if(menu.events){  // menu instance
2922                return menu;
2923            }
2924            /*else if(typeof menu.length == 'number'){ // array of menu items?
2925                return new Roo.bootstrap.Menu({items:menu});
2926            }else{ // otherwise, must be a config
2927                return new Roo.bootstrap.Menu(menu);
2928            }
2929            */
2930            return false;
2931        },
2932
2933        // private
2934        unregister : function(menu){
2935            delete menus[menu.id];
2936            menu.un("beforehide", onBeforeHide);
2937            menu.un("hide", onHide);
2938            menu.un("beforeshow", onBeforeShow);
2939            menu.un("show", onShow);
2940            var g = menu.group;
2941            if(g && menu.events["checkchange"]){
2942                groups[g].remove(menu);
2943                menu.un("checkchange", onCheck);
2944            }
2945        },
2946
2947        // private
2948        registerCheckable : function(menuItem){
2949            var g = menuItem.group;
2950            if(g){
2951                if(!groups[g]){
2952                    groups[g] = [];
2953                }
2954                groups[g].push(menuItem);
2955                menuItem.on("beforecheckchange", onBeforeCheck);
2956            }
2957        },
2958
2959        // private
2960        unregisterCheckable : function(menuItem){
2961            var g = menuItem.group;
2962            if(g){
2963                groups[g].remove(menuItem);
2964                menuItem.un("beforecheckchange", onBeforeCheck);
2965            }
2966        }
2967    };
2968 }();/*
2969  * - LGPL
2970  *
2971  * menu
2972  * 
2973  */
2974
2975 /**
2976  * @class Roo.bootstrap.Menu
2977  * @extends Roo.bootstrap.Component
2978  * Bootstrap Menu class - container for MenuItems
2979  * @cfg {String} type (dropdown|treeview|submenu) type of menu
2980  * @cfg {bool} hidden  if the menu should be hidden when rendered.
2981  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
2982  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
2983  * 
2984  * @constructor
2985  * Create a new Menu
2986  * @param {Object} config The config object
2987  */
2988
2989
2990 Roo.bootstrap.Menu = function(config){
2991     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
2992     if (this.registerMenu && this.type != 'treeview')  {
2993         Roo.bootstrap.MenuMgr.register(this);
2994     }
2995     
2996     
2997     this.addEvents({
2998         /**
2999          * @event beforeshow
3000          * Fires before this menu is displayed (return false to block)
3001          * @param {Roo.menu.Menu} this
3002          */
3003         beforeshow : true,
3004         /**
3005          * @event beforehide
3006          * Fires before this menu is hidden (return false to block)
3007          * @param {Roo.menu.Menu} this
3008          */
3009         beforehide : true,
3010         /**
3011          * @event show
3012          * Fires after this menu is displayed
3013          * @param {Roo.menu.Menu} this
3014          */
3015         show : true,
3016         /**
3017          * @event hide
3018          * Fires after this menu is hidden
3019          * @param {Roo.menu.Menu} this
3020          */
3021         hide : true,
3022         /**
3023          * @event click
3024          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3025          * @param {Roo.menu.Menu} this
3026          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3027          * @param {Roo.EventObject} e
3028          */
3029         click : true,
3030         /**
3031          * @event mouseover
3032          * Fires when the mouse is hovering over this menu
3033          * @param {Roo.menu.Menu} this
3034          * @param {Roo.EventObject} e
3035          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3036          */
3037         mouseover : true,
3038         /**
3039          * @event mouseout
3040          * Fires when the mouse exits this menu
3041          * @param {Roo.menu.Menu} this
3042          * @param {Roo.EventObject} e
3043          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3044          */
3045         mouseout : true,
3046         /**
3047          * @event itemclick
3048          * Fires when a menu item contained in this menu is clicked
3049          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3050          * @param {Roo.EventObject} e
3051          */
3052         itemclick: true
3053     });
3054     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3055 };
3056
3057 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3058     
3059    /// html : false,
3060     //align : '',
3061     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3062     type: false,
3063     /**
3064      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3065      */
3066     registerMenu : true,
3067     
3068     menuItems :false, // stores the menu items..
3069     
3070     hidden:true,
3071         
3072     parentMenu : false,
3073     
3074     stopEvent : true,
3075     
3076     isLink : false,
3077     
3078     getChildContainer : function() {
3079         return this.el;  
3080     },
3081     
3082     getAutoCreate : function(){
3083          
3084         //if (['right'].indexOf(this.align)!==-1) {
3085         //    cfg.cn[1].cls += ' pull-right'
3086         //}
3087         
3088         
3089         var cfg = {
3090             tag : 'ul',
3091             cls : 'dropdown-menu' ,
3092             style : 'z-index:1000'
3093             
3094         };
3095         
3096         if (this.type === 'submenu') {
3097             cfg.cls = 'submenu active';
3098         }
3099         if (this.type === 'treeview') {
3100             cfg.cls = 'treeview-menu';
3101         }
3102         
3103         return cfg;
3104     },
3105     initEvents : function() {
3106         
3107        // Roo.log("ADD event");
3108        // Roo.log(this.triggerEl.dom);
3109         
3110         this.triggerEl.on('click', this.onTriggerClick, this);
3111         
3112         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3113         
3114         
3115         if (this.triggerEl.hasClass('nav-item')) {
3116             // dropdown toggle on the 'a' in BS4?
3117             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3118         } else {
3119             this.triggerEl.addClass('dropdown-toggle');
3120         }
3121         if (Roo.isTouch) {
3122             this.el.on('touchstart'  , this.onTouch, this);
3123         }
3124         this.el.on('click' , this.onClick, this);
3125
3126         this.el.on("mouseover", this.onMouseOver, this);
3127         this.el.on("mouseout", this.onMouseOut, this);
3128         
3129     },
3130     
3131     findTargetItem : function(e)
3132     {
3133         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3134         if(!t){
3135             return false;
3136         }
3137         //Roo.log(t);         Roo.log(t.id);
3138         if(t && t.id){
3139             //Roo.log(this.menuitems);
3140             return this.menuitems.get(t.id);
3141             
3142             //return this.items.get(t.menuItemId);
3143         }
3144         
3145         return false;
3146     },
3147     
3148     onTouch : function(e) 
3149     {
3150         Roo.log("menu.onTouch");
3151         //e.stopEvent(); this make the user popdown broken
3152         this.onClick(e);
3153     },
3154     
3155     onClick : function(e)
3156     {
3157         Roo.log("menu.onClick");
3158         
3159         var t = this.findTargetItem(e);
3160         if(!t || t.isContainer){
3161             return;
3162         }
3163         Roo.log(e);
3164         /*
3165         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3166             if(t == this.activeItem && t.shouldDeactivate(e)){
3167                 this.activeItem.deactivate();
3168                 delete this.activeItem;
3169                 return;
3170             }
3171             if(t.canActivate){
3172                 this.setActiveItem(t, true);
3173             }
3174             return;
3175             
3176             
3177         }
3178         */
3179        
3180         Roo.log('pass click event');
3181         
3182         t.onClick(e);
3183         
3184         this.fireEvent("click", this, t, e);
3185         
3186         var _this = this;
3187         
3188         if(!t.href.length || t.href == '#'){
3189             (function() { _this.hide(); }).defer(100);
3190         }
3191         
3192     },
3193     
3194     onMouseOver : function(e){
3195         var t  = this.findTargetItem(e);
3196         //Roo.log(t);
3197         //if(t){
3198         //    if(t.canActivate && !t.disabled){
3199         //        this.setActiveItem(t, true);
3200         //    }
3201         //}
3202         
3203         this.fireEvent("mouseover", this, e, t);
3204     },
3205     isVisible : function(){
3206         return !this.hidden;
3207     },
3208     onMouseOut : function(e){
3209         var t  = this.findTargetItem(e);
3210         
3211         //if(t ){
3212         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3213         //        this.activeItem.deactivate();
3214         //        delete this.activeItem;
3215         //    }
3216         //}
3217         this.fireEvent("mouseout", this, e, t);
3218     },
3219     
3220     
3221     /**
3222      * Displays this menu relative to another element
3223      * @param {String/HTMLElement/Roo.Element} element The element to align to
3224      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3225      * the element (defaults to this.defaultAlign)
3226      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3227      */
3228     show : function(el, pos, parentMenu)
3229     {
3230         if (false === this.fireEvent("beforeshow", this)) {
3231             Roo.log("show canceled");
3232             return;
3233         }
3234         this.parentMenu = parentMenu;
3235         if(!this.el){
3236             this.render();
3237         }
3238         
3239         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3240     },
3241      /**
3242      * Displays this menu at a specific xy position
3243      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3244      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3245      */
3246     showAt : function(xy, parentMenu, /* private: */_e){
3247         this.parentMenu = parentMenu;
3248         if(!this.el){
3249             this.render();
3250         }
3251         if(_e !== false){
3252             this.fireEvent("beforeshow", this);
3253             //xy = this.el.adjustForConstraints(xy);
3254         }
3255         
3256         //this.el.show();
3257         this.hideMenuItems();
3258         this.hidden = false;
3259         this.triggerEl.addClass('open');
3260         this.el.addClass('show');
3261         
3262         // reassign x when hitting right
3263         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3264             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3265         }
3266         
3267         // reassign y when hitting bottom
3268         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3269             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3270         }
3271         
3272         // but the list may align on trigger left or trigger top... should it be a properity?
3273         
3274         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3275             this.el.setXY(xy);
3276         }
3277         
3278         this.focus();
3279         this.fireEvent("show", this);
3280     },
3281     
3282     focus : function(){
3283         return;
3284         if(!this.hidden){
3285             this.doFocus.defer(50, this);
3286         }
3287     },
3288
3289     doFocus : function(){
3290         if(!this.hidden){
3291             this.focusEl.focus();
3292         }
3293     },
3294
3295     /**
3296      * Hides this menu and optionally all parent menus
3297      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3298      */
3299     hide : function(deep)
3300     {
3301         if (false === this.fireEvent("beforehide", this)) {
3302             Roo.log("hide canceled");
3303             return;
3304         }
3305         this.hideMenuItems();
3306         if(this.el && this.isVisible()){
3307            
3308             if(this.activeItem){
3309                 this.activeItem.deactivate();
3310                 this.activeItem = null;
3311             }
3312             this.triggerEl.removeClass('open');;
3313             this.el.removeClass('show');
3314             this.hidden = true;
3315             this.fireEvent("hide", this);
3316         }
3317         if(deep === true && this.parentMenu){
3318             this.parentMenu.hide(true);
3319         }
3320     },
3321     
3322     onTriggerClick : function(e)
3323     {
3324         Roo.log('trigger click');
3325         
3326         var target = e.getTarget();
3327         
3328         Roo.log(target.nodeName.toLowerCase());
3329         
3330         if(target.nodeName.toLowerCase() === 'i'){
3331             e.preventDefault();
3332         }
3333         
3334     },
3335     
3336     onTriggerPress  : function(e)
3337     {
3338         Roo.log('trigger press');
3339         //Roo.log(e.getTarget());
3340        // Roo.log(this.triggerEl.dom);
3341        
3342         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3343         var pel = Roo.get(e.getTarget());
3344         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3345             Roo.log('is treeview or dropdown?');
3346             return;
3347         }
3348         
3349         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3350             return;
3351         }
3352         
3353         if (this.isVisible()) {
3354             Roo.log('hide');
3355             this.hide();
3356         } else {
3357             Roo.log('show');
3358             this.show(this.triggerEl, '?', false);
3359         }
3360         
3361         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3362             e.stopEvent();
3363         }
3364         
3365     },
3366        
3367     
3368     hideMenuItems : function()
3369     {
3370         Roo.log("hide Menu Items");
3371         if (!this.el) { 
3372             return;
3373         }
3374         
3375         this.el.select('.open',true).each(function(aa) {
3376             
3377             aa.removeClass('open');
3378          
3379         });
3380     },
3381     addxtypeChild : function (tree, cntr) {
3382         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3383           
3384         this.menuitems.add(comp);
3385         return comp;
3386
3387     },
3388     getEl : function()
3389     {
3390         Roo.log(this.el);
3391         return this.el;
3392     },
3393     
3394     clear : function()
3395     {
3396         this.getEl().dom.innerHTML = '';
3397         this.menuitems.clear();
3398     }
3399 });
3400
3401  
3402  /*
3403  * - LGPL
3404  *
3405  * menu item
3406  * 
3407  */
3408
3409
3410 /**
3411  * @class Roo.bootstrap.MenuItem
3412  * @extends Roo.bootstrap.Component
3413  * Bootstrap MenuItem class
3414  * @cfg {String} html the menu label
3415  * @cfg {String} href the link
3416  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3417  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3418  * @cfg {Boolean} active  used on sidebars to highlight active itesm
3419  * @cfg {String} fa favicon to show on left of menu item.
3420  * @cfg {Roo.bootsrap.Menu} menu the child menu.
3421  * 
3422  * 
3423  * @constructor
3424  * Create a new MenuItem
3425  * @param {Object} config The config object
3426  */
3427
3428
3429 Roo.bootstrap.MenuItem = function(config){
3430     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3431     this.addEvents({
3432         // raw events
3433         /**
3434          * @event click
3435          * The raw click event for the entire grid.
3436          * @param {Roo.bootstrap.MenuItem} this
3437          * @param {Roo.EventObject} e
3438          */
3439         "click" : true
3440     });
3441 };
3442
3443 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
3444     
3445     href : false,
3446     html : false,
3447     preventDefault: false,
3448     isContainer : false,
3449     active : false,
3450     fa: false,
3451     
3452     getAutoCreate : function(){
3453         
3454         if(this.isContainer){
3455             return {
3456                 tag: 'li',
3457                 cls: 'dropdown-menu-item '
3458             };
3459         }
3460         var ctag = {
3461             tag: 'span',
3462             html: 'Link'
3463         };
3464         
3465         var anc = {
3466             tag : 'a',
3467             cls : 'dropdown-item',
3468             href : '#',
3469             cn : [  ]
3470         };
3471         
3472         if (this.fa !== false) {
3473             anc.cn.push({
3474                 tag : 'i',
3475                 cls : 'fa fa-' + this.fa
3476             });
3477         }
3478         
3479         anc.cn.push(ctag);
3480         
3481         
3482         var cfg= {
3483             tag: 'li',
3484             cls: 'dropdown-menu-item',
3485             cn: [ anc ]
3486         };
3487         if (this.parent().type == 'treeview') {
3488             cfg.cls = 'treeview-menu';
3489         }
3490         if (this.active) {
3491             cfg.cls += ' active';
3492         }
3493         
3494         
3495         
3496         anc.href = this.href || cfg.cn[0].href ;
3497         ctag.html = this.html || cfg.cn[0].html ;
3498         return cfg;
3499     },
3500     
3501     initEvents: function()
3502     {
3503         if (this.parent().type == 'treeview') {
3504             this.el.select('a').on('click', this.onClick, this);
3505         }
3506         
3507         if (this.menu) {
3508             this.menu.parentType = this.xtype;
3509             this.menu.triggerEl = this.el;
3510             this.menu = this.addxtype(Roo.apply({}, this.menu));
3511         }
3512         
3513     },
3514     onClick : function(e)
3515     {
3516         Roo.log('item on click ');
3517         
3518         if(this.preventDefault){
3519             e.preventDefault();
3520         }
3521         //this.parent().hideMenuItems();
3522         
3523         this.fireEvent('click', this, e);
3524     },
3525     getEl : function()
3526     {
3527         return this.el;
3528     } 
3529 });
3530
3531  
3532
3533  /*
3534  * - LGPL
3535  *
3536  * menu separator
3537  * 
3538  */
3539
3540
3541 /**
3542  * @class Roo.bootstrap.MenuSeparator
3543  * @extends Roo.bootstrap.Component
3544  * Bootstrap MenuSeparator class
3545  * 
3546  * @constructor
3547  * Create a new MenuItem
3548  * @param {Object} config The config object
3549  */
3550
3551
3552 Roo.bootstrap.MenuSeparator = function(config){
3553     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3554 };
3555
3556 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
3557     
3558     getAutoCreate : function(){
3559         var cfg = {
3560             cls: 'divider',
3561             tag : 'li'
3562         };
3563         
3564         return cfg;
3565     }
3566    
3567 });
3568
3569  
3570
3571  
3572 /*
3573 * Licence: LGPL
3574 */
3575
3576 /**
3577  * @class Roo.bootstrap.Modal
3578  * @extends Roo.bootstrap.Component
3579  * Bootstrap Modal class
3580  * @cfg {String} title Title of dialog
3581  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3582  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
3583  * @cfg {Boolean} specificTitle default false
3584  * @cfg {Array} buttons Array of buttons or standard button set..
3585  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3586  * @cfg {Boolean} animate default true
3587  * @cfg {Boolean} allow_close default true
3588  * @cfg {Boolean} fitwindow default false
3589  * @cfg {Number} width fixed width - usefull for chrome extension only really.
3590  * @cfg {Number} height fixed height - usefull for chrome extension only really.
3591  * @cfg {String} size (sm|lg) default empty
3592  * @cfg {Number} max_width set the max width of modal
3593  *
3594  *
3595  * @constructor
3596  * Create a new Modal Dialog
3597  * @param {Object} config The config object
3598  */
3599
3600 Roo.bootstrap.Modal = function(config){
3601     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3602     this.addEvents({
3603         // raw events
3604         /**
3605          * @event btnclick
3606          * The raw btnclick event for the button
3607          * @param {Roo.EventObject} e
3608          */
3609         "btnclick" : true,
3610         /**
3611          * @event resize
3612          * Fire when dialog resize
3613          * @param {Roo.bootstrap.Modal} this
3614          * @param {Roo.EventObject} e
3615          */
3616         "resize" : true
3617     });
3618     this.buttons = this.buttons || [];
3619
3620     if (this.tmpl) {
3621         this.tmpl = Roo.factory(this.tmpl);
3622     }
3623
3624 };
3625
3626 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
3627
3628     title : 'test dialog',
3629
3630     buttons : false,
3631
3632     // set on load...
3633
3634     html: false,
3635
3636     tmp: false,
3637
3638     specificTitle: false,
3639
3640     buttonPosition: 'right',
3641
3642     allow_close : true,
3643
3644     animate : true,
3645
3646     fitwindow: false,
3647     
3648      // private
3649     dialogEl: false,
3650     bodyEl:  false,
3651     footerEl:  false,
3652     titleEl:  false,
3653     closeEl:  false,
3654
3655     size: '',
3656     
3657     max_width: 0,
3658     
3659     max_height: 0,
3660     
3661     fit_content: false,
3662
3663     onRender : function(ct, position)
3664     {
3665         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
3666
3667         if(!this.el){
3668             var cfg = Roo.apply({},  this.getAutoCreate());
3669             cfg.id = Roo.id();
3670             //if(!cfg.name){
3671             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
3672             //}
3673             //if (!cfg.name.length) {
3674             //    delete cfg.name;
3675            // }
3676             if (this.cls) {
3677                 cfg.cls += ' ' + this.cls;
3678             }
3679             if (this.style) {
3680                 cfg.style = this.style;
3681             }
3682             this.el = Roo.get(document.body).createChild(cfg, position);
3683         }
3684         //var type = this.el.dom.type;
3685
3686
3687         if(this.tabIndex !== undefined){
3688             this.el.dom.setAttribute('tabIndex', this.tabIndex);
3689         }
3690
3691         this.dialogEl = this.el.select('.modal-dialog',true).first();
3692         this.bodyEl = this.el.select('.modal-body',true).first();
3693         this.closeEl = this.el.select('.modal-header .close', true).first();
3694         this.headerEl = this.el.select('.modal-header',true).first();
3695         this.titleEl = this.el.select('.modal-title',true).first();
3696         this.footerEl = this.el.select('.modal-footer',true).first();
3697
3698         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
3699         
3700         //this.el.addClass("x-dlg-modal");
3701
3702         if (this.buttons.length) {
3703             Roo.each(this.buttons, function(bb) {
3704                 var b = Roo.apply({}, bb);
3705                 b.xns = b.xns || Roo.bootstrap;
3706                 b.xtype = b.xtype || 'Button';
3707                 if (typeof(b.listeners) == 'undefined') {
3708                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
3709                 }
3710
3711                 var btn = Roo.factory(b);
3712
3713                 btn.render(this.getButtonContainer());
3714
3715             },this);
3716         }
3717         // render the children.
3718         var nitems = [];
3719
3720         if(typeof(this.items) != 'undefined'){
3721             var items = this.items;
3722             delete this.items;
3723
3724             for(var i =0;i < items.length;i++) {
3725                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
3726             }
3727         }
3728
3729         this.items = nitems;
3730
3731         // where are these used - they used to be body/close/footer
3732
3733
3734         this.initEvents();
3735         //this.el.addClass([this.fieldClass, this.cls]);
3736
3737     },
3738
3739     getAutoCreate : function()
3740     {
3741         // we will default to modal-body-overflow - might need to remove or make optional later.
3742         var bdy = {
3743                 cls : 'modal-body enable-modal-body-overflow ', 
3744                 html : this.html || ''
3745         };
3746
3747         var title = {
3748             tag: 'h4',
3749             cls : 'modal-title',
3750             html : this.title
3751         };
3752
3753         if(this.specificTitle){
3754             title = this.title;
3755
3756         }
3757
3758         var header = [];
3759         if (this.allow_close && Roo.bootstrap.version == 3) {
3760             header.push({
3761                 tag: 'button',
3762                 cls : 'close',
3763                 html : '&times'
3764             });
3765         }
3766
3767         header.push(title);
3768
3769         if (this.allow_close && Roo.bootstrap.version == 4) {
3770             header.push({
3771                 tag: 'button',
3772                 cls : 'close',
3773                 html : '&times'
3774             });
3775         }
3776         
3777         var size = '';
3778
3779         if(this.size.length){
3780             size = 'modal-' + this.size;
3781         }
3782         
3783         var footer = Roo.bootstrap.version == 3 ?
3784             {
3785                 cls : 'modal-footer',
3786                 cn : [
3787                     {
3788                         tag: 'div',
3789                         cls: 'btn-' + this.buttonPosition
3790                     }
3791                 ]
3792
3793             } :
3794             {  // BS4 uses mr-auto on left buttons....
3795                 cls : 'modal-footer'
3796             };
3797
3798             
3799
3800         
3801         
3802         var modal = {
3803             cls: "modal",
3804              cn : [
3805                 {
3806                     cls: "modal-dialog " + size,
3807                     cn : [
3808                         {
3809                             cls : "modal-content",
3810                             cn : [
3811                                 {
3812                                     cls : 'modal-header',
3813                                     cn : header
3814                                 },
3815                                 bdy,
3816                                 footer
3817                             ]
3818
3819                         }
3820                     ]
3821
3822                 }
3823             ]
3824         };
3825
3826         if(this.animate){
3827             modal.cls += ' fade';
3828         }
3829
3830         return modal;
3831
3832     },
3833     getChildContainer : function() {
3834
3835          return this.bodyEl;
3836
3837     },
3838     getButtonContainer : function() {
3839         
3840          return Roo.bootstrap.version == 4 ?
3841             this.el.select('.modal-footer',true).first()
3842             : this.el.select('.modal-footer div',true).first();
3843
3844     },
3845     initEvents : function()
3846     {
3847         if (this.allow_close) {
3848             this.closeEl.on('click', this.hide, this);
3849         }
3850         Roo.EventManager.onWindowResize(this.resize, this, true);
3851
3852
3853     },
3854   
3855
3856     resize : function()
3857     {
3858         this.maskEl.setSize(
3859             Roo.lib.Dom.getViewWidth(true),
3860             Roo.lib.Dom.getViewHeight(true)
3861         );
3862         
3863         if (this.fitwindow) {
3864             
3865            
3866             this.setSize(
3867                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
3868                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
3869             );
3870             return;
3871         }
3872         
3873         if(this.max_width !== 0) {
3874             
3875             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
3876             
3877             if(this.height) {
3878                 this.setSize(w, this.height);
3879                 return;
3880             }
3881             
3882             if(this.max_height) {
3883                 this.setSize(w,Math.min(
3884                     this.max_height,
3885                     Roo.lib.Dom.getViewportHeight(true) - 60
3886                 ));
3887                 
3888                 return;
3889             }
3890             
3891             if(!this.fit_content) {
3892                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
3893                 return;
3894             }
3895             
3896             this.setSize(w, Math.min(
3897                 60 +
3898                 this.headerEl.getHeight() + 
3899                 this.footerEl.getHeight() + 
3900                 this.getChildHeight(this.bodyEl.dom.childNodes),
3901                 Roo.lib.Dom.getViewportHeight(true) - 60)
3902             );
3903         }
3904         
3905     },
3906
3907     setSize : function(w,h)
3908     {
3909         if (!w && !h) {
3910             return;
3911         }
3912         
3913         this.resizeTo(w,h);
3914     },
3915
3916     show : function() {
3917
3918         if (!this.rendered) {
3919             this.render();
3920         }
3921
3922         //this.el.setStyle('display', 'block');
3923         this.el.removeClass('hideing');
3924         this.el.dom.style.display='block';
3925         
3926         Roo.get(document.body).addClass('modal-open');
3927  
3928         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
3929             
3930             (function(){
3931                 this.el.addClass('show');
3932                 this.el.addClass('in');
3933             }).defer(50, this);
3934         }else{
3935             this.el.addClass('show');
3936             this.el.addClass('in');
3937         }
3938
3939         // not sure how we can show data in here..
3940         //if (this.tmpl) {
3941         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
3942         //}
3943
3944         Roo.get(document.body).addClass("x-body-masked");
3945         
3946         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
3947         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3948         this.maskEl.dom.style.display = 'block';
3949         this.maskEl.addClass('show');
3950         
3951         
3952         this.resize();
3953         
3954         this.fireEvent('show', this);
3955
3956         // set zindex here - otherwise it appears to be ignored...
3957         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3958
3959         (function () {
3960             this.items.forEach( function(e) {
3961                 e.layout ? e.layout() : false;
3962
3963             });
3964         }).defer(100,this);
3965
3966     },
3967     hide : function()
3968     {
3969         if(this.fireEvent("beforehide", this) !== false){
3970             
3971             this.maskEl.removeClass('show');
3972             
3973             this.maskEl.dom.style.display = '';
3974             Roo.get(document.body).removeClass("x-body-masked");
3975             this.el.removeClass('in');
3976             this.el.select('.modal-dialog', true).first().setStyle('transform','');
3977
3978             if(this.animate){ // why
3979                 this.el.addClass('hideing');
3980                 this.el.removeClass('show');
3981                 (function(){
3982                     if (!this.el.hasClass('hideing')) {
3983                         return; // it's been shown again...
3984                     }
3985                     
3986                     this.el.dom.style.display='';
3987
3988                     Roo.get(document.body).removeClass('modal-open');
3989                     this.el.removeClass('hideing');
3990                 }).defer(150,this);
3991                 
3992             }else{
3993                 this.el.removeClass('show');
3994                 this.el.dom.style.display='';
3995                 Roo.get(document.body).removeClass('modal-open');
3996
3997             }
3998             this.fireEvent('hide', this);
3999         }
4000     },
4001     isVisible : function()
4002     {
4003         
4004         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4005         
4006     },
4007
4008     addButton : function(str, cb)
4009     {
4010
4011
4012         var b = Roo.apply({}, { html : str } );
4013         b.xns = b.xns || Roo.bootstrap;
4014         b.xtype = b.xtype || 'Button';
4015         if (typeof(b.listeners) == 'undefined') {
4016             b.listeners = { click : cb.createDelegate(this)  };
4017         }
4018
4019         var btn = Roo.factory(b);
4020
4021         btn.render(this.getButtonContainer());
4022
4023         return btn;
4024
4025     },
4026
4027     setDefaultButton : function(btn)
4028     {
4029         //this.el.select('.modal-footer').()
4030     },
4031
4032     resizeTo: function(w,h)
4033     {
4034         this.dialogEl.setWidth(w);
4035         
4036         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4037
4038         this.bodyEl.setHeight(h - diff);
4039         
4040         this.fireEvent('resize', this);
4041     },
4042     
4043     setContentSize  : function(w, h)
4044     {
4045
4046     },
4047     onButtonClick: function(btn,e)
4048     {
4049         //Roo.log([a,b,c]);
4050         this.fireEvent('btnclick', btn.name, e);
4051     },
4052      /**
4053      * Set the title of the Dialog
4054      * @param {String} str new Title
4055      */
4056     setTitle: function(str) {
4057         this.titleEl.dom.innerHTML = str;
4058     },
4059     /**
4060      * Set the body of the Dialog
4061      * @param {String} str new Title
4062      */
4063     setBody: function(str) {
4064         this.bodyEl.dom.innerHTML = str;
4065     },
4066     /**
4067      * Set the body of the Dialog using the template
4068      * @param {Obj} data - apply this data to the template and replace the body contents.
4069      */
4070     applyBody: function(obj)
4071     {
4072         if (!this.tmpl) {
4073             Roo.log("Error - using apply Body without a template");
4074             //code
4075         }
4076         this.tmpl.overwrite(this.bodyEl, obj);
4077     },
4078     
4079     getChildHeight : function(child_nodes)
4080     {
4081         if(
4082             !child_nodes ||
4083             child_nodes.length == 0
4084         ) {
4085             return;
4086         }
4087         
4088         var child_height = 0;
4089         
4090         for(var i = 0; i < child_nodes.length; i++) {
4091             
4092             /*
4093             * for modal with tabs...
4094             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4095                 
4096                 var layout_childs = child_nodes[i].childNodes;
4097                 
4098                 for(var j = 0; j < layout_childs.length; j++) {
4099                     
4100                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4101                         
4102                         var layout_body_childs = layout_childs[j].childNodes;
4103                         
4104                         for(var k = 0; k < layout_body_childs.length; k++) {
4105                             
4106                             if(layout_body_childs[k].classList.contains('navbar')) {
4107                                 child_height += layout_body_childs[k].offsetHeight;
4108                                 continue;
4109                             }
4110                             
4111                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4112                                 
4113                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4114                                 
4115                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4116                                     
4117                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4118                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4119                                         continue;
4120                                     }
4121                                     
4122                                 }
4123                                 
4124                             }
4125                             
4126                         }
4127                     }
4128                 }
4129                 continue;
4130             }
4131             */
4132             
4133             child_height += child_nodes[i].offsetHeight;
4134             // Roo.log(child_nodes[i].offsetHeight);
4135         }
4136         
4137         return child_height;
4138     }
4139
4140 });
4141
4142
4143 Roo.apply(Roo.bootstrap.Modal,  {
4144     /**
4145          * Button config that displays a single OK button
4146          * @type Object
4147          */
4148         OK :  [{
4149             name : 'ok',
4150             weight : 'primary',
4151             html : 'OK'
4152         }],
4153         /**
4154          * Button config that displays Yes and No buttons
4155          * @type Object
4156          */
4157         YESNO : [
4158             {
4159                 name  : 'no',
4160                 html : 'No'
4161             },
4162             {
4163                 name  :'yes',
4164                 weight : 'primary',
4165                 html : 'Yes'
4166             }
4167         ],
4168
4169         /**
4170          * Button config that displays OK and Cancel buttons
4171          * @type Object
4172          */
4173         OKCANCEL : [
4174             {
4175                name : 'cancel',
4176                 html : 'Cancel'
4177             },
4178             {
4179                 name : 'ok',
4180                 weight : 'primary',
4181                 html : 'OK'
4182             }
4183         ],
4184         /**
4185          * Button config that displays Yes, No and Cancel buttons
4186          * @type Object
4187          */
4188         YESNOCANCEL : [
4189             {
4190                 name : 'yes',
4191                 weight : 'primary',
4192                 html : 'Yes'
4193             },
4194             {
4195                 name : 'no',
4196                 html : 'No'
4197             },
4198             {
4199                 name : 'cancel',
4200                 html : 'Cancel'
4201             }
4202         ],
4203         
4204         zIndex : 10001
4205 });
4206 /*
4207  * - LGPL
4208  *
4209  * messagebox - can be used as a replace
4210  * 
4211  */
4212 /**
4213  * @class Roo.MessageBox
4214  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4215  * Example usage:
4216  *<pre><code>
4217 // Basic alert:
4218 Roo.Msg.alert('Status', 'Changes saved successfully.');
4219
4220 // Prompt for user data:
4221 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4222     if (btn == 'ok'){
4223         // process text value...
4224     }
4225 });
4226
4227 // Show a dialog using config options:
4228 Roo.Msg.show({
4229    title:'Save Changes?',
4230    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4231    buttons: Roo.Msg.YESNOCANCEL,
4232    fn: processResult,
4233    animEl: 'elId'
4234 });
4235 </code></pre>
4236  * @singleton
4237  */
4238 Roo.bootstrap.MessageBox = function(){
4239     var dlg, opt, mask, waitTimer;
4240     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4241     var buttons, activeTextEl, bwidth;
4242
4243     
4244     // private
4245     var handleButton = function(button){
4246         dlg.hide();
4247         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4248     };
4249
4250     // private
4251     var handleHide = function(){
4252         if(opt && opt.cls){
4253             dlg.el.removeClass(opt.cls);
4254         }
4255         //if(waitTimer){
4256         //    Roo.TaskMgr.stop(waitTimer);
4257         //    waitTimer = null;
4258         //}
4259     };
4260
4261     // private
4262     var updateButtons = function(b){
4263         var width = 0;
4264         if(!b){
4265             buttons["ok"].hide();
4266             buttons["cancel"].hide();
4267             buttons["yes"].hide();
4268             buttons["no"].hide();
4269             dlg.footerEl.hide();
4270             
4271             return width;
4272         }
4273         dlg.footerEl.show();
4274         for(var k in buttons){
4275             if(typeof buttons[k] != "function"){
4276                 if(b[k]){
4277                     buttons[k].show();
4278                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4279                     width += buttons[k].el.getWidth()+15;
4280                 }else{
4281                     buttons[k].hide();
4282                 }
4283             }
4284         }
4285         return width;
4286     };
4287
4288     // private
4289     var handleEsc = function(d, k, e){
4290         if(opt && opt.closable !== false){
4291             dlg.hide();
4292         }
4293         if(e){
4294             e.stopEvent();
4295         }
4296     };
4297
4298     return {
4299         /**
4300          * Returns a reference to the underlying {@link Roo.BasicDialog} element
4301          * @return {Roo.BasicDialog} The BasicDialog element
4302          */
4303         getDialog : function(){
4304            if(!dlg){
4305                 dlg = new Roo.bootstrap.Modal( {
4306                     //draggable: true,
4307                     //resizable:false,
4308                     //constraintoviewport:false,
4309                     //fixedcenter:true,
4310                     //collapsible : false,
4311                     //shim:true,
4312                     //modal: true,
4313                 //    width: 'auto',
4314                   //  height:100,
4315                     //buttonAlign:"center",
4316                     closeClick : function(){
4317                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4318                             handleButton("no");
4319                         }else{
4320                             handleButton("cancel");
4321                         }
4322                     }
4323                 });
4324                 dlg.render();
4325                 dlg.on("hide", handleHide);
4326                 mask = dlg.mask;
4327                 //dlg.addKeyListener(27, handleEsc);
4328                 buttons = {};
4329                 this.buttons = buttons;
4330                 var bt = this.buttonText;
4331                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4332                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4333                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4334                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4335                 //Roo.log(buttons);
4336                 bodyEl = dlg.bodyEl.createChild({
4337
4338                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4339                         '<textarea class="roo-mb-textarea"></textarea>' +
4340                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
4341                 });
4342                 msgEl = bodyEl.dom.firstChild;
4343                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4344                 textboxEl.enableDisplayMode();
4345                 textboxEl.addKeyListener([10,13], function(){
4346                     if(dlg.isVisible() && opt && opt.buttons){
4347                         if(opt.buttons.ok){
4348                             handleButton("ok");
4349                         }else if(opt.buttons.yes){
4350                             handleButton("yes");
4351                         }
4352                     }
4353                 });
4354                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4355                 textareaEl.enableDisplayMode();
4356                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4357                 progressEl.enableDisplayMode();
4358                 
4359                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4360                 var pf = progressEl.dom.firstChild;
4361                 if (pf) {
4362                     pp = Roo.get(pf.firstChild);
4363                     pp.setHeight(pf.offsetHeight);
4364                 }
4365                 
4366             }
4367             return dlg;
4368         },
4369
4370         /**
4371          * Updates the message box body text
4372          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4373          * the XHTML-compliant non-breaking space character '&amp;#160;')
4374          * @return {Roo.MessageBox} This message box
4375          */
4376         updateText : function(text)
4377         {
4378             if(!dlg.isVisible() && !opt.width){
4379                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4380                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4381             }
4382             msgEl.innerHTML = text || '&#160;';
4383       
4384             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4385             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4386             var w = Math.max(
4387                     Math.min(opt.width || cw , this.maxWidth), 
4388                     Math.max(opt.minWidth || this.minWidth, bwidth)
4389             );
4390             if(opt.prompt){
4391                 activeTextEl.setWidth(w);
4392             }
4393             if(dlg.isVisible()){
4394                 dlg.fixedcenter = false;
4395             }
4396             // to big, make it scroll. = But as usual stupid IE does not support
4397             // !important..
4398             
4399             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4400                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4401                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4402             } else {
4403                 bodyEl.dom.style.height = '';
4404                 bodyEl.dom.style.overflowY = '';
4405             }
4406             if (cw > w) {
4407                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4408             } else {
4409                 bodyEl.dom.style.overflowX = '';
4410             }
4411             
4412             dlg.setContentSize(w, bodyEl.getHeight());
4413             if(dlg.isVisible()){
4414                 dlg.fixedcenter = true;
4415             }
4416             return this;
4417         },
4418
4419         /**
4420          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
4421          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4422          * @param {Number} value Any number between 0 and 1 (e.g., .5)
4423          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4424          * @return {Roo.MessageBox} This message box
4425          */
4426         updateProgress : function(value, text){
4427             if(text){
4428                 this.updateText(text);
4429             }
4430             
4431             if (pp) { // weird bug on my firefox - for some reason this is not defined
4432                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4433                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4434             }
4435             return this;
4436         },        
4437
4438         /**
4439          * Returns true if the message box is currently displayed
4440          * @return {Boolean} True if the message box is visible, else false
4441          */
4442         isVisible : function(){
4443             return dlg && dlg.isVisible();  
4444         },
4445
4446         /**
4447          * Hides the message box if it is displayed
4448          */
4449         hide : function(){
4450             if(this.isVisible()){
4451                 dlg.hide();
4452             }  
4453         },
4454
4455         /**
4456          * Displays a new message box, or reinitializes an existing message box, based on the config options
4457          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4458          * The following config object properties are supported:
4459          * <pre>
4460 Property    Type             Description
4461 ----------  ---------------  ------------------------------------------------------------------------------------
4462 animEl            String/Element   An id or Element from which the message box should animate as it opens and
4463                                    closes (defaults to undefined)
4464 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4465                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
4466 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
4467                                    progress and wait dialogs will ignore this property and always hide the
4468                                    close button as they can only be closed programmatically.
4469 cls               String           A custom CSS class to apply to the message box element
4470 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
4471                                    displayed (defaults to 75)
4472 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
4473                                    function will be btn (the name of the button that was clicked, if applicable,
4474                                    e.g. "ok"), and text (the value of the active text field, if applicable).
4475                                    Progress and wait dialogs will ignore this option since they do not respond to
4476                                    user actions and can only be closed programmatically, so any required function
4477                                    should be called by the same code after it closes the dialog.
4478 icon              String           A CSS class that provides a background image to be used as an icon for
4479                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4480 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
4481 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
4482 modal             Boolean          False to allow user interaction with the page while the message box is
4483                                    displayed (defaults to true)
4484 msg               String           A string that will replace the existing message box body text (defaults
4485                                    to the XHTML-compliant non-breaking space character '&#160;')
4486 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
4487 progress          Boolean          True to display a progress bar (defaults to false)
4488 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
4489 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
4490 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
4491 title             String           The title text
4492 value             String           The string value to set into the active textbox element if displayed
4493 wait              Boolean          True to display a progress bar (defaults to false)
4494 width             Number           The width of the dialog in pixels
4495 </pre>
4496          *
4497          * Example usage:
4498          * <pre><code>
4499 Roo.Msg.show({
4500    title: 'Address',
4501    msg: 'Please enter your address:',
4502    width: 300,
4503    buttons: Roo.MessageBox.OKCANCEL,
4504    multiline: true,
4505    fn: saveAddress,
4506    animEl: 'addAddressBtn'
4507 });
4508 </code></pre>
4509          * @param {Object} config Configuration options
4510          * @return {Roo.MessageBox} This message box
4511          */
4512         show : function(options)
4513         {
4514             
4515             // this causes nightmares if you show one dialog after another
4516             // especially on callbacks..
4517              
4518             if(this.isVisible()){
4519                 
4520                 this.hide();
4521                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4522                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
4523                 Roo.log("New Dialog Message:" +  options.msg )
4524                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4525                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4526                 
4527             }
4528             var d = this.getDialog();
4529             opt = options;
4530             d.setTitle(opt.title || "&#160;");
4531             d.closeEl.setDisplayed(opt.closable !== false);
4532             activeTextEl = textboxEl;
4533             opt.prompt = opt.prompt || (opt.multiline ? true : false);
4534             if(opt.prompt){
4535                 if(opt.multiline){
4536                     textboxEl.hide();
4537                     textareaEl.show();
4538                     textareaEl.setHeight(typeof opt.multiline == "number" ?
4539                         opt.multiline : this.defaultTextHeight);
4540                     activeTextEl = textareaEl;
4541                 }else{
4542                     textboxEl.show();
4543                     textareaEl.hide();
4544                 }
4545             }else{
4546                 textboxEl.hide();
4547                 textareaEl.hide();
4548             }
4549             progressEl.setDisplayed(opt.progress === true);
4550             if (opt.progress) {
4551                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4552             }
4553             this.updateProgress(0);
4554             activeTextEl.dom.value = opt.value || "";
4555             if(opt.prompt){
4556                 dlg.setDefaultButton(activeTextEl);
4557             }else{
4558                 var bs = opt.buttons;
4559                 var db = null;
4560                 if(bs && bs.ok){
4561                     db = buttons["ok"];
4562                 }else if(bs && bs.yes){
4563                     db = buttons["yes"];
4564                 }
4565                 dlg.setDefaultButton(db);
4566             }
4567             bwidth = updateButtons(opt.buttons);
4568             this.updateText(opt.msg);
4569             if(opt.cls){
4570                 d.el.addClass(opt.cls);
4571             }
4572             d.proxyDrag = opt.proxyDrag === true;
4573             d.modal = opt.modal !== false;
4574             d.mask = opt.modal !== false ? mask : false;
4575             if(!d.isVisible()){
4576                 // force it to the end of the z-index stack so it gets a cursor in FF
4577                 document.body.appendChild(dlg.el.dom);
4578                 d.animateTarget = null;
4579                 d.show(options.animEl);
4580             }
4581             return this;
4582         },
4583
4584         /**
4585          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
4586          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
4587          * and closing the message box when the process is complete.
4588          * @param {String} title The title bar text
4589          * @param {String} msg The message box body text
4590          * @return {Roo.MessageBox} This message box
4591          */
4592         progress : function(title, msg){
4593             this.show({
4594                 title : title,
4595                 msg : msg,
4596                 buttons: false,
4597                 progress:true,
4598                 closable:false,
4599                 minWidth: this.minProgressWidth,
4600                 modal : true
4601             });
4602             return this;
4603         },
4604
4605         /**
4606          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
4607          * If a callback function is passed it will be called after the user clicks the button, and the
4608          * id of the button that was clicked will be passed as the only parameter to the callback
4609          * (could also be the top-right close button).
4610          * @param {String} title The title bar text
4611          * @param {String} msg The message box body text
4612          * @param {Function} fn (optional) The callback function invoked after the message box is closed
4613          * @param {Object} scope (optional) The scope of the callback function
4614          * @return {Roo.MessageBox} This message box
4615          */
4616         alert : function(title, msg, fn, scope)
4617         {
4618             this.show({
4619                 title : title,
4620                 msg : msg,
4621                 buttons: this.OK,
4622                 fn: fn,
4623                 closable : false,
4624                 scope : scope,
4625                 modal : true
4626             });
4627             return this;
4628         },
4629
4630         /**
4631          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
4632          * interaction while waiting for a long-running process to complete that does not have defined intervals.
4633          * You are responsible for closing the message box when the process is complete.
4634          * @param {String} msg The message box body text
4635          * @param {String} title (optional) The title bar text
4636          * @return {Roo.MessageBox} This message box
4637          */
4638         wait : function(msg, title){
4639             this.show({
4640                 title : title,
4641                 msg : msg,
4642                 buttons: false,
4643                 closable:false,
4644                 progress:true,
4645                 modal:true,
4646                 width:300,
4647                 wait:true
4648             });
4649             waitTimer = Roo.TaskMgr.start({
4650                 run: function(i){
4651                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
4652                 },
4653                 interval: 1000
4654             });
4655             return this;
4656         },
4657
4658         /**
4659          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
4660          * If a callback function is passed it will be called after the user clicks either button, and the id of the
4661          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
4662          * @param {String} title The title bar text
4663          * @param {String} msg The message box body text
4664          * @param {Function} fn (optional) The callback function invoked after the message box is closed
4665          * @param {Object} scope (optional) The scope of the callback function
4666          * @return {Roo.MessageBox} This message box
4667          */
4668         confirm : function(title, msg, fn, scope){
4669             this.show({
4670                 title : title,
4671                 msg : msg,
4672                 buttons: this.YESNO,
4673                 fn: fn,
4674                 scope : scope,
4675                 modal : true
4676             });
4677             return this;
4678         },
4679
4680         /**
4681          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
4682          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
4683          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
4684          * (could also be the top-right close button) and the text that was entered will be passed as the two
4685          * parameters to the callback.
4686          * @param {String} title The title bar text
4687          * @param {String} msg The message box body text
4688          * @param {Function} fn (optional) The callback function invoked after the message box is closed
4689          * @param {Object} scope (optional) The scope of the callback function
4690          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
4691          * property, or the height in pixels to create the textbox (defaults to false / single-line)
4692          * @return {Roo.MessageBox} This message box
4693          */
4694         prompt : function(title, msg, fn, scope, multiline){
4695             this.show({
4696                 title : title,
4697                 msg : msg,
4698                 buttons: this.OKCANCEL,
4699                 fn: fn,
4700                 minWidth:250,
4701                 scope : scope,
4702                 prompt:true,
4703                 multiline: multiline,
4704                 modal : true
4705             });
4706             return this;
4707         },
4708
4709         /**
4710          * Button config that displays a single OK button
4711          * @type Object
4712          */
4713         OK : {ok:true},
4714         /**
4715          * Button config that displays Yes and No buttons
4716          * @type Object
4717          */
4718         YESNO : {yes:true, no:true},
4719         /**
4720          * Button config that displays OK and Cancel buttons
4721          * @type Object
4722          */
4723         OKCANCEL : {ok:true, cancel:true},
4724         /**
4725          * Button config that displays Yes, No and Cancel buttons
4726          * @type Object
4727          */
4728         YESNOCANCEL : {yes:true, no:true, cancel:true},
4729
4730         /**
4731          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
4732          * @type Number
4733          */
4734         defaultTextHeight : 75,
4735         /**
4736          * The maximum width in pixels of the message box (defaults to 600)
4737          * @type Number
4738          */
4739         maxWidth : 600,
4740         /**
4741          * The minimum width in pixels of the message box (defaults to 100)
4742          * @type Number
4743          */
4744         minWidth : 100,
4745         /**
4746          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
4747          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
4748          * @type Number
4749          */
4750         minProgressWidth : 250,
4751         /**
4752          * An object containing the default button text strings that can be overriden for localized language support.
4753          * Supported properties are: ok, cancel, yes and no.
4754          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
4755          * @type Object
4756          */
4757         buttonText : {
4758             ok : "OK",
4759             cancel : "Cancel",
4760             yes : "Yes",
4761             no : "No"
4762         }
4763     };
4764 }();
4765
4766 /**
4767  * Shorthand for {@link Roo.MessageBox}
4768  */
4769 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
4770 Roo.Msg = Roo.Msg || Roo.MessageBox;
4771 /*
4772  * - LGPL
4773  *
4774  * navbar
4775  * 
4776  */
4777
4778 /**
4779  * @class Roo.bootstrap.Navbar
4780  * @extends Roo.bootstrap.Component
4781  * Bootstrap Navbar class
4782
4783  * @constructor
4784  * Create a new Navbar
4785  * @param {Object} config The config object
4786  */
4787
4788
4789 Roo.bootstrap.Navbar = function(config){
4790     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
4791     this.addEvents({
4792         // raw events
4793         /**
4794          * @event beforetoggle
4795          * Fire before toggle the menu
4796          * @param {Roo.EventObject} e
4797          */
4798         "beforetoggle" : true
4799     });
4800 };
4801
4802 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
4803     
4804     
4805    
4806     // private
4807     navItems : false,
4808     loadMask : false,
4809     
4810     
4811     getAutoCreate : function(){
4812         
4813         
4814         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
4815         
4816     },
4817     
4818     initEvents :function ()
4819     {
4820         //Roo.log(this.el.select('.navbar-toggle',true));
4821         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
4822         
4823         var mark = {
4824             tag: "div",
4825             cls:"x-dlg-mask"
4826         };
4827         
4828         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
4829         
4830         var size = this.el.getSize();
4831         this.maskEl.setSize(size.width, size.height);
4832         this.maskEl.enableDisplayMode("block");
4833         this.maskEl.hide();
4834         
4835         if(this.loadMask){
4836             this.maskEl.show();
4837         }
4838     },
4839     
4840     
4841     getChildContainer : function()
4842     {
4843         if (this.el && this.el.select('.collapse').getCount()) {
4844             return this.el.select('.collapse',true).first();
4845         }
4846         
4847         return this.el;
4848     },
4849     
4850     mask : function()
4851     {
4852         this.maskEl.show();
4853     },
4854     
4855     unmask : function()
4856     {
4857         this.maskEl.hide();
4858     },
4859     onToggle : function()
4860     {
4861         
4862         if(this.fireEvent('beforetoggle', this) === false){
4863             return;
4864         }
4865         var ce = this.el.select('.navbar-collapse',true).first();
4866       
4867         if (!ce.hasClass('show')) {
4868            this.expand();
4869         } else {
4870             this.collapse();
4871         }
4872         
4873         
4874     
4875     },
4876     /**
4877      * Expand the navbar pulldown 
4878      */
4879     expand : function ()
4880     {
4881        
4882         var ce = this.el.select('.navbar-collapse',true).first();
4883         if (ce.hasClass('collapsing')) {
4884             return;
4885         }
4886         ce.dom.style.height = '';
4887                // show it...
4888         ce.addClass('in'); // old...
4889         ce.removeClass('collapse');
4890         ce.addClass('show');
4891         var h = ce.getHeight();
4892         Roo.log(h);
4893         ce.removeClass('show');
4894         // at this point we should be able to see it..
4895         ce.addClass('collapsing');
4896         
4897         ce.setHeight(0); // resize it ...
4898         ce.on('transitionend', function() {
4899             //Roo.log('done transition');
4900             ce.removeClass('collapsing');
4901             ce.addClass('show');
4902             ce.removeClass('collapse');
4903
4904             ce.dom.style.height = '';
4905         }, this, { single: true} );
4906         ce.setHeight(h);
4907         ce.dom.scrollTop = 0;
4908     },
4909     /**
4910      * Collapse the navbar pulldown 
4911      */
4912     collapse : function()
4913     {
4914          var ce = this.el.select('.navbar-collapse',true).first();
4915        
4916         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
4917             // it's collapsed or collapsing..
4918             return;
4919         }
4920         ce.removeClass('in'); // old...
4921         ce.setHeight(ce.getHeight());
4922         ce.removeClass('show');
4923         ce.addClass('collapsing');
4924         
4925         ce.on('transitionend', function() {
4926             ce.dom.style.height = '';
4927             ce.removeClass('collapsing');
4928             ce.addClass('collapse');
4929         }, this, { single: true} );
4930         ce.setHeight(0);
4931     }
4932     
4933     
4934     
4935 });
4936
4937
4938
4939  
4940
4941  /*
4942  * - LGPL
4943  *
4944  * navbar
4945  * 
4946  */
4947
4948 /**
4949  * @class Roo.bootstrap.NavSimplebar
4950  * @extends Roo.bootstrap.Navbar
4951  * Bootstrap Sidebar class
4952  *
4953  * @cfg {Boolean} inverse is inverted color
4954  * 
4955  * @cfg {String} type (nav | pills | tabs)
4956  * @cfg {Boolean} arrangement stacked | justified
4957  * @cfg {String} align (left | right) alignment
4958  * 
4959  * @cfg {Boolean} main (true|false) main nav bar? default false
4960  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
4961  * 
4962  * @cfg {String} tag (header|footer|nav|div) default is nav 
4963
4964  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
4965  * 
4966  * 
4967  * @constructor
4968  * Create a new Sidebar
4969  * @param {Object} config The config object
4970  */
4971
4972
4973 Roo.bootstrap.NavSimplebar = function(config){
4974     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
4975 };
4976
4977 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
4978     
4979     inverse: false,
4980     
4981     type: false,
4982     arrangement: '',
4983     align : false,
4984     
4985     weight : 'light',
4986     
4987     main : false,
4988     
4989     
4990     tag : false,
4991     
4992     
4993     getAutoCreate : function(){
4994         
4995         
4996         var cfg = {
4997             tag : this.tag || 'div',
4998             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
4999         };
5000         if (['light','white'].indexOf(this.weight) > -1) {
5001             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5002         }
5003         cfg.cls += ' bg-' + this.weight;
5004         
5005         if (this.inverse) {
5006             cfg.cls += ' navbar-inverse';
5007             
5008         }
5009         
5010         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5011         
5012         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5013             return cfg;
5014         }
5015         
5016         
5017     
5018         
5019         cfg.cn = [
5020             {
5021                 cls: 'nav nav-' + this.xtype,
5022                 tag : 'ul'
5023             }
5024         ];
5025         
5026          
5027         this.type = this.type || 'nav';
5028         if (['tabs','pills'].indexOf(this.type) != -1) {
5029             cfg.cn[0].cls += ' nav-' + this.type
5030         
5031         
5032         } else {
5033             if (this.type!=='nav') {
5034                 Roo.log('nav type must be nav/tabs/pills')
5035             }
5036             cfg.cn[0].cls += ' navbar-nav'
5037         }
5038         
5039         
5040         
5041         
5042         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5043             cfg.cn[0].cls += ' nav-' + this.arrangement;
5044         }
5045         
5046         
5047         if (this.align === 'right') {
5048             cfg.cn[0].cls += ' navbar-right';
5049         }
5050         
5051         
5052         
5053         
5054         return cfg;
5055     
5056         
5057     }
5058     
5059     
5060     
5061 });
5062
5063
5064
5065  
5066
5067  
5068        /*
5069  * - LGPL
5070  *
5071  * navbar
5072  * navbar-fixed-top
5073  * navbar-expand-md  fixed-top 
5074  */
5075
5076 /**
5077  * @class Roo.bootstrap.NavHeaderbar
5078  * @extends Roo.bootstrap.NavSimplebar
5079  * Bootstrap Sidebar class
5080  *
5081  * @cfg {String} brand what is brand
5082  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5083  * @cfg {String} brand_href href of the brand
5084  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5085  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5086  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5087  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5088  * 
5089  * @constructor
5090  * Create a new Sidebar
5091  * @param {Object} config The config object
5092  */
5093
5094
5095 Roo.bootstrap.NavHeaderbar = function(config){
5096     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5097       
5098 };
5099
5100 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5101     
5102     position: '',
5103     brand: '',
5104     brand_href: false,
5105     srButton : true,
5106     autohide : false,
5107     desktopCenter : false,
5108    
5109     
5110     getAutoCreate : function(){
5111         
5112         var   cfg = {
5113             tag: this.nav || 'nav',
5114             cls: 'navbar navbar-expand-md',
5115             role: 'navigation',
5116             cn: []
5117         };
5118         
5119         var cn = cfg.cn;
5120         if (this.desktopCenter) {
5121             cn.push({cls : 'container', cn : []});
5122             cn = cn[0].cn;
5123         }
5124         
5125         if(this.srButton){
5126             var btn = {
5127                 tag: 'button',
5128                 type: 'button',
5129                 cls: 'navbar-toggle navbar-toggler',
5130                 'data-toggle': 'collapse',
5131                 cn: [
5132                     {
5133                         tag: 'span',
5134                         cls: 'sr-only',
5135                         html: 'Toggle navigation'
5136                     },
5137                     {
5138                         tag: 'span',
5139                         cls: 'icon-bar navbar-toggler-icon'
5140                     },
5141                     {
5142                         tag: 'span',
5143                         cls: 'icon-bar'
5144                     },
5145                     {
5146                         tag: 'span',
5147                         cls: 'icon-bar'
5148                     }
5149                 ]
5150             };
5151             
5152             cn.push( Roo.bootstrap.version == 4 ? btn : {
5153                 tag: 'div',
5154                 cls: 'navbar-header',
5155                 cn: [
5156                     btn
5157                 ]
5158             });
5159         }
5160         
5161         cn.push({
5162             tag: 'div',
5163             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5164             cn : []
5165         });
5166         
5167         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5168         
5169         if (['light','white'].indexOf(this.weight) > -1) {
5170             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5171         }
5172         cfg.cls += ' bg-' + this.weight;
5173         
5174         
5175         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5176             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5177             
5178             // tag can override this..
5179             
5180             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5181         }
5182         
5183         if (this.brand !== '') {
5184             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5185             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5186                 tag: 'a',
5187                 href: this.brand_href ? this.brand_href : '#',
5188                 cls: 'navbar-brand',
5189                 cn: [
5190                 this.brand
5191                 ]
5192             });
5193         }
5194         
5195         if(this.main){
5196             cfg.cls += ' main-nav';
5197         }
5198         
5199         
5200         return cfg;
5201
5202         
5203     },
5204     getHeaderChildContainer : function()
5205     {
5206         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5207             return this.el.select('.navbar-header',true).first();
5208         }
5209         
5210         return this.getChildContainer();
5211     },
5212     
5213     getChildContainer : function()
5214     {
5215          
5216         return this.el.select('.roo-navbar-collapse',true).first();
5217          
5218         
5219     },
5220     
5221     initEvents : function()
5222     {
5223         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5224         
5225         if (this.autohide) {
5226             
5227             var prevScroll = 0;
5228             var ft = this.el;
5229             
5230             Roo.get(document).on('scroll',function(e) {
5231                 var ns = Roo.get(document).getScroll().top;
5232                 var os = prevScroll;
5233                 prevScroll = ns;
5234                 
5235                 if(ns > os){
5236                     ft.removeClass('slideDown');
5237                     ft.addClass('slideUp');
5238                     return;
5239                 }
5240                 ft.removeClass('slideUp');
5241                 ft.addClass('slideDown');
5242                  
5243               
5244           },this);
5245         }
5246     }    
5247     
5248 });
5249
5250
5251
5252  
5253
5254  /*
5255  * - LGPL
5256  *
5257  * navbar
5258  * 
5259  */
5260
5261 /**
5262  * @class Roo.bootstrap.NavSidebar
5263  * @extends Roo.bootstrap.Navbar
5264  * Bootstrap Sidebar class
5265  * 
5266  * @constructor
5267  * Create a new Sidebar
5268  * @param {Object} config The config object
5269  */
5270
5271
5272 Roo.bootstrap.NavSidebar = function(config){
5273     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5274 };
5275
5276 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
5277     
5278     sidebar : true, // used by Navbar Item and NavbarGroup at present...
5279     
5280     getAutoCreate : function(){
5281         
5282         
5283         return  {
5284             tag: 'div',
5285             cls: 'sidebar sidebar-nav'
5286         };
5287     
5288         
5289     }
5290     
5291     
5292     
5293 });
5294
5295
5296
5297  
5298
5299  /*
5300  * - LGPL
5301  *
5302  * nav group
5303  * 
5304  */
5305
5306 /**
5307  * @class Roo.bootstrap.NavGroup
5308  * @extends Roo.bootstrap.Component
5309  * Bootstrap NavGroup class
5310  * @cfg {String} align (left|right)
5311  * @cfg {Boolean} inverse
5312  * @cfg {String} type (nav|pills|tab) default nav
5313  * @cfg {String} navId - reference Id for navbar.
5314
5315  * 
5316  * @constructor
5317  * Create a new nav group
5318  * @param {Object} config The config object
5319  */
5320
5321 Roo.bootstrap.NavGroup = function(config){
5322     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5323     this.navItems = [];
5324    
5325     Roo.bootstrap.NavGroup.register(this);
5326      this.addEvents({
5327         /**
5328              * @event changed
5329              * Fires when the active item changes
5330              * @param {Roo.bootstrap.NavGroup} this
5331              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5332              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
5333          */
5334         'changed': true
5335      });
5336     
5337 };
5338
5339 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
5340     
5341     align: '',
5342     inverse: false,
5343     form: false,
5344     type: 'nav',
5345     navId : '',
5346     // private
5347     
5348     navItems : false, 
5349     
5350     getAutoCreate : function()
5351     {
5352         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5353         
5354         cfg = {
5355             tag : 'ul',
5356             cls: 'nav' 
5357         };
5358         if (Roo.bootstrap.version == 4) {
5359             if (['tabs','pills'].indexOf(this.type) != -1) {
5360                 cfg.cls += ' nav-' + this.type; 
5361             } else {
5362                 // trying to remove so header bar can right align top?
5363                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5364                     // do not use on header bar... 
5365                     cfg.cls += ' navbar-nav';
5366                 }
5367             }
5368             
5369         } else {
5370             if (['tabs','pills'].indexOf(this.type) != -1) {
5371                 cfg.cls += ' nav-' + this.type
5372             } else {
5373                 if (this.type !== 'nav') {
5374                     Roo.log('nav type must be nav/tabs/pills')
5375                 }
5376                 cfg.cls += ' navbar-nav'
5377             }
5378         }
5379         
5380         if (this.parent() && this.parent().sidebar) {
5381             cfg = {
5382                 tag: 'ul',
5383                 cls: 'dashboard-menu sidebar-menu'
5384             };
5385             
5386             return cfg;
5387         }
5388         
5389         if (this.form === true) {
5390             cfg = {
5391                 tag: 'form',
5392                 cls: 'navbar-form form-inline'
5393             };
5394             //nav navbar-right ml-md-auto
5395             if (this.align === 'right') {
5396                 cfg.cls += ' navbar-right ml-md-auto';
5397             } else {
5398                 cfg.cls += ' navbar-left';
5399             }
5400         }
5401         
5402         if (this.align === 'right') {
5403             cfg.cls += ' navbar-right ml-md-auto';
5404         } else {
5405             cfg.cls += ' mr-auto';
5406         }
5407         
5408         if (this.inverse) {
5409             cfg.cls += ' navbar-inverse';
5410             
5411         }
5412         
5413         
5414         return cfg;
5415     },
5416     /**
5417     * sets the active Navigation item
5418     * @param {Roo.bootstrap.NavItem} the new current navitem
5419     */
5420     setActiveItem : function(item)
5421     {
5422         var prev = false;
5423         Roo.each(this.navItems, function(v){
5424             if (v == item) {
5425                 return ;
5426             }
5427             if (v.isActive()) {
5428                 v.setActive(false, true);
5429                 prev = v;
5430                 
5431             }
5432             
5433         });
5434
5435         item.setActive(true, true);
5436         this.fireEvent('changed', this, item, prev);
5437         
5438         
5439     },
5440     /**
5441     * gets the active Navigation item
5442     * @return {Roo.bootstrap.NavItem} the current navitem
5443     */
5444     getActive : function()
5445     {
5446         
5447         var prev = false;
5448         Roo.each(this.navItems, function(v){
5449             
5450             if (v.isActive()) {
5451                 prev = v;
5452                 
5453             }
5454             
5455         });
5456         return prev;
5457     },
5458     
5459     indexOfNav : function()
5460     {
5461         
5462         var prev = false;
5463         Roo.each(this.navItems, function(v,i){
5464             
5465             if (v.isActive()) {
5466                 prev = i;
5467                 
5468             }
5469             
5470         });
5471         return prev;
5472     },
5473     /**
5474     * adds a Navigation item
5475     * @param {Roo.bootstrap.NavItem} the navitem to add
5476     */
5477     addItem : function(cfg)
5478     {
5479         if (this.form && Roo.bootstrap.version == 4) {
5480             cfg.tag = 'div';
5481         }
5482         var cn = new Roo.bootstrap.NavItem(cfg);
5483         this.register(cn);
5484         cn.parentId = this.id;
5485         cn.onRender(this.el, null);
5486         return cn;
5487     },
5488     /**
5489     * register a Navigation item
5490     * @param {Roo.bootstrap.NavItem} the navitem to add
5491     */
5492     register : function(item)
5493     {
5494         this.navItems.push( item);
5495         item.navId = this.navId;
5496     
5497     },
5498     
5499     /**
5500     * clear all the Navigation item
5501     */
5502    
5503     clearAll : function()
5504     {
5505         this.navItems = [];
5506         this.el.dom.innerHTML = '';
5507     },
5508     
5509     getNavItem: function(tabId)
5510     {
5511         var ret = false;
5512         Roo.each(this.navItems, function(e) {
5513             if (e.tabId == tabId) {
5514                ret =  e;
5515                return false;
5516             }
5517             return true;
5518             
5519         });
5520         return ret;
5521     },
5522     
5523     setActiveNext : function()
5524     {
5525         var i = this.indexOfNav(this.getActive());
5526         if (i > this.navItems.length) {
5527             return;
5528         }
5529         this.setActiveItem(this.navItems[i+1]);
5530     },
5531     setActivePrev : function()
5532     {
5533         var i = this.indexOfNav(this.getActive());
5534         if (i  < 1) {
5535             return;
5536         }
5537         this.setActiveItem(this.navItems[i-1]);
5538     },
5539     clearWasActive : function(except) {
5540         Roo.each(this.navItems, function(e) {
5541             if (e.tabId != except.tabId && e.was_active) {
5542                e.was_active = false;
5543                return false;
5544             }
5545             return true;
5546             
5547         });
5548     },
5549     getWasActive : function ()
5550     {
5551         var r = false;
5552         Roo.each(this.navItems, function(e) {
5553             if (e.was_active) {
5554                r = e;
5555                return false;
5556             }
5557             return true;
5558             
5559         });
5560         return r;
5561     }
5562     
5563     
5564 });
5565
5566  
5567 Roo.apply(Roo.bootstrap.NavGroup, {
5568     
5569     groups: {},
5570      /**
5571     * register a Navigation Group
5572     * @param {Roo.bootstrap.NavGroup} the navgroup to add
5573     */
5574     register : function(navgrp)
5575     {
5576         this.groups[navgrp.navId] = navgrp;
5577         
5578     },
5579     /**
5580     * fetch a Navigation Group based on the navigation ID
5581     * @param {string} the navgroup to add
5582     * @returns {Roo.bootstrap.NavGroup} the navgroup 
5583     */
5584     get: function(navId) {
5585         if (typeof(this.groups[navId]) == 'undefined') {
5586             return false;
5587             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
5588         }
5589         return this.groups[navId] ;
5590     }
5591     
5592     
5593     
5594 });
5595
5596  /*
5597  * - LGPL
5598  *
5599  * row
5600  * 
5601  */
5602
5603 /**
5604  * @class Roo.bootstrap.NavItem
5605  * @extends Roo.bootstrap.Component
5606  * Bootstrap Navbar.NavItem class
5607  * @cfg {String} href  link to
5608  * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
5609
5610  * @cfg {String} html content of button
5611  * @cfg {String} badge text inside badge
5612  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
5613  * @cfg {String} glyphicon DEPRICATED - use fa
5614  * @cfg {String} icon DEPRICATED - use fa
5615  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
5616  * @cfg {Boolean} active Is item active
5617  * @cfg {Boolean} disabled Is item disabled
5618  
5619  * @cfg {Boolean} preventDefault (true | false) default false
5620  * @cfg {String} tabId the tab that this item activates.
5621  * @cfg {String} tagtype (a|span) render as a href or span?
5622  * @cfg {Boolean} animateRef (true|false) link to element default false  
5623   
5624  * @constructor
5625  * Create a new Navbar Item
5626  * @param {Object} config The config object
5627  */
5628 Roo.bootstrap.NavItem = function(config){
5629     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
5630     this.addEvents({
5631         // raw events
5632         /**
5633          * @event click
5634          * The raw click event for the entire grid.
5635          * @param {Roo.EventObject} e
5636          */
5637         "click" : true,
5638          /**
5639             * @event changed
5640             * Fires when the active item active state changes
5641             * @param {Roo.bootstrap.NavItem} this
5642             * @param {boolean} state the new state
5643              
5644          */
5645         'changed': true,
5646         /**
5647             * @event scrollto
5648             * Fires when scroll to element
5649             * @param {Roo.bootstrap.NavItem} this
5650             * @param {Object} options
5651             * @param {Roo.EventObject} e
5652              
5653          */
5654         'scrollto': true
5655     });
5656    
5657 };
5658
5659 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
5660     
5661     href: false,
5662     html: '',
5663     badge: '',
5664     icon: false,
5665     fa : false,
5666     glyphicon: false,
5667     active: false,
5668     preventDefault : false,
5669     tabId : false,
5670     tagtype : 'a',
5671     tag: 'li',
5672     disabled : false,
5673     animateRef : false,
5674     was_active : false,
5675     button_weight : '',
5676     button_outline : false,
5677     
5678     navLink: false,
5679     
5680     getAutoCreate : function(){
5681          
5682         var cfg = {
5683             tag: this.tag,
5684             cls: 'nav-item'
5685         };
5686         
5687         if (this.active) {
5688             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
5689         }
5690         if (this.disabled) {
5691             cfg.cls += ' disabled';
5692         }
5693         
5694         // BS4 only?
5695         if (this.button_weight.length) {
5696             cfg.tag = this.href ? 'a' : 'button';
5697             cfg.html = this.html || '';
5698             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
5699             if (this.href) {
5700                 cfg.href = this.href;
5701             }
5702             if (this.fa) {
5703                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
5704             }
5705             
5706             // menu .. should add dropdown-menu class - so no need for carat..
5707             
5708             if (this.badge !== '') {
5709                  
5710                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
5711             }
5712             return cfg;
5713         }
5714         
5715         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
5716             cfg.cn = [
5717                 {
5718                     tag: this.tagtype,
5719                     href : this.href || "#",
5720                     html: this.html || ''
5721                 }
5722             ];
5723             if (this.tagtype == 'a') {
5724                 cfg.cn[0].cls = 'nav-link';
5725             }
5726             if (this.icon) {
5727                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
5728             }
5729             if (this.fa) {
5730                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
5731             }
5732             if(this.glyphicon) {
5733                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
5734             }
5735             
5736             if (this.menu) {
5737                 
5738                 cfg.cn[0].html += " <span class='caret'></span>";
5739              
5740             }
5741             
5742             if (this.badge !== '') {
5743                  
5744                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
5745             }
5746         }
5747         
5748         
5749         
5750         return cfg;
5751     },
5752     onRender : function(ct, position)
5753     {
5754        // Roo.log("Call onRender: " + this.xtype);
5755         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
5756             this.tag = 'div';
5757         }
5758         
5759         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
5760         this.navLink = this.el.select('.nav-link',true).first();
5761         return ret;
5762     },
5763       
5764     
5765     initEvents: function() 
5766     {
5767         if (typeof (this.menu) != 'undefined') {
5768             this.menu.parentType = this.xtype;
5769             this.menu.triggerEl = this.el;
5770             this.menu = this.addxtype(Roo.apply({}, this.menu));
5771         }
5772         
5773         this.el.select('a',true).on('click', this.onClick, this);
5774         
5775         if(this.tagtype == 'span'){
5776             this.el.select('span',true).on('click', this.onClick, this);
5777         }
5778        
5779         // at this point parent should be available..
5780         this.parent().register(this);
5781     },
5782     
5783     onClick : function(e)
5784     {
5785         if (e.getTarget('.dropdown-menu-item')) {
5786             // did you click on a menu itemm.... - then don't trigger onclick..
5787             return;
5788         }
5789         
5790         if(
5791                 this.preventDefault || 
5792                 this.href == '#' 
5793         ){
5794             Roo.log("NavItem - prevent Default?");
5795             e.preventDefault();
5796         }
5797         
5798         if (this.disabled) {
5799             return;
5800         }
5801         
5802         var tg = Roo.bootstrap.TabGroup.get(this.navId);
5803         if (tg && tg.transition) {
5804             Roo.log("waiting for the transitionend");
5805             return;
5806         }
5807         
5808         
5809         
5810         //Roo.log("fire event clicked");
5811         if(this.fireEvent('click', this, e) === false){
5812             return;
5813         };
5814         
5815         if(this.tagtype == 'span'){
5816             return;
5817         }
5818         
5819         //Roo.log(this.href);
5820         var ael = this.el.select('a',true).first();
5821         //Roo.log(ael);
5822         
5823         if(ael && this.animateRef && this.href.indexOf('#') > -1){
5824             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
5825             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
5826                 return; // ignore... - it's a 'hash' to another page.
5827             }
5828             Roo.log("NavItem - prevent Default?");
5829             e.preventDefault();
5830             this.scrollToElement(e);
5831         }
5832         
5833         
5834         var p =  this.parent();
5835    
5836         if (['tabs','pills'].indexOf(p.type)!==-1) {
5837             if (typeof(p.setActiveItem) !== 'undefined') {
5838                 p.setActiveItem(this);
5839             }
5840         }
5841         
5842         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
5843         if (p.parentType == 'NavHeaderbar' && !this.menu) {
5844             // remove the collapsed menu expand...
5845             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
5846         }
5847     },
5848     
5849     isActive: function () {
5850         return this.active
5851     },
5852     setActive : function(state, fire, is_was_active)
5853     {
5854         if (this.active && !state && this.navId) {
5855             this.was_active = true;
5856             var nv = Roo.bootstrap.NavGroup.get(this.navId);
5857             if (nv) {
5858                 nv.clearWasActive(this);
5859             }
5860             
5861         }
5862         this.active = state;
5863         
5864         if (!state ) {
5865             this.el.removeClass('active');
5866             this.navLink ? this.navLink.removeClass('active') : false;
5867         } else if (!this.el.hasClass('active')) {
5868             
5869             this.el.addClass('active');
5870             if (Roo.bootstrap.version == 4 && this.navLink ) {
5871                 this.navLink.addClass('active');
5872             }
5873             
5874         }
5875         if (fire) {
5876             this.fireEvent('changed', this, state);
5877         }
5878         
5879         // show a panel if it's registered and related..
5880         
5881         if (!this.navId || !this.tabId || !state || is_was_active) {
5882             return;
5883         }
5884         
5885         var tg = Roo.bootstrap.TabGroup.get(this.navId);
5886         if (!tg) {
5887             return;
5888         }
5889         var pan = tg.getPanelByName(this.tabId);
5890         if (!pan) {
5891             return;
5892         }
5893         // if we can not flip to new panel - go back to old nav highlight..
5894         if (false == tg.showPanel(pan)) {
5895             var nv = Roo.bootstrap.NavGroup.get(this.navId);
5896             if (nv) {
5897                 var onav = nv.getWasActive();
5898                 if (onav) {
5899                     onav.setActive(true, false, true);
5900                 }
5901             }
5902             
5903         }
5904         
5905         
5906         
5907     },
5908      // this should not be here...
5909     setDisabled : function(state)
5910     {
5911         this.disabled = state;
5912         if (!state ) {
5913             this.el.removeClass('disabled');
5914         } else if (!this.el.hasClass('disabled')) {
5915             this.el.addClass('disabled');
5916         }
5917         
5918     },
5919     
5920     /**
5921      * Fetch the element to display the tooltip on.
5922      * @return {Roo.Element} defaults to this.el
5923      */
5924     tooltipEl : function()
5925     {
5926         return this.el.select('' + this.tagtype + '', true).first();
5927     },
5928     
5929     scrollToElement : function(e)
5930     {
5931         var c = document.body;
5932         
5933         /*
5934          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
5935          */
5936         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
5937             c = document.documentElement;
5938         }
5939         
5940         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
5941         
5942         if(!target){
5943             return;
5944         }
5945
5946         var o = target.calcOffsetsTo(c);
5947         
5948         var options = {
5949             target : target,
5950             value : o[1]
5951         };
5952         
5953         this.fireEvent('scrollto', this, options, e);
5954         
5955         Roo.get(c).scrollTo('top', options.value, true);
5956         
5957         return;
5958     }
5959 });
5960  
5961
5962  /*
5963  * - LGPL
5964  *
5965  * sidebar item
5966  *
5967  *  li
5968  *    <span> icon </span>
5969  *    <span> text </span>
5970  *    <span>badge </span>
5971  */
5972
5973 /**
5974  * @class Roo.bootstrap.NavSidebarItem
5975  * @extends Roo.bootstrap.NavItem
5976  * Bootstrap Navbar.NavSidebarItem class
5977  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
5978  * {Boolean} open is the menu open
5979  * {Boolean} buttonView use button as the tigger el rather that a (default false)
5980  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
5981  * {String} buttonSize (sm|md|lg)the extra classes for the button
5982  * {Boolean} showArrow show arrow next to the text (default true)
5983  * @constructor
5984  * Create a new Navbar Button
5985  * @param {Object} config The config object
5986  */
5987 Roo.bootstrap.NavSidebarItem = function(config){
5988     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
5989     this.addEvents({
5990         // raw events
5991         /**
5992          * @event click
5993          * The raw click event for the entire grid.
5994          * @param {Roo.EventObject} e
5995          */
5996         "click" : true,
5997          /**
5998             * @event changed
5999             * Fires when the active item active state changes
6000             * @param {Roo.bootstrap.NavSidebarItem} this
6001             * @param {boolean} state the new state
6002              
6003          */
6004         'changed': true
6005     });
6006    
6007 };
6008
6009 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6010     
6011     badgeWeight : 'default',
6012     
6013     open: false,
6014     
6015     buttonView : false,
6016     
6017     buttonWeight : 'default',
6018     
6019     buttonSize : 'md',
6020     
6021     showArrow : true,
6022     
6023     getAutoCreate : function(){
6024         
6025         
6026         var a = {
6027                 tag: 'a',
6028                 href : this.href || '#',
6029                 cls: '',
6030                 html : '',
6031                 cn : []
6032         };
6033         
6034         if(this.buttonView){
6035             a = {
6036                 tag: 'button',
6037                 href : this.href || '#',
6038                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6039                 html : this.html,
6040                 cn : []
6041             };
6042         }
6043         
6044         var cfg = {
6045             tag: 'li',
6046             cls: '',
6047             cn: [ a ]
6048         };
6049         
6050         if (this.active) {
6051             cfg.cls += ' active';
6052         }
6053         
6054         if (this.disabled) {
6055             cfg.cls += ' disabled';
6056         }
6057         if (this.open) {
6058             cfg.cls += ' open x-open';
6059         }
6060         // left icon..
6061         if (this.glyphicon || this.icon) {
6062             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6063             a.cn.push({ tag : 'i', cls : c }) ;
6064         }
6065         
6066         if(!this.buttonView){
6067             var span = {
6068                 tag: 'span',
6069                 html : this.html || ''
6070             };
6071
6072             a.cn.push(span);
6073             
6074         }
6075         
6076         if (this.badge !== '') {
6077             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6078         }
6079         
6080         if (this.menu) {
6081             
6082             if(this.showArrow){
6083                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6084             }
6085             
6086             a.cls += ' dropdown-toggle treeview' ;
6087         }
6088         
6089         return cfg;
6090     },
6091     
6092     initEvents : function()
6093     { 
6094         if (typeof (this.menu) != 'undefined') {
6095             this.menu.parentType = this.xtype;
6096             this.menu.triggerEl = this.el;
6097             this.menu = this.addxtype(Roo.apply({}, this.menu));
6098         }
6099         
6100         this.el.on('click', this.onClick, this);
6101         
6102         if(this.badge !== ''){
6103             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6104         }
6105         
6106     },
6107     
6108     onClick : function(e)
6109     {
6110         if(this.disabled){
6111             e.preventDefault();
6112             return;
6113         }
6114         
6115         if(this.preventDefault){
6116             e.preventDefault();
6117         }
6118         
6119         this.fireEvent('click', this, e);
6120     },
6121     
6122     disable : function()
6123     {
6124         this.setDisabled(true);
6125     },
6126     
6127     enable : function()
6128     {
6129         this.setDisabled(false);
6130     },
6131     
6132     setDisabled : function(state)
6133     {
6134         if(this.disabled == state){
6135             return;
6136         }
6137         
6138         this.disabled = state;
6139         
6140         if (state) {
6141             this.el.addClass('disabled');
6142             return;
6143         }
6144         
6145         this.el.removeClass('disabled');
6146         
6147         return;
6148     },
6149     
6150     setActive : function(state)
6151     {
6152         if(this.active == state){
6153             return;
6154         }
6155         
6156         this.active = state;
6157         
6158         if (state) {
6159             this.el.addClass('active');
6160             return;
6161         }
6162         
6163         this.el.removeClass('active');
6164         
6165         return;
6166     },
6167     
6168     isActive: function () 
6169     {
6170         return this.active;
6171     },
6172     
6173     setBadge : function(str)
6174     {
6175         if(!this.badgeEl){
6176             return;
6177         }
6178         
6179         this.badgeEl.dom.innerHTML = str;
6180     }
6181     
6182    
6183      
6184  
6185 });
6186  
6187
6188  /*
6189  * - LGPL
6190  *
6191  * row
6192  * 
6193  */
6194
6195 /**
6196  * @class Roo.bootstrap.Row
6197  * @extends Roo.bootstrap.Component
6198  * Bootstrap Row class (contains columns...)
6199  * 
6200  * @constructor
6201  * Create a new Row
6202  * @param {Object} config The config object
6203  */
6204
6205 Roo.bootstrap.Row = function(config){
6206     Roo.bootstrap.Row.superclass.constructor.call(this, config);
6207 };
6208
6209 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
6210     
6211     getAutoCreate : function(){
6212        return {
6213             cls: 'row clearfix'
6214        };
6215     }
6216     
6217     
6218 });
6219
6220  
6221
6222  /*
6223  * - LGPL
6224  *
6225  * pagination
6226  * 
6227  */
6228
6229 /**
6230  * @class Roo.bootstrap.Pagination
6231  * @extends Roo.bootstrap.Component
6232  * Bootstrap Pagination class
6233  * @cfg {String} size xs | sm | md | lg
6234  * @cfg {Boolean} inverse false | true
6235  * 
6236  * @constructor
6237  * Create a new Pagination
6238  * @param {Object} config The config object
6239  */
6240
6241 Roo.bootstrap.Pagination = function(config){
6242     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6243 };
6244
6245 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
6246     
6247     cls: false,
6248     size: false,
6249     inverse: false,
6250     
6251     getAutoCreate : function(){
6252         var cfg = {
6253             tag: 'ul',
6254                 cls: 'pagination'
6255         };
6256         if (this.inverse) {
6257             cfg.cls += ' inverse';
6258         }
6259         if (this.html) {
6260             cfg.html=this.html;
6261         }
6262         if (this.cls) {
6263             cfg.cls += " " + this.cls;
6264         }
6265         return cfg;
6266     }
6267    
6268 });
6269
6270  
6271
6272  /*
6273  * - LGPL
6274  *
6275  * Pagination item
6276  * 
6277  */
6278
6279
6280 /**
6281  * @class Roo.bootstrap.PaginationItem
6282  * @extends Roo.bootstrap.Component
6283  * Bootstrap PaginationItem class
6284  * @cfg {String} html text
6285  * @cfg {String} href the link
6286  * @cfg {Boolean} preventDefault (true | false) default true
6287  * @cfg {Boolean} active (true | false) default false
6288  * @cfg {Boolean} disabled default false
6289  * 
6290  * 
6291  * @constructor
6292  * Create a new PaginationItem
6293  * @param {Object} config The config object
6294  */
6295
6296
6297 Roo.bootstrap.PaginationItem = function(config){
6298     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6299     this.addEvents({
6300         // raw events
6301         /**
6302          * @event click
6303          * The raw click event for the entire grid.
6304          * @param {Roo.EventObject} e
6305          */
6306         "click" : true
6307     });
6308 };
6309
6310 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
6311     
6312     href : false,
6313     html : false,
6314     preventDefault: true,
6315     active : false,
6316     cls : false,
6317     disabled: false,
6318     
6319     getAutoCreate : function(){
6320         var cfg= {
6321             tag: 'li',
6322             cn: [
6323                 {
6324                     tag : 'a',
6325                     href : this.href ? this.href : '#',
6326                     html : this.html ? this.html : ''
6327                 }
6328             ]
6329         };
6330         
6331         if(this.cls){
6332             cfg.cls = this.cls;
6333         }
6334         
6335         if(this.disabled){
6336             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6337         }
6338         
6339         if(this.active){
6340             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6341         }
6342         
6343         return cfg;
6344     },
6345     
6346     initEvents: function() {
6347         
6348         this.el.on('click', this.onClick, this);
6349         
6350     },
6351     onClick : function(e)
6352     {
6353         Roo.log('PaginationItem on click ');
6354         if(this.preventDefault){
6355             e.preventDefault();
6356         }
6357         
6358         if(this.disabled){
6359             return;
6360         }
6361         
6362         this.fireEvent('click', this, e);
6363     }
6364    
6365 });
6366
6367  
6368
6369  /*
6370  * - LGPL
6371  *
6372  * slider
6373  * 
6374  */
6375
6376
6377 /**
6378  * @class Roo.bootstrap.Slider
6379  * @extends Roo.bootstrap.Component
6380  * Bootstrap Slider class
6381  *    
6382  * @constructor
6383  * Create a new Slider
6384  * @param {Object} config The config object
6385  */
6386
6387 Roo.bootstrap.Slider = function(config){
6388     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6389 };
6390
6391 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
6392     
6393     getAutoCreate : function(){
6394         
6395         var cfg = {
6396             tag: 'div',
6397             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6398             cn: [
6399                 {
6400                     tag: 'a',
6401                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
6402                 }
6403             ]
6404         };
6405         
6406         return cfg;
6407     }
6408    
6409 });
6410
6411  /*
6412  * Based on:
6413  * Ext JS Library 1.1.1
6414  * Copyright(c) 2006-2007, Ext JS, LLC.
6415  *
6416  * Originally Released Under LGPL - original licence link has changed is not relivant.
6417  *
6418  * Fork - LGPL
6419  * <script type="text/javascript">
6420  */
6421  
6422
6423 /**
6424  * @class Roo.grid.ColumnModel
6425  * @extends Roo.util.Observable
6426  * This is the default implementation of a ColumnModel used by the Grid. It defines
6427  * the columns in the grid.
6428  * <br>Usage:<br>
6429  <pre><code>
6430  var colModel = new Roo.grid.ColumnModel([
6431         {header: "Ticker", width: 60, sortable: true, locked: true},
6432         {header: "Company Name", width: 150, sortable: true},
6433         {header: "Market Cap.", width: 100, sortable: true},
6434         {header: "$ Sales", width: 100, sortable: true, renderer: money},
6435         {header: "Employees", width: 100, sortable: true, resizable: false}
6436  ]);
6437  </code></pre>
6438  * <p>
6439  
6440  * The config options listed for this class are options which may appear in each
6441  * individual column definition.
6442  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
6443  * @constructor
6444  * @param {Object} config An Array of column config objects. See this class's
6445  * config objects for details.
6446 */
6447 Roo.grid.ColumnModel = function(config){
6448         /**
6449      * The config passed into the constructor
6450      */
6451     this.config = config;
6452     this.lookup = {};
6453
6454     // if no id, create one
6455     // if the column does not have a dataIndex mapping,
6456     // map it to the order it is in the config
6457     for(var i = 0, len = config.length; i < len; i++){
6458         var c = config[i];
6459         if(typeof c.dataIndex == "undefined"){
6460             c.dataIndex = i;
6461         }
6462         if(typeof c.renderer == "string"){
6463             c.renderer = Roo.util.Format[c.renderer];
6464         }
6465         if(typeof c.id == "undefined"){
6466             c.id = Roo.id();
6467         }
6468         if(c.editor && c.editor.xtype){
6469             c.editor  = Roo.factory(c.editor, Roo.grid);
6470         }
6471         if(c.editor && c.editor.isFormField){
6472             c.editor = new Roo.grid.GridEditor(c.editor);
6473         }
6474         this.lookup[c.id] = c;
6475     }
6476
6477     /**
6478      * The width of columns which have no width specified (defaults to 100)
6479      * @type Number
6480      */
6481     this.defaultWidth = 100;
6482
6483     /**
6484      * Default sortable of columns which have no sortable specified (defaults to false)
6485      * @type Boolean
6486      */
6487     this.defaultSortable = false;
6488
6489     this.addEvents({
6490         /**
6491              * @event widthchange
6492              * Fires when the width of a column changes.
6493              * @param {ColumnModel} this
6494              * @param {Number} columnIndex The column index
6495              * @param {Number} newWidth The new width
6496              */
6497             "widthchange": true,
6498         /**
6499              * @event headerchange
6500              * Fires when the text of a header changes.
6501              * @param {ColumnModel} this
6502              * @param {Number} columnIndex The column index
6503              * @param {Number} newText The new header text
6504              */
6505             "headerchange": true,
6506         /**
6507              * @event hiddenchange
6508              * Fires when a column is hidden or "unhidden".
6509              * @param {ColumnModel} this
6510              * @param {Number} columnIndex The column index
6511              * @param {Boolean} hidden true if hidden, false otherwise
6512              */
6513             "hiddenchange": true,
6514             /**
6515          * @event columnmoved
6516          * Fires when a column is moved.
6517          * @param {ColumnModel} this
6518          * @param {Number} oldIndex
6519          * @param {Number} newIndex
6520          */
6521         "columnmoved" : true,
6522         /**
6523          * @event columlockchange
6524          * Fires when a column's locked state is changed
6525          * @param {ColumnModel} this
6526          * @param {Number} colIndex
6527          * @param {Boolean} locked true if locked
6528          */
6529         "columnlockchange" : true
6530     });
6531     Roo.grid.ColumnModel.superclass.constructor.call(this);
6532 };
6533 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
6534     /**
6535      * @cfg {String} header The header text to display in the Grid view.
6536      */
6537     /**
6538      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
6539      * {@link Roo.data.Record} definition from which to draw the column's value. If not
6540      * specified, the column's index is used as an index into the Record's data Array.
6541      */
6542     /**
6543      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
6544      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
6545      */
6546     /**
6547      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
6548      * Defaults to the value of the {@link #defaultSortable} property.
6549      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
6550      */
6551     /**
6552      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
6553      */
6554     /**
6555      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
6556      */
6557     /**
6558      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
6559      */
6560     /**
6561      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
6562      */
6563     /**
6564      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
6565      * given the cell's data value. See {@link #setRenderer}. If not specified, the
6566      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
6567      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
6568      */
6569        /**
6570      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
6571      */
6572     /**
6573      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
6574      */
6575     /**
6576      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
6577      */
6578     /**
6579      * @cfg {String} cursor (Optional)
6580      */
6581     /**
6582      * @cfg {String} tooltip (Optional)
6583      */
6584     /**
6585      * @cfg {Number} xs (Optional)
6586      */
6587     /**
6588      * @cfg {Number} sm (Optional)
6589      */
6590     /**
6591      * @cfg {Number} md (Optional)
6592      */
6593     /**
6594      * @cfg {Number} lg (Optional)
6595      */
6596     /**
6597      * Returns the id of the column at the specified index.
6598      * @param {Number} index The column index
6599      * @return {String} the id
6600      */
6601     getColumnId : function(index){
6602         return this.config[index].id;
6603     },
6604
6605     /**
6606      * Returns the column for a specified id.
6607      * @param {String} id The column id
6608      * @return {Object} the column
6609      */
6610     getColumnById : function(id){
6611         return this.lookup[id];
6612     },
6613
6614     
6615     /**
6616      * Returns the column for a specified dataIndex.
6617      * @param {String} dataIndex The column dataIndex
6618      * @return {Object|Boolean} the column or false if not found
6619      */
6620     getColumnByDataIndex: function(dataIndex){
6621         var index = this.findColumnIndex(dataIndex);
6622         return index > -1 ? this.config[index] : false;
6623     },
6624     
6625     /**
6626      * Returns the index for a specified column id.
6627      * @param {String} id The column id
6628      * @return {Number} the index, or -1 if not found
6629      */
6630     getIndexById : function(id){
6631         for(var i = 0, len = this.config.length; i < len; i++){
6632             if(this.config[i].id == id){
6633                 return i;
6634             }
6635         }
6636         return -1;
6637     },
6638     
6639     /**
6640      * Returns the index for a specified column dataIndex.
6641      * @param {String} dataIndex The column dataIndex
6642      * @return {Number} the index, or -1 if not found
6643      */
6644     
6645     findColumnIndex : function(dataIndex){
6646         for(var i = 0, len = this.config.length; i < len; i++){
6647             if(this.config[i].dataIndex == dataIndex){
6648                 return i;
6649             }
6650         }
6651         return -1;
6652     },
6653     
6654     
6655     moveColumn : function(oldIndex, newIndex){
6656         var c = this.config[oldIndex];
6657         this.config.splice(oldIndex, 1);
6658         this.config.splice(newIndex, 0, c);
6659         this.dataMap = null;
6660         this.fireEvent("columnmoved", this, oldIndex, newIndex);
6661     },
6662
6663     isLocked : function(colIndex){
6664         return this.config[colIndex].locked === true;
6665     },
6666
6667     setLocked : function(colIndex, value, suppressEvent){
6668         if(this.isLocked(colIndex) == value){
6669             return;
6670         }
6671         this.config[colIndex].locked = value;
6672         if(!suppressEvent){
6673             this.fireEvent("columnlockchange", this, colIndex, value);
6674         }
6675     },
6676
6677     getTotalLockedWidth : function(){
6678         var totalWidth = 0;
6679         for(var i = 0; i < this.config.length; i++){
6680             if(this.isLocked(i) && !this.isHidden(i)){
6681                 this.totalWidth += this.getColumnWidth(i);
6682             }
6683         }
6684         return totalWidth;
6685     },
6686
6687     getLockedCount : function(){
6688         for(var i = 0, len = this.config.length; i < len; i++){
6689             if(!this.isLocked(i)){
6690                 return i;
6691             }
6692         }
6693         
6694         return this.config.length;
6695     },
6696
6697     /**
6698      * Returns the number of columns.
6699      * @return {Number}
6700      */
6701     getColumnCount : function(visibleOnly){
6702         if(visibleOnly === true){
6703             var c = 0;
6704             for(var i = 0, len = this.config.length; i < len; i++){
6705                 if(!this.isHidden(i)){
6706                     c++;
6707                 }
6708             }
6709             return c;
6710         }
6711         return this.config.length;
6712     },
6713
6714     /**
6715      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
6716      * @param {Function} fn
6717      * @param {Object} scope (optional)
6718      * @return {Array} result
6719      */
6720     getColumnsBy : function(fn, scope){
6721         var r = [];
6722         for(var i = 0, len = this.config.length; i < len; i++){
6723             var c = this.config[i];
6724             if(fn.call(scope||this, c, i) === true){
6725                 r[r.length] = c;
6726             }
6727         }
6728         return r;
6729     },
6730
6731     /**
6732      * Returns true if the specified column is sortable.
6733      * @param {Number} col The column index
6734      * @return {Boolean}
6735      */
6736     isSortable : function(col){
6737         if(typeof this.config[col].sortable == "undefined"){
6738             return this.defaultSortable;
6739         }
6740         return this.config[col].sortable;
6741     },
6742
6743     /**
6744      * Returns the rendering (formatting) function defined for the column.
6745      * @param {Number} col The column index.
6746      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
6747      */
6748     getRenderer : function(col){
6749         if(!this.config[col].renderer){
6750             return Roo.grid.ColumnModel.defaultRenderer;
6751         }
6752         return this.config[col].renderer;
6753     },
6754
6755     /**
6756      * Sets the rendering (formatting) function for a column.
6757      * @param {Number} col The column index
6758      * @param {Function} fn The function to use to process the cell's raw data
6759      * to return HTML markup for the grid view. The render function is called with
6760      * the following parameters:<ul>
6761      * <li>Data value.</li>
6762      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
6763      * <li>css A CSS style string to apply to the table cell.</li>
6764      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
6765      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
6766      * <li>Row index</li>
6767      * <li>Column index</li>
6768      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
6769      */
6770     setRenderer : function(col, fn){
6771         this.config[col].renderer = fn;
6772     },
6773
6774     /**
6775      * Returns the width for the specified column.
6776      * @param {Number} col The column index
6777      * @return {Number}
6778      */
6779     getColumnWidth : function(col){
6780         return this.config[col].width * 1 || this.defaultWidth;
6781     },
6782
6783     /**
6784      * Sets the width for a column.
6785      * @param {Number} col The column index
6786      * @param {Number} width The new width
6787      */
6788     setColumnWidth : function(col, width, suppressEvent){
6789         this.config[col].width = width;
6790         this.totalWidth = null;
6791         if(!suppressEvent){
6792              this.fireEvent("widthchange", this, col, width);
6793         }
6794     },
6795
6796     /**
6797      * Returns the total width of all columns.
6798      * @param {Boolean} includeHidden True to include hidden column widths
6799      * @return {Number}
6800      */
6801     getTotalWidth : function(includeHidden){
6802         if(!this.totalWidth){
6803             this.totalWidth = 0;
6804             for(var i = 0, len = this.config.length; i < len; i++){
6805                 if(includeHidden || !this.isHidden(i)){
6806                     this.totalWidth += this.getColumnWidth(i);
6807                 }
6808             }
6809         }
6810         return this.totalWidth;
6811     },
6812
6813     /**
6814      * Returns the header for the specified column.
6815      * @param {Number} col The column index
6816      * @return {String}
6817      */
6818     getColumnHeader : function(col){
6819         return this.config[col].header;
6820     },
6821
6822     /**
6823      * Sets the header for a column.
6824      * @param {Number} col The column index
6825      * @param {String} header The new header
6826      */
6827     setColumnHeader : function(col, header){
6828         this.config[col].header = header;
6829         this.fireEvent("headerchange", this, col, header);
6830     },
6831
6832     /**
6833      * Returns the tooltip for the specified column.
6834      * @param {Number} col The column index
6835      * @return {String}
6836      */
6837     getColumnTooltip : function(col){
6838             return this.config[col].tooltip;
6839     },
6840     /**
6841      * Sets the tooltip for a column.
6842      * @param {Number} col The column index
6843      * @param {String} tooltip The new tooltip
6844      */
6845     setColumnTooltip : function(col, tooltip){
6846             this.config[col].tooltip = tooltip;
6847     },
6848
6849     /**
6850      * Returns the dataIndex for the specified column.
6851      * @param {Number} col The column index
6852      * @return {Number}
6853      */
6854     getDataIndex : function(col){
6855         return this.config[col].dataIndex;
6856     },
6857
6858     /**
6859      * Sets the dataIndex for a column.
6860      * @param {Number} col The column index
6861      * @param {Number} dataIndex The new dataIndex
6862      */
6863     setDataIndex : function(col, dataIndex){
6864         this.config[col].dataIndex = dataIndex;
6865     },
6866
6867     
6868     
6869     /**
6870      * Returns true if the cell is editable.
6871      * @param {Number} colIndex The column index
6872      * @param {Number} rowIndex The row index - this is nto actually used..?
6873      * @return {Boolean}
6874      */
6875     isCellEditable : function(colIndex, rowIndex){
6876         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
6877     },
6878
6879     /**
6880      * Returns the editor defined for the cell/column.
6881      * return false or null to disable editing.
6882      * @param {Number} colIndex The column index
6883      * @param {Number} rowIndex The row index
6884      * @return {Object}
6885      */
6886     getCellEditor : function(colIndex, rowIndex){
6887         return this.config[colIndex].editor;
6888     },
6889
6890     /**
6891      * Sets if a column is editable.
6892      * @param {Number} col The column index
6893      * @param {Boolean} editable True if the column is editable
6894      */
6895     setEditable : function(col, editable){
6896         this.config[col].editable = editable;
6897     },
6898
6899
6900     /**
6901      * Returns true if the column is hidden.
6902      * @param {Number} colIndex The column index
6903      * @return {Boolean}
6904      */
6905     isHidden : function(colIndex){
6906         return this.config[colIndex].hidden;
6907     },
6908
6909
6910     /**
6911      * Returns true if the column width cannot be changed
6912      */
6913     isFixed : function(colIndex){
6914         return this.config[colIndex].fixed;
6915     },
6916
6917     /**
6918      * Returns true if the column can be resized
6919      * @return {Boolean}
6920      */
6921     isResizable : function(colIndex){
6922         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
6923     },
6924     /**
6925      * Sets if a column is hidden.
6926      * @param {Number} colIndex The column index
6927      * @param {Boolean} hidden True if the column is hidden
6928      */
6929     setHidden : function(colIndex, hidden){
6930         this.config[colIndex].hidden = hidden;
6931         this.totalWidth = null;
6932         this.fireEvent("hiddenchange", this, colIndex, hidden);
6933     },
6934
6935     /**
6936      * Sets the editor for a column.
6937      * @param {Number} col The column index
6938      * @param {Object} editor The editor object
6939      */
6940     setEditor : function(col, editor){
6941         this.config[col].editor = editor;
6942     }
6943 });
6944
6945 Roo.grid.ColumnModel.defaultRenderer = function(value)
6946 {
6947     if(typeof value == "object") {
6948         return value;
6949     }
6950         if(typeof value == "string" && value.length < 1){
6951             return "&#160;";
6952         }
6953     
6954         return String.format("{0}", value);
6955 };
6956
6957 // Alias for backwards compatibility
6958 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
6959 /*
6960  * Based on:
6961  * Ext JS Library 1.1.1
6962  * Copyright(c) 2006-2007, Ext JS, LLC.
6963  *
6964  * Originally Released Under LGPL - original licence link has changed is not relivant.
6965  *
6966  * Fork - LGPL
6967  * <script type="text/javascript">
6968  */
6969  
6970 /**
6971  * @class Roo.LoadMask
6972  * A simple utility class for generically masking elements while loading data.  If the element being masked has
6973  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
6974  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
6975  * element's UpdateManager load indicator and will be destroyed after the initial load.
6976  * @constructor
6977  * Create a new LoadMask
6978  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
6979  * @param {Object} config The config object
6980  */
6981 Roo.LoadMask = function(el, config){
6982     this.el = Roo.get(el);
6983     Roo.apply(this, config);
6984     if(this.store){
6985         this.store.on('beforeload', this.onBeforeLoad, this);
6986         this.store.on('load', this.onLoad, this);
6987         this.store.on('loadexception', this.onLoadException, this);
6988         this.removeMask = false;
6989     }else{
6990         var um = this.el.getUpdateManager();
6991         um.showLoadIndicator = false; // disable the default indicator
6992         um.on('beforeupdate', this.onBeforeLoad, this);
6993         um.on('update', this.onLoad, this);
6994         um.on('failure', this.onLoad, this);
6995         this.removeMask = true;
6996     }
6997 };
6998
6999 Roo.LoadMask.prototype = {
7000     /**
7001      * @cfg {Boolean} removeMask
7002      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7003      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7004      */
7005     /**
7006      * @cfg {String} msg
7007      * The text to display in a centered loading message box (defaults to 'Loading...')
7008      */
7009     msg : 'Loading...',
7010     /**
7011      * @cfg {String} msgCls
7012      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7013      */
7014     msgCls : 'x-mask-loading',
7015
7016     /**
7017      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7018      * @type Boolean
7019      */
7020     disabled: false,
7021
7022     /**
7023      * Disables the mask to prevent it from being displayed
7024      */
7025     disable : function(){
7026        this.disabled = true;
7027     },
7028
7029     /**
7030      * Enables the mask so that it can be displayed
7031      */
7032     enable : function(){
7033         this.disabled = false;
7034     },
7035     
7036     onLoadException : function()
7037     {
7038         Roo.log(arguments);
7039         
7040         if (typeof(arguments[3]) != 'undefined') {
7041             Roo.MessageBox.alert("Error loading",arguments[3]);
7042         } 
7043         /*
7044         try {
7045             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7046                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7047             }   
7048         } catch(e) {
7049             
7050         }
7051         */
7052     
7053         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7054     },
7055     // private
7056     onLoad : function()
7057     {
7058         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7059     },
7060
7061     // private
7062     onBeforeLoad : function(){
7063         if(!this.disabled){
7064             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7065         }
7066     },
7067
7068     // private
7069     destroy : function(){
7070         if(this.store){
7071             this.store.un('beforeload', this.onBeforeLoad, this);
7072             this.store.un('load', this.onLoad, this);
7073             this.store.un('loadexception', this.onLoadException, this);
7074         }else{
7075             var um = this.el.getUpdateManager();
7076             um.un('beforeupdate', this.onBeforeLoad, this);
7077             um.un('update', this.onLoad, this);
7078             um.un('failure', this.onLoad, this);
7079         }
7080     }
7081 };/*
7082  * - LGPL
7083  *
7084  * table
7085  * 
7086  */
7087
7088 /**
7089  * @class Roo.bootstrap.Table
7090  * @extends Roo.bootstrap.Component
7091  * Bootstrap Table class
7092  * @cfg {String} cls table class
7093  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7094  * @cfg {String} bgcolor Specifies the background color for a table
7095  * @cfg {Number} border Specifies whether the table cells should have borders or not
7096  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7097  * @cfg {Number} cellspacing Specifies the space between cells
7098  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7099  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7100  * @cfg {String} sortable Specifies that the table should be sortable
7101  * @cfg {String} summary Specifies a summary of the content of a table
7102  * @cfg {Number} width Specifies the width of a table
7103  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7104  * 
7105  * @cfg {boolean} striped Should the rows be alternative striped
7106  * @cfg {boolean} bordered Add borders to the table
7107  * @cfg {boolean} hover Add hover highlighting
7108  * @cfg {boolean} condensed Format condensed
7109  * @cfg {boolean} responsive Format condensed
7110  * @cfg {Boolean} loadMask (true|false) default false
7111  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7112  * @cfg {Boolean} headerShow (true|false) generate thead, default true
7113  * @cfg {Boolean} rowSelection (true|false) default false
7114  * @cfg {Boolean} cellSelection (true|false) default false
7115  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7116  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
7117  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
7118  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
7119  
7120  * 
7121  * @constructor
7122  * Create a new Table
7123  * @param {Object} config The config object
7124  */
7125
7126 Roo.bootstrap.Table = function(config){
7127     Roo.bootstrap.Table.superclass.constructor.call(this, config);
7128     
7129   
7130     
7131     // BC...
7132     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7133     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7134     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7135     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7136     
7137     this.sm = this.sm || {xtype: 'RowSelectionModel'};
7138     if (this.sm) {
7139         this.sm.grid = this;
7140         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7141         this.sm = this.selModel;
7142         this.sm.xmodule = this.xmodule || false;
7143     }
7144     
7145     if (this.cm && typeof(this.cm.config) == 'undefined') {
7146         this.colModel = new Roo.grid.ColumnModel(this.cm);
7147         this.cm = this.colModel;
7148         this.cm.xmodule = this.xmodule || false;
7149     }
7150     if (this.store) {
7151         this.store= Roo.factory(this.store, Roo.data);
7152         this.ds = this.store;
7153         this.ds.xmodule = this.xmodule || false;
7154          
7155     }
7156     if (this.footer && this.store) {
7157         this.footer.dataSource = this.ds;
7158         this.footer = Roo.factory(this.footer);
7159     }
7160     
7161     /** @private */
7162     this.addEvents({
7163         /**
7164          * @event cellclick
7165          * Fires when a cell is clicked
7166          * @param {Roo.bootstrap.Table} this
7167          * @param {Roo.Element} el
7168          * @param {Number} rowIndex
7169          * @param {Number} columnIndex
7170          * @param {Roo.EventObject} e
7171          */
7172         "cellclick" : true,
7173         /**
7174          * @event celldblclick
7175          * Fires when a cell is double clicked
7176          * @param {Roo.bootstrap.Table} this
7177          * @param {Roo.Element} el
7178          * @param {Number} rowIndex
7179          * @param {Number} columnIndex
7180          * @param {Roo.EventObject} e
7181          */
7182         "celldblclick" : true,
7183         /**
7184          * @event rowclick
7185          * Fires when a row is clicked
7186          * @param {Roo.bootstrap.Table} this
7187          * @param {Roo.Element} el
7188          * @param {Number} rowIndex
7189          * @param {Roo.EventObject} e
7190          */
7191         "rowclick" : true,
7192         /**
7193          * @event rowdblclick
7194          * Fires when a row is double clicked
7195          * @param {Roo.bootstrap.Table} this
7196          * @param {Roo.Element} el
7197          * @param {Number} rowIndex
7198          * @param {Roo.EventObject} e
7199          */
7200         "rowdblclick" : true,
7201         /**
7202          * @event mouseover
7203          * Fires when a mouseover occur
7204          * @param {Roo.bootstrap.Table} this
7205          * @param {Roo.Element} el
7206          * @param {Number} rowIndex
7207          * @param {Number} columnIndex
7208          * @param {Roo.EventObject} e
7209          */
7210         "mouseover" : true,
7211         /**
7212          * @event mouseout
7213          * Fires when a mouseout occur
7214          * @param {Roo.bootstrap.Table} this
7215          * @param {Roo.Element} el
7216          * @param {Number} rowIndex
7217          * @param {Number} columnIndex
7218          * @param {Roo.EventObject} e
7219          */
7220         "mouseout" : true,
7221         /**
7222          * @event rowclass
7223          * Fires when a row is rendered, so you can change add a style to it.
7224          * @param {Roo.bootstrap.Table} this
7225          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
7226          */
7227         'rowclass' : true,
7228           /**
7229          * @event rowsrendered
7230          * Fires when all the  rows have been rendered
7231          * @param {Roo.bootstrap.Table} this
7232          */
7233         'rowsrendered' : true,
7234         /**
7235          * @event contextmenu
7236          * The raw contextmenu event for the entire grid.
7237          * @param {Roo.EventObject} e
7238          */
7239         "contextmenu" : true,
7240         /**
7241          * @event rowcontextmenu
7242          * Fires when a row is right clicked
7243          * @param {Roo.bootstrap.Table} this
7244          * @param {Number} rowIndex
7245          * @param {Roo.EventObject} e
7246          */
7247         "rowcontextmenu" : true,
7248         /**
7249          * @event cellcontextmenu
7250          * Fires when a cell is right clicked
7251          * @param {Roo.bootstrap.Table} this
7252          * @param {Number} rowIndex
7253          * @param {Number} cellIndex
7254          * @param {Roo.EventObject} e
7255          */
7256          "cellcontextmenu" : true,
7257          /**
7258          * @event headercontextmenu
7259          * Fires when a header is right clicked
7260          * @param {Roo.bootstrap.Table} this
7261          * @param {Number} columnIndex
7262          * @param {Roo.EventObject} e
7263          */
7264         "headercontextmenu" : true
7265     });
7266 };
7267
7268 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
7269     
7270     cls: false,
7271     align: false,
7272     bgcolor: false,
7273     border: false,
7274     cellpadding: false,
7275     cellspacing: false,
7276     frame: false,
7277     rules: false,
7278     sortable: false,
7279     summary: false,
7280     width: false,
7281     striped : false,
7282     scrollBody : false,
7283     bordered: false,
7284     hover:  false,
7285     condensed : false,
7286     responsive : false,
7287     sm : false,
7288     cm : false,
7289     store : false,
7290     loadMask : false,
7291     footerShow : true,
7292     headerShow : true,
7293   
7294     rowSelection : false,
7295     cellSelection : false,
7296     layout : false,
7297     
7298     // Roo.Element - the tbody
7299     mainBody: false,
7300     // Roo.Element - thead element
7301     mainHead: false,
7302     
7303     container: false, // used by gridpanel...
7304     
7305     lazyLoad : false,
7306     
7307     CSS : Roo.util.CSS,
7308     
7309     auto_hide_footer : false,
7310     
7311     getAutoCreate : function()
7312     {
7313         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7314         
7315         cfg = {
7316             tag: 'table',
7317             cls : 'table',
7318             cn : []
7319         };
7320         if (this.scrollBody) {
7321             cfg.cls += ' table-body-fixed';
7322         }    
7323         if (this.striped) {
7324             cfg.cls += ' table-striped';
7325         }
7326         
7327         if (this.hover) {
7328             cfg.cls += ' table-hover';
7329         }
7330         if (this.bordered) {
7331             cfg.cls += ' table-bordered';
7332         }
7333         if (this.condensed) {
7334             cfg.cls += ' table-condensed';
7335         }
7336         if (this.responsive) {
7337             cfg.cls += ' table-responsive';
7338         }
7339         
7340         if (this.cls) {
7341             cfg.cls+=  ' ' +this.cls;
7342         }
7343         
7344         // this lot should be simplifed...
7345         var _t = this;
7346         var cp = [
7347             'align',
7348             'bgcolor',
7349             'border',
7350             'cellpadding',
7351             'cellspacing',
7352             'frame',
7353             'rules',
7354             'sortable',
7355             'summary',
7356             'width'
7357         ].forEach(function(k) {
7358             if (_t[k]) {
7359                 cfg[k] = _t[k];
7360             }
7361         });
7362         
7363         
7364         if (this.layout) {
7365             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7366         }
7367         
7368         if(this.store || this.cm){
7369             if(this.headerShow){
7370                 cfg.cn.push(this.renderHeader());
7371             }
7372             
7373             cfg.cn.push(this.renderBody());
7374             
7375             if(this.footerShow){
7376                 cfg.cn.push(this.renderFooter());
7377             }
7378             // where does this come from?
7379             //cfg.cls+=  ' TableGrid';
7380         }
7381         
7382         return { cn : [ cfg ] };
7383     },
7384     
7385     initEvents : function()
7386     {   
7387         if(!this.store || !this.cm){
7388             return;
7389         }
7390         if (this.selModel) {
7391             this.selModel.initEvents();
7392         }
7393         
7394         
7395         //Roo.log('initEvents with ds!!!!');
7396         
7397         this.mainBody = this.el.select('tbody', true).first();
7398         this.mainHead = this.el.select('thead', true).first();
7399         this.mainFoot = this.el.select('tfoot', true).first();
7400         
7401         
7402         
7403         var _this = this;
7404         
7405         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7406             e.on('click', _this.sort, _this);
7407         });
7408         
7409         this.mainBody.on("click", this.onClick, this);
7410         this.mainBody.on("dblclick", this.onDblClick, this);
7411         
7412         // why is this done????? = it breaks dialogs??
7413         //this.parent().el.setStyle('position', 'relative');
7414         
7415         
7416         if (this.footer) {
7417             this.footer.parentId = this.id;
7418             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
7419             
7420             if(this.lazyLoad){
7421                 this.el.select('tfoot tr td').first().addClass('hide');
7422             }
7423         } 
7424         
7425         if(this.loadMask) {
7426             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
7427         }
7428         
7429         this.store.on('load', this.onLoad, this);
7430         this.store.on('beforeload', this.onBeforeLoad, this);
7431         this.store.on('update', this.onUpdate, this);
7432         this.store.on('add', this.onAdd, this);
7433         this.store.on("clear", this.clear, this);
7434         
7435         this.el.on("contextmenu", this.onContextMenu, this);
7436         
7437         this.mainBody.on('scroll', this.onBodyScroll, this);
7438         
7439         this.cm.on("headerchange", this.onHeaderChange, this);
7440         
7441         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
7442         
7443     },
7444     
7445     onContextMenu : function(e, t)
7446     {
7447         this.processEvent("contextmenu", e);
7448     },
7449     
7450     processEvent : function(name, e)
7451     {
7452         if (name != 'touchstart' ) {
7453             this.fireEvent(name, e);    
7454         }
7455         
7456         var t = e.getTarget();
7457         
7458         var cell = Roo.get(t);
7459         
7460         if(!cell){
7461             return;
7462         }
7463         
7464         if(cell.findParent('tfoot', false, true)){
7465             return;
7466         }
7467         
7468         if(cell.findParent('thead', false, true)){
7469             
7470             if(e.getTarget().nodeName.toLowerCase() != 'th'){
7471                 cell = Roo.get(t).findParent('th', false, true);
7472                 if (!cell) {
7473                     Roo.log("failed to find th in thead?");
7474                     Roo.log(e.getTarget());
7475                     return;
7476                 }
7477             }
7478             
7479             var cellIndex = cell.dom.cellIndex;
7480             
7481             var ename = name == 'touchstart' ? 'click' : name;
7482             this.fireEvent("header" + ename, this, cellIndex, e);
7483             
7484             return;
7485         }
7486         
7487         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7488             cell = Roo.get(t).findParent('td', false, true);
7489             if (!cell) {
7490                 Roo.log("failed to find th in tbody?");
7491                 Roo.log(e.getTarget());
7492                 return;
7493             }
7494         }
7495         
7496         var row = cell.findParent('tr', false, true);
7497         var cellIndex = cell.dom.cellIndex;
7498         var rowIndex = row.dom.rowIndex - 1;
7499         
7500         if(row !== false){
7501             
7502             this.fireEvent("row" + name, this, rowIndex, e);
7503             
7504             if(cell !== false){
7505             
7506                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
7507             }
7508         }
7509         
7510     },
7511     
7512     onMouseover : function(e, el)
7513     {
7514         var cell = Roo.get(el);
7515         
7516         if(!cell){
7517             return;
7518         }
7519         
7520         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7521             cell = cell.findParent('td', false, true);
7522         }
7523         
7524         var row = cell.findParent('tr', false, true);
7525         var cellIndex = cell.dom.cellIndex;
7526         var rowIndex = row.dom.rowIndex - 1; // start from 0
7527         
7528         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
7529         
7530     },
7531     
7532     onMouseout : function(e, el)
7533     {
7534         var cell = Roo.get(el);
7535         
7536         if(!cell){
7537             return;
7538         }
7539         
7540         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7541             cell = cell.findParent('td', false, true);
7542         }
7543         
7544         var row = cell.findParent('tr', false, true);
7545         var cellIndex = cell.dom.cellIndex;
7546         var rowIndex = row.dom.rowIndex - 1; // start from 0
7547         
7548         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
7549         
7550     },
7551     
7552     onClick : function(e, el)
7553     {
7554         var cell = Roo.get(el);
7555         
7556         if(!cell || (!this.cellSelection && !this.rowSelection)){
7557             return;
7558         }
7559         
7560         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7561             cell = cell.findParent('td', false, true);
7562         }
7563         
7564         if(!cell || typeof(cell) == 'undefined'){
7565             return;
7566         }
7567         
7568         var row = cell.findParent('tr', false, true);
7569         
7570         if(!row || typeof(row) == 'undefined'){
7571             return;
7572         }
7573         
7574         var cellIndex = cell.dom.cellIndex;
7575         var rowIndex = this.getRowIndex(row);
7576         
7577         // why??? - should these not be based on SelectionModel?
7578         if(this.cellSelection){
7579             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
7580         }
7581         
7582         if(this.rowSelection){
7583             this.fireEvent('rowclick', this, row, rowIndex, e);
7584         }
7585         
7586         
7587     },
7588         
7589     onDblClick : function(e,el)
7590     {
7591         var cell = Roo.get(el);
7592         
7593         if(!cell || (!this.cellSelection && !this.rowSelection)){
7594             return;
7595         }
7596         
7597         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7598             cell = cell.findParent('td', false, true);
7599         }
7600         
7601         if(!cell || typeof(cell) == 'undefined'){
7602             return;
7603         }
7604         
7605         var row = cell.findParent('tr', false, true);
7606         
7607         if(!row || typeof(row) == 'undefined'){
7608             return;
7609         }
7610         
7611         var cellIndex = cell.dom.cellIndex;
7612         var rowIndex = this.getRowIndex(row);
7613         
7614         if(this.cellSelection){
7615             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
7616         }
7617         
7618         if(this.rowSelection){
7619             this.fireEvent('rowdblclick', this, row, rowIndex, e);
7620         }
7621     },
7622     
7623     sort : function(e,el)
7624     {
7625         var col = Roo.get(el);
7626         
7627         if(!col.hasClass('sortable')){
7628             return;
7629         }
7630         
7631         var sort = col.attr('sort');
7632         var dir = 'ASC';
7633         
7634         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
7635             dir = 'DESC';
7636         }
7637         
7638         this.store.sortInfo = {field : sort, direction : dir};
7639         
7640         if (this.footer) {
7641             Roo.log("calling footer first");
7642             this.footer.onClick('first');
7643         } else {
7644         
7645             this.store.load({ params : { start : 0 } });
7646         }
7647     },
7648     
7649     renderHeader : function()
7650     {
7651         var header = {
7652             tag: 'thead',
7653             cn : []
7654         };
7655         
7656         var cm = this.cm;
7657         this.totalWidth = 0;
7658         
7659         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7660             
7661             var config = cm.config[i];
7662             
7663             var c = {
7664                 tag: 'th',
7665                 cls : 'x-hcol-' + i,
7666                 style : '',
7667                 html: cm.getColumnHeader(i)
7668             };
7669             
7670             var hh = '';
7671             
7672             if(typeof(config.sortable) != 'undefined' && config.sortable){
7673                 c.cls = 'sortable';
7674                 c.html = '<i class="glyphicon"></i>' + c.html;
7675             }
7676             
7677             // could use BS4 hidden-..-down 
7678             
7679             if(typeof(config.lgHeader) != 'undefined'){
7680                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
7681             }
7682             
7683             if(typeof(config.mdHeader) != 'undefined'){
7684                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
7685             }
7686             
7687             if(typeof(config.smHeader) != 'undefined'){
7688                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
7689             }
7690             
7691             if(typeof(config.xsHeader) != 'undefined'){
7692                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
7693             }
7694             
7695             if(hh.length){
7696                 c.html = hh;
7697             }
7698             
7699             if(typeof(config.tooltip) != 'undefined'){
7700                 c.tooltip = config.tooltip;
7701             }
7702             
7703             if(typeof(config.colspan) != 'undefined'){
7704                 c.colspan = config.colspan;
7705             }
7706             
7707             if(typeof(config.hidden) != 'undefined' && config.hidden){
7708                 c.style += ' display:none;';
7709             }
7710             
7711             if(typeof(config.dataIndex) != 'undefined'){
7712                 c.sort = config.dataIndex;
7713             }
7714             
7715            
7716             
7717             if(typeof(config.align) != 'undefined' && config.align.length){
7718                 c.style += ' text-align:' + config.align + ';';
7719             }
7720             
7721             if(typeof(config.width) != 'undefined'){
7722                 c.style += ' width:' + config.width + 'px;';
7723                 this.totalWidth += config.width;
7724             } else {
7725                 this.totalWidth += 100; // assume minimum of 100 per column?
7726             }
7727             
7728             if(typeof(config.cls) != 'undefined'){
7729                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
7730             }
7731             
7732             ['xs','sm','md','lg'].map(function(size){
7733                 
7734                 if(typeof(config[size]) == 'undefined'){
7735                     return;
7736                 }
7737                  
7738                 if (!config[size]) { // 0 = hidden
7739                     // BS 4 '0' is treated as hide that column and below.
7740                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
7741                     return;
7742                 }
7743                 
7744                 c.cls += ' col-' + size + '-' + config[size] + (
7745                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
7746                 );
7747                 
7748                 
7749             });
7750             
7751             header.cn.push(c)
7752         }
7753         
7754         return header;
7755     },
7756     
7757     renderBody : function()
7758     {
7759         var body = {
7760             tag: 'tbody',
7761             cn : [
7762                 {
7763                     tag: 'tr',
7764                     cn : [
7765                         {
7766                             tag : 'td',
7767                             colspan :  this.cm.getColumnCount()
7768                         }
7769                     ]
7770                 }
7771             ]
7772         };
7773         
7774         return body;
7775     },
7776     
7777     renderFooter : function()
7778     {
7779         var footer = {
7780             tag: 'tfoot',
7781             cn : [
7782                 {
7783                     tag: 'tr',
7784                     cn : [
7785                         {
7786                             tag : 'td',
7787                             colspan :  this.cm.getColumnCount()
7788                         }
7789                     ]
7790                 }
7791             ]
7792         };
7793         
7794         return footer;
7795     },
7796     
7797     
7798     
7799     onLoad : function()
7800     {
7801 //        Roo.log('ds onload');
7802         this.clear();
7803         
7804         var _this = this;
7805         var cm = this.cm;
7806         var ds = this.store;
7807         
7808         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7809             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
7810             if (_this.store.sortInfo) {
7811                     
7812                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
7813                     e.select('i', true).addClass(['glyphicon-arrow-up']);
7814                 }
7815                 
7816                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
7817                     e.select('i', true).addClass(['glyphicon-arrow-down']);
7818                 }
7819             }
7820         });
7821         
7822         var tbody =  this.mainBody;
7823               
7824         if(ds.getCount() > 0){
7825             ds.data.each(function(d,rowIndex){
7826                 var row =  this.renderRow(cm, ds, rowIndex);
7827                 
7828                 tbody.createChild(row);
7829                 
7830                 var _this = this;
7831                 
7832                 if(row.cellObjects.length){
7833                     Roo.each(row.cellObjects, function(r){
7834                         _this.renderCellObject(r);
7835                     })
7836                 }
7837                 
7838             }, this);
7839         }
7840         
7841         var tfoot = this.el.select('tfoot', true).first();
7842         
7843         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
7844             
7845             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
7846             
7847             var total = this.ds.getTotalCount();
7848             
7849             if(this.footer.pageSize < total){
7850                 this.mainFoot.show();
7851             }
7852         }
7853         
7854         Roo.each(this.el.select('tbody td', true).elements, function(e){
7855             e.on('mouseover', _this.onMouseover, _this);
7856         });
7857         
7858         Roo.each(this.el.select('tbody td', true).elements, function(e){
7859             e.on('mouseout', _this.onMouseout, _this);
7860         });
7861         this.fireEvent('rowsrendered', this);
7862         
7863         this.autoSize();
7864     },
7865     
7866     
7867     onUpdate : function(ds,record)
7868     {
7869         this.refreshRow(record);
7870         this.autoSize();
7871     },
7872     
7873     onRemove : function(ds, record, index, isUpdate){
7874         if(isUpdate !== true){
7875             this.fireEvent("beforerowremoved", this, index, record);
7876         }
7877         var bt = this.mainBody.dom;
7878         
7879         var rows = this.el.select('tbody > tr', true).elements;
7880         
7881         if(typeof(rows[index]) != 'undefined'){
7882             bt.removeChild(rows[index].dom);
7883         }
7884         
7885 //        if(bt.rows[index]){
7886 //            bt.removeChild(bt.rows[index]);
7887 //        }
7888         
7889         if(isUpdate !== true){
7890             //this.stripeRows(index);
7891             //this.syncRowHeights(index, index);
7892             //this.layout();
7893             this.fireEvent("rowremoved", this, index, record);
7894         }
7895     },
7896     
7897     onAdd : function(ds, records, rowIndex)
7898     {
7899         //Roo.log('on Add called');
7900         // - note this does not handle multiple adding very well..
7901         var bt = this.mainBody.dom;
7902         for (var i =0 ; i < records.length;i++) {
7903             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
7904             //Roo.log(records[i]);
7905             //Roo.log(this.store.getAt(rowIndex+i));
7906             this.insertRow(this.store, rowIndex + i, false);
7907             return;
7908         }
7909         
7910     },
7911     
7912     
7913     refreshRow : function(record){
7914         var ds = this.store, index;
7915         if(typeof record == 'number'){
7916             index = record;
7917             record = ds.getAt(index);
7918         }else{
7919             index = ds.indexOf(record);
7920         }
7921         this.insertRow(ds, index, true);
7922         this.autoSize();
7923         this.onRemove(ds, record, index+1, true);
7924         this.autoSize();
7925         //this.syncRowHeights(index, index);
7926         //this.layout();
7927         this.fireEvent("rowupdated", this, index, record);
7928     },
7929     
7930     insertRow : function(dm, rowIndex, isUpdate){
7931         
7932         if(!isUpdate){
7933             this.fireEvent("beforerowsinserted", this, rowIndex);
7934         }
7935             //var s = this.getScrollState();
7936         var row = this.renderRow(this.cm, this.store, rowIndex);
7937         // insert before rowIndex..
7938         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
7939         
7940         var _this = this;
7941                 
7942         if(row.cellObjects.length){
7943             Roo.each(row.cellObjects, function(r){
7944                 _this.renderCellObject(r);
7945             })
7946         }
7947             
7948         if(!isUpdate){
7949             this.fireEvent("rowsinserted", this, rowIndex);
7950             //this.syncRowHeights(firstRow, lastRow);
7951             //this.stripeRows(firstRow);
7952             //this.layout();
7953         }
7954         
7955     },
7956     
7957     
7958     getRowDom : function(rowIndex)
7959     {
7960         var rows = this.el.select('tbody > tr', true).elements;
7961         
7962         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
7963         
7964     },
7965     // returns the object tree for a tr..
7966   
7967     
7968     renderRow : function(cm, ds, rowIndex) 
7969     {
7970         var d = ds.getAt(rowIndex);
7971         
7972         var row = {
7973             tag : 'tr',
7974             cls : 'x-row-' + rowIndex,
7975             cn : []
7976         };
7977             
7978         var cellObjects = [];
7979         
7980         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7981             var config = cm.config[i];
7982             
7983             var renderer = cm.getRenderer(i);
7984             var value = '';
7985             var id = false;
7986             
7987             if(typeof(renderer) !== 'undefined'){
7988                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
7989             }
7990             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
7991             // and are rendered into the cells after the row is rendered - using the id for the element.
7992             
7993             if(typeof(value) === 'object'){
7994                 id = Roo.id();
7995                 cellObjects.push({
7996                     container : id,
7997                     cfg : value 
7998                 })
7999             }
8000             
8001             var rowcfg = {
8002                 record: d,
8003                 rowIndex : rowIndex,
8004                 colIndex : i,
8005                 rowClass : ''
8006             };
8007
8008             this.fireEvent('rowclass', this, rowcfg);
8009             
8010             var td = {
8011                 tag: 'td',
8012                 cls : rowcfg.rowClass + ' x-col-' + i,
8013                 style: '',
8014                 html: (typeof(value) === 'object') ? '' : value
8015             };
8016             
8017             if (id) {
8018                 td.id = id;
8019             }
8020             
8021             if(typeof(config.colspan) != 'undefined'){
8022                 td.colspan = config.colspan;
8023             }
8024             
8025             if(typeof(config.hidden) != 'undefined' && config.hidden){
8026                 td.style += ' display:none;';
8027             }
8028             
8029             if(typeof(config.align) != 'undefined' && config.align.length){
8030                 td.style += ' text-align:' + config.align + ';';
8031             }
8032             if(typeof(config.valign) != 'undefined' && config.valign.length){
8033                 td.style += ' vertical-align:' + config.valign + ';';
8034             }
8035             
8036             if(typeof(config.width) != 'undefined'){
8037                 td.style += ' width:' +  config.width + 'px;';
8038             }
8039             
8040             if(typeof(config.cursor) != 'undefined'){
8041                 td.style += ' cursor:' +  config.cursor + ';';
8042             }
8043             
8044             if(typeof(config.cls) != 'undefined'){
8045                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8046             }
8047             
8048             ['xs','sm','md','lg'].map(function(size){
8049                 
8050                 if(typeof(config[size]) == 'undefined'){
8051                     return;
8052                 }
8053                 
8054                 
8055                   
8056                 if (!config[size]) { // 0 = hidden
8057                     // BS 4 '0' is treated as hide that column and below.
8058                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8059                     return;
8060                 }
8061                 
8062                 td.cls += ' col-' + size + '-' + config[size] + (
8063                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8064                 );
8065                  
8066
8067             });
8068             
8069             row.cn.push(td);
8070            
8071         }
8072         
8073         row.cellObjects = cellObjects;
8074         
8075         return row;
8076           
8077     },
8078     
8079     
8080     
8081     onBeforeLoad : function()
8082     {
8083         
8084     },
8085      /**
8086      * Remove all rows
8087      */
8088     clear : function()
8089     {
8090         this.el.select('tbody', true).first().dom.innerHTML = '';
8091     },
8092     /**
8093      * Show or hide a row.
8094      * @param {Number} rowIndex to show or hide
8095      * @param {Boolean} state hide
8096      */
8097     setRowVisibility : function(rowIndex, state)
8098     {
8099         var bt = this.mainBody.dom;
8100         
8101         var rows = this.el.select('tbody > tr', true).elements;
8102         
8103         if(typeof(rows[rowIndex]) == 'undefined'){
8104             return;
8105         }
8106         rows[rowIndex].dom.style.display = state ? '' : 'none';
8107     },
8108     
8109     
8110     getSelectionModel : function(){
8111         if(!this.selModel){
8112             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8113         }
8114         return this.selModel;
8115     },
8116     /*
8117      * Render the Roo.bootstrap object from renderder
8118      */
8119     renderCellObject : function(r)
8120     {
8121         var _this = this;
8122         
8123         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8124         
8125         var t = r.cfg.render(r.container);
8126         
8127         if(r.cfg.cn){
8128             Roo.each(r.cfg.cn, function(c){
8129                 var child = {
8130                     container: t.getChildContainer(),
8131                     cfg: c
8132                 };
8133                 _this.renderCellObject(child);
8134             })
8135         }
8136     },
8137     
8138     getRowIndex : function(row)
8139     {
8140         var rowIndex = -1;
8141         
8142         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8143             if(el != row){
8144                 return;
8145             }
8146             
8147             rowIndex = index;
8148         });
8149         
8150         return rowIndex;
8151     },
8152      /**
8153      * Returns the grid's underlying element = used by panel.Grid
8154      * @return {Element} The element
8155      */
8156     getGridEl : function(){
8157         return this.el;
8158     },
8159      /**
8160      * Forces a resize - used by panel.Grid
8161      * @return {Element} The element
8162      */
8163     autoSize : function()
8164     {
8165         //var ctr = Roo.get(this.container.dom.parentElement);
8166         var ctr = Roo.get(this.el.dom);
8167         
8168         var thd = this.getGridEl().select('thead',true).first();
8169         var tbd = this.getGridEl().select('tbody', true).first();
8170         var tfd = this.getGridEl().select('tfoot', true).first();
8171         
8172         var cw = ctr.getWidth();
8173         
8174         if (tbd) {
8175             
8176             tbd.setWidth(ctr.getWidth());
8177             // if the body has a max height - and then scrolls - we should perhaps set up the height here
8178             // this needs fixing for various usage - currently only hydra job advers I think..
8179             //tdb.setHeight(
8180             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8181             //); 
8182             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8183             cw -= barsize;
8184         }
8185         cw = Math.max(cw, this.totalWidth);
8186         this.getGridEl().select('tr',true).setWidth(cw);
8187         // resize 'expandable coloumn?
8188         
8189         return; // we doe not have a view in this design..
8190         
8191     },
8192     onBodyScroll: function()
8193     {
8194         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8195         if(this.mainHead){
8196             this.mainHead.setStyle({
8197                 'position' : 'relative',
8198                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8199             });
8200         }
8201         
8202         if(this.lazyLoad){
8203             
8204             var scrollHeight = this.mainBody.dom.scrollHeight;
8205             
8206             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8207             
8208             var height = this.mainBody.getHeight();
8209             
8210             if(scrollHeight - height == scrollTop) {
8211                 
8212                 var total = this.ds.getTotalCount();
8213                 
8214                 if(this.footer.cursor + this.footer.pageSize < total){
8215                     
8216                     this.footer.ds.load({
8217                         params : {
8218                             start : this.footer.cursor + this.footer.pageSize,
8219                             limit : this.footer.pageSize
8220                         },
8221                         add : true
8222                     });
8223                 }
8224             }
8225             
8226         }
8227     },
8228     
8229     onHeaderChange : function()
8230     {
8231         var header = this.renderHeader();
8232         var table = this.el.select('table', true).first();
8233         
8234         this.mainHead.remove();
8235         this.mainHead = table.createChild(header, this.mainBody, false);
8236     },
8237     
8238     onHiddenChange : function(colModel, colIndex, hidden)
8239     {
8240         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8241         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8242         
8243         this.CSS.updateRule(thSelector, "display", "");
8244         this.CSS.updateRule(tdSelector, "display", "");
8245         
8246         if(hidden){
8247             this.CSS.updateRule(thSelector, "display", "none");
8248             this.CSS.updateRule(tdSelector, "display", "none");
8249         }
8250         
8251         this.onHeaderChange();
8252         this.onLoad();
8253     },
8254     
8255     setColumnWidth: function(col_index, width)
8256     {
8257         // width = "md-2 xs-2..."
8258         if(!this.colModel.config[col_index]) {
8259             return;
8260         }
8261         
8262         var w = width.split(" ");
8263         
8264         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8265         
8266         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8267         
8268         
8269         for(var j = 0; j < w.length; j++) {
8270             
8271             if(!w[j]) {
8272                 continue;
8273             }
8274             
8275             var size_cls = w[j].split("-");
8276             
8277             if(!Number.isInteger(size_cls[1] * 1)) {
8278                 continue;
8279             }
8280             
8281             if(!this.colModel.config[col_index][size_cls[0]]) {
8282                 continue;
8283             }
8284             
8285             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8286                 continue;
8287             }
8288             
8289             h_row[0].classList.replace(
8290                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8291                 "col-"+size_cls[0]+"-"+size_cls[1]
8292             );
8293             
8294             for(var i = 0; i < rows.length; i++) {
8295                 
8296                 var size_cls = w[j].split("-");
8297                 
8298                 if(!Number.isInteger(size_cls[1] * 1)) {
8299                     continue;
8300                 }
8301                 
8302                 if(!this.colModel.config[col_index][size_cls[0]]) {
8303                     continue;
8304                 }
8305                 
8306                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8307                     continue;
8308                 }
8309                 
8310                 rows[i].classList.replace(
8311                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8312                     "col-"+size_cls[0]+"-"+size_cls[1]
8313                 );
8314             }
8315             
8316             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8317         }
8318     }
8319 });
8320
8321  
8322
8323  /*
8324  * - LGPL
8325  *
8326  * table cell
8327  * 
8328  */
8329
8330 /**
8331  * @class Roo.bootstrap.TableCell
8332  * @extends Roo.bootstrap.Component
8333  * Bootstrap TableCell class
8334  * @cfg {String} html cell contain text
8335  * @cfg {String} cls cell class
8336  * @cfg {String} tag cell tag (td|th) default td
8337  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8338  * @cfg {String} align Aligns the content in a cell
8339  * @cfg {String} axis Categorizes cells
8340  * @cfg {String} bgcolor Specifies the background color of a cell
8341  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8342  * @cfg {Number} colspan Specifies the number of columns a cell should span
8343  * @cfg {String} headers Specifies one or more header cells a cell is related to
8344  * @cfg {Number} height Sets the height of a cell
8345  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8346  * @cfg {Number} rowspan Sets the number of rows a cell should span
8347  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8348  * @cfg {String} valign Vertical aligns the content in a cell
8349  * @cfg {Number} width Specifies the width of a cell
8350  * 
8351  * @constructor
8352  * Create a new TableCell
8353  * @param {Object} config The config object
8354  */
8355
8356 Roo.bootstrap.TableCell = function(config){
8357     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8358 };
8359
8360 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
8361     
8362     html: false,
8363     cls: false,
8364     tag: false,
8365     abbr: false,
8366     align: false,
8367     axis: false,
8368     bgcolor: false,
8369     charoff: false,
8370     colspan: false,
8371     headers: false,
8372     height: false,
8373     nowrap: false,
8374     rowspan: false,
8375     scope: false,
8376     valign: false,
8377     width: false,
8378     
8379     
8380     getAutoCreate : function(){
8381         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8382         
8383         cfg = {
8384             tag: 'td'
8385         };
8386         
8387         if(this.tag){
8388             cfg.tag = this.tag;
8389         }
8390         
8391         if (this.html) {
8392             cfg.html=this.html
8393         }
8394         if (this.cls) {
8395             cfg.cls=this.cls
8396         }
8397         if (this.abbr) {
8398             cfg.abbr=this.abbr
8399         }
8400         if (this.align) {
8401             cfg.align=this.align
8402         }
8403         if (this.axis) {
8404             cfg.axis=this.axis
8405         }
8406         if (this.bgcolor) {
8407             cfg.bgcolor=this.bgcolor
8408         }
8409         if (this.charoff) {
8410             cfg.charoff=this.charoff
8411         }
8412         if (this.colspan) {
8413             cfg.colspan=this.colspan
8414         }
8415         if (this.headers) {
8416             cfg.headers=this.headers
8417         }
8418         if (this.height) {
8419             cfg.height=this.height
8420         }
8421         if (this.nowrap) {
8422             cfg.nowrap=this.nowrap
8423         }
8424         if (this.rowspan) {
8425             cfg.rowspan=this.rowspan
8426         }
8427         if (this.scope) {
8428             cfg.scope=this.scope
8429         }
8430         if (this.valign) {
8431             cfg.valign=this.valign
8432         }
8433         if (this.width) {
8434             cfg.width=this.width
8435         }
8436         
8437         
8438         return cfg;
8439     }
8440    
8441 });
8442
8443  
8444
8445  /*
8446  * - LGPL
8447  *
8448  * table row
8449  * 
8450  */
8451
8452 /**
8453  * @class Roo.bootstrap.TableRow
8454  * @extends Roo.bootstrap.Component
8455  * Bootstrap TableRow class
8456  * @cfg {String} cls row class
8457  * @cfg {String} align Aligns the content in a table row
8458  * @cfg {String} bgcolor Specifies a background color for a table row
8459  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8460  * @cfg {String} valign Vertical aligns the content in a table row
8461  * 
8462  * @constructor
8463  * Create a new TableRow
8464  * @param {Object} config The config object
8465  */
8466
8467 Roo.bootstrap.TableRow = function(config){
8468     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
8469 };
8470
8471 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
8472     
8473     cls: false,
8474     align: false,
8475     bgcolor: false,
8476     charoff: false,
8477     valign: false,
8478     
8479     getAutoCreate : function(){
8480         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
8481         
8482         cfg = {
8483             tag: 'tr'
8484         };
8485             
8486         if(this.cls){
8487             cfg.cls = this.cls;
8488         }
8489         if(this.align){
8490             cfg.align = this.align;
8491         }
8492         if(this.bgcolor){
8493             cfg.bgcolor = this.bgcolor;
8494         }
8495         if(this.charoff){
8496             cfg.charoff = this.charoff;
8497         }
8498         if(this.valign){
8499             cfg.valign = this.valign;
8500         }
8501         
8502         return cfg;
8503     }
8504    
8505 });
8506
8507  
8508
8509  /*
8510  * - LGPL
8511  *
8512  * table body
8513  * 
8514  */
8515
8516 /**
8517  * @class Roo.bootstrap.TableBody
8518  * @extends Roo.bootstrap.Component
8519  * Bootstrap TableBody class
8520  * @cfg {String} cls element class
8521  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
8522  * @cfg {String} align Aligns the content inside the element
8523  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
8524  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
8525  * 
8526  * @constructor
8527  * Create a new TableBody
8528  * @param {Object} config The config object
8529  */
8530
8531 Roo.bootstrap.TableBody = function(config){
8532     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
8533 };
8534
8535 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
8536     
8537     cls: false,
8538     tag: false,
8539     align: false,
8540     charoff: false,
8541     valign: false,
8542     
8543     getAutoCreate : function(){
8544         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
8545         
8546         cfg = {
8547             tag: 'tbody'
8548         };
8549             
8550         if (this.cls) {
8551             cfg.cls=this.cls
8552         }
8553         if(this.tag){
8554             cfg.tag = this.tag;
8555         }
8556         
8557         if(this.align){
8558             cfg.align = this.align;
8559         }
8560         if(this.charoff){
8561             cfg.charoff = this.charoff;
8562         }
8563         if(this.valign){
8564             cfg.valign = this.valign;
8565         }
8566         
8567         return cfg;
8568     }
8569     
8570     
8571 //    initEvents : function()
8572 //    {
8573 //        
8574 //        if(!this.store){
8575 //            return;
8576 //        }
8577 //        
8578 //        this.store = Roo.factory(this.store, Roo.data);
8579 //        this.store.on('load', this.onLoad, this);
8580 //        
8581 //        this.store.load();
8582 //        
8583 //    },
8584 //    
8585 //    onLoad: function () 
8586 //    {   
8587 //        this.fireEvent('load', this);
8588 //    }
8589 //    
8590 //   
8591 });
8592
8593  
8594
8595  /*
8596  * Based on:
8597  * Ext JS Library 1.1.1
8598  * Copyright(c) 2006-2007, Ext JS, LLC.
8599  *
8600  * Originally Released Under LGPL - original licence link has changed is not relivant.
8601  *
8602  * Fork - LGPL
8603  * <script type="text/javascript">
8604  */
8605
8606 // as we use this in bootstrap.
8607 Roo.namespace('Roo.form');
8608  /**
8609  * @class Roo.form.Action
8610  * Internal Class used to handle form actions
8611  * @constructor
8612  * @param {Roo.form.BasicForm} el The form element or its id
8613  * @param {Object} config Configuration options
8614  */
8615
8616  
8617  
8618 // define the action interface
8619 Roo.form.Action = function(form, options){
8620     this.form = form;
8621     this.options = options || {};
8622 };
8623 /**
8624  * Client Validation Failed
8625  * @const 
8626  */
8627 Roo.form.Action.CLIENT_INVALID = 'client';
8628 /**
8629  * Server Validation Failed
8630  * @const 
8631  */
8632 Roo.form.Action.SERVER_INVALID = 'server';
8633  /**
8634  * Connect to Server Failed
8635  * @const 
8636  */
8637 Roo.form.Action.CONNECT_FAILURE = 'connect';
8638 /**
8639  * Reading Data from Server Failed
8640  * @const 
8641  */
8642 Roo.form.Action.LOAD_FAILURE = 'load';
8643
8644 Roo.form.Action.prototype = {
8645     type : 'default',
8646     failureType : undefined,
8647     response : undefined,
8648     result : undefined,
8649
8650     // interface method
8651     run : function(options){
8652
8653     },
8654
8655     // interface method
8656     success : function(response){
8657
8658     },
8659
8660     // interface method
8661     handleResponse : function(response){
8662
8663     },
8664
8665     // default connection failure
8666     failure : function(response){
8667         
8668         this.response = response;
8669         this.failureType = Roo.form.Action.CONNECT_FAILURE;
8670         this.form.afterAction(this, false);
8671     },
8672
8673     processResponse : function(response){
8674         this.response = response;
8675         if(!response.responseText){
8676             return true;
8677         }
8678         this.result = this.handleResponse(response);
8679         return this.result;
8680     },
8681
8682     // utility functions used internally
8683     getUrl : function(appendParams){
8684         var url = this.options.url || this.form.url || this.form.el.dom.action;
8685         if(appendParams){
8686             var p = this.getParams();
8687             if(p){
8688                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
8689             }
8690         }
8691         return url;
8692     },
8693
8694     getMethod : function(){
8695         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
8696     },
8697
8698     getParams : function(){
8699         var bp = this.form.baseParams;
8700         var p = this.options.params;
8701         if(p){
8702             if(typeof p == "object"){
8703                 p = Roo.urlEncode(Roo.applyIf(p, bp));
8704             }else if(typeof p == 'string' && bp){
8705                 p += '&' + Roo.urlEncode(bp);
8706             }
8707         }else if(bp){
8708             p = Roo.urlEncode(bp);
8709         }
8710         return p;
8711     },
8712
8713     createCallback : function(){
8714         return {
8715             success: this.success,
8716             failure: this.failure,
8717             scope: this,
8718             timeout: (this.form.timeout*1000),
8719             upload: this.form.fileUpload ? this.success : undefined
8720         };
8721     }
8722 };
8723
8724 Roo.form.Action.Submit = function(form, options){
8725     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
8726 };
8727
8728 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
8729     type : 'submit',
8730
8731     haveProgress : false,
8732     uploadComplete : false,
8733     
8734     // uploadProgress indicator.
8735     uploadProgress : function()
8736     {
8737         if (!this.form.progressUrl) {
8738             return;
8739         }
8740         
8741         if (!this.haveProgress) {
8742             Roo.MessageBox.progress("Uploading", "Uploading");
8743         }
8744         if (this.uploadComplete) {
8745            Roo.MessageBox.hide();
8746            return;
8747         }
8748         
8749         this.haveProgress = true;
8750    
8751         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
8752         
8753         var c = new Roo.data.Connection();
8754         c.request({
8755             url : this.form.progressUrl,
8756             params: {
8757                 id : uid
8758             },
8759             method: 'GET',
8760             success : function(req){
8761                //console.log(data);
8762                 var rdata = false;
8763                 var edata;
8764                 try  {
8765                    rdata = Roo.decode(req.responseText)
8766                 } catch (e) {
8767                     Roo.log("Invalid data from server..");
8768                     Roo.log(edata);
8769                     return;
8770                 }
8771                 if (!rdata || !rdata.success) {
8772                     Roo.log(rdata);
8773                     Roo.MessageBox.alert(Roo.encode(rdata));
8774                     return;
8775                 }
8776                 var data = rdata.data;
8777                 
8778                 if (this.uploadComplete) {
8779                    Roo.MessageBox.hide();
8780                    return;
8781                 }
8782                    
8783                 if (data){
8784                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
8785                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
8786                     );
8787                 }
8788                 this.uploadProgress.defer(2000,this);
8789             },
8790        
8791             failure: function(data) {
8792                 Roo.log('progress url failed ');
8793                 Roo.log(data);
8794             },
8795             scope : this
8796         });
8797            
8798     },
8799     
8800     
8801     run : function()
8802     {
8803         // run get Values on the form, so it syncs any secondary forms.
8804         this.form.getValues();
8805         
8806         var o = this.options;
8807         var method = this.getMethod();
8808         var isPost = method == 'POST';
8809         if(o.clientValidation === false || this.form.isValid()){
8810             
8811             if (this.form.progressUrl) {
8812                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
8813                     (new Date() * 1) + '' + Math.random());
8814                     
8815             } 
8816             
8817             
8818             Roo.Ajax.request(Roo.apply(this.createCallback(), {
8819                 form:this.form.el.dom,
8820                 url:this.getUrl(!isPost),
8821                 method: method,
8822                 params:isPost ? this.getParams() : null,
8823                 isUpload: this.form.fileUpload,
8824                 formData : this.form.formData
8825             }));
8826             
8827             this.uploadProgress();
8828
8829         }else if (o.clientValidation !== false){ // client validation failed
8830             this.failureType = Roo.form.Action.CLIENT_INVALID;
8831             this.form.afterAction(this, false);
8832         }
8833     },
8834
8835     success : function(response)
8836     {
8837         this.uploadComplete= true;
8838         if (this.haveProgress) {
8839             Roo.MessageBox.hide();
8840         }
8841         
8842         
8843         var result = this.processResponse(response);
8844         if(result === true || result.success){
8845             this.form.afterAction(this, true);
8846             return;
8847         }
8848         if(result.errors){
8849             this.form.markInvalid(result.errors);
8850             this.failureType = Roo.form.Action.SERVER_INVALID;
8851         }
8852         this.form.afterAction(this, false);
8853     },
8854     failure : function(response)
8855     {
8856         this.uploadComplete= true;
8857         if (this.haveProgress) {
8858             Roo.MessageBox.hide();
8859         }
8860         
8861         this.response = response;
8862         this.failureType = Roo.form.Action.CONNECT_FAILURE;
8863         this.form.afterAction(this, false);
8864     },
8865     
8866     handleResponse : function(response){
8867         if(this.form.errorReader){
8868             var rs = this.form.errorReader.read(response);
8869             var errors = [];
8870             if(rs.records){
8871                 for(var i = 0, len = rs.records.length; i < len; i++) {
8872                     var r = rs.records[i];
8873                     errors[i] = r.data;
8874                 }
8875             }
8876             if(errors.length < 1){
8877                 errors = null;
8878             }
8879             return {
8880                 success : rs.success,
8881                 errors : errors
8882             };
8883         }
8884         var ret = false;
8885         try {
8886             ret = Roo.decode(response.responseText);
8887         } catch (e) {
8888             ret = {
8889                 success: false,
8890                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
8891                 errors : []
8892             };
8893         }
8894         return ret;
8895         
8896     }
8897 });
8898
8899
8900 Roo.form.Action.Load = function(form, options){
8901     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
8902     this.reader = this.form.reader;
8903 };
8904
8905 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
8906     type : 'load',
8907
8908     run : function(){
8909         
8910         Roo.Ajax.request(Roo.apply(
8911                 this.createCallback(), {
8912                     method:this.getMethod(),
8913                     url:this.getUrl(false),
8914                     params:this.getParams()
8915         }));
8916     },
8917
8918     success : function(response){
8919         
8920         var result = this.processResponse(response);
8921         if(result === true || !result.success || !result.data){
8922             this.failureType = Roo.form.Action.LOAD_FAILURE;
8923             this.form.afterAction(this, false);
8924             return;
8925         }
8926         this.form.clearInvalid();
8927         this.form.setValues(result.data);
8928         this.form.afterAction(this, true);
8929     },
8930
8931     handleResponse : function(response){
8932         if(this.form.reader){
8933             var rs = this.form.reader.read(response);
8934             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
8935             return {
8936                 success : rs.success,
8937                 data : data
8938             };
8939         }
8940         return Roo.decode(response.responseText);
8941     }
8942 });
8943
8944 Roo.form.Action.ACTION_TYPES = {
8945     'load' : Roo.form.Action.Load,
8946     'submit' : Roo.form.Action.Submit
8947 };/*
8948  * - LGPL
8949  *
8950  * form
8951  *
8952  */
8953
8954 /**
8955  * @class Roo.bootstrap.Form
8956  * @extends Roo.bootstrap.Component
8957  * Bootstrap Form class
8958  * @cfg {String} method  GET | POST (default POST)
8959  * @cfg {String} labelAlign top | left (default top)
8960  * @cfg {String} align left  | right - for navbars
8961  * @cfg {Boolean} loadMask load mask when submit (default true)
8962
8963  *
8964  * @constructor
8965  * Create a new Form
8966  * @param {Object} config The config object
8967  */
8968
8969
8970 Roo.bootstrap.Form = function(config){
8971     
8972     Roo.bootstrap.Form.superclass.constructor.call(this, config);
8973     
8974     Roo.bootstrap.Form.popover.apply();
8975     
8976     this.addEvents({
8977         /**
8978          * @event clientvalidation
8979          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
8980          * @param {Form} this
8981          * @param {Boolean} valid true if the form has passed client-side validation
8982          */
8983         clientvalidation: true,
8984         /**
8985          * @event beforeaction
8986          * Fires before any action is performed. Return false to cancel the action.
8987          * @param {Form} this
8988          * @param {Action} action The action to be performed
8989          */
8990         beforeaction: true,
8991         /**
8992          * @event actionfailed
8993          * Fires when an action fails.
8994          * @param {Form} this
8995          * @param {Action} action The action that failed
8996          */
8997         actionfailed : true,
8998         /**
8999          * @event actioncomplete
9000          * Fires when an action is completed.
9001          * @param {Form} this
9002          * @param {Action} action The action that completed
9003          */
9004         actioncomplete : true
9005     });
9006 };
9007
9008 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9009
9010      /**
9011      * @cfg {String} method
9012      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9013      */
9014     method : 'POST',
9015     /**
9016      * @cfg {String} url
9017      * The URL to use for form actions if one isn't supplied in the action options.
9018      */
9019     /**
9020      * @cfg {Boolean} fileUpload
9021      * Set to true if this form is a file upload.
9022      */
9023
9024     /**
9025      * @cfg {Object} baseParams
9026      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9027      */
9028
9029     /**
9030      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9031      */
9032     timeout: 30,
9033     /**
9034      * @cfg {Sting} align (left|right) for navbar forms
9035      */
9036     align : 'left',
9037
9038     // private
9039     activeAction : null,
9040
9041     /**
9042      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9043      * element by passing it or its id or mask the form itself by passing in true.
9044      * @type Mixed
9045      */
9046     waitMsgTarget : false,
9047
9048     loadMask : true,
9049     
9050     /**
9051      * @cfg {Boolean} errorMask (true|false) default false
9052      */
9053     errorMask : false,
9054     
9055     /**
9056      * @cfg {Number} maskOffset Default 100
9057      */
9058     maskOffset : 100,
9059     
9060     /**
9061      * @cfg {Boolean} maskBody
9062      */
9063     maskBody : false,
9064
9065     getAutoCreate : function(){
9066
9067         var cfg = {
9068             tag: 'form',
9069             method : this.method || 'POST',
9070             id : this.id || Roo.id(),
9071             cls : ''
9072         };
9073         if (this.parent().xtype.match(/^Nav/)) {
9074             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9075
9076         }
9077
9078         if (this.labelAlign == 'left' ) {
9079             cfg.cls += ' form-horizontal';
9080         }
9081
9082
9083         return cfg;
9084     },
9085     initEvents : function()
9086     {
9087         this.el.on('submit', this.onSubmit, this);
9088         // this was added as random key presses on the form where triggering form submit.
9089         this.el.on('keypress', function(e) {
9090             if (e.getCharCode() != 13) {
9091                 return true;
9092             }
9093             // we might need to allow it for textareas.. and some other items.
9094             // check e.getTarget().
9095
9096             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9097                 return true;
9098             }
9099
9100             Roo.log("keypress blocked");
9101
9102             e.preventDefault();
9103             return false;
9104         });
9105         
9106     },
9107     // private
9108     onSubmit : function(e){
9109         e.stopEvent();
9110     },
9111
9112      /**
9113      * Returns true if client-side validation on the form is successful.
9114      * @return Boolean
9115      */
9116     isValid : function(){
9117         var items = this.getItems();
9118         var valid = true;
9119         var target = false;
9120         
9121         items.each(function(f){
9122             
9123             if(f.validate()){
9124                 return;
9125             }
9126             
9127             Roo.log('invalid field: ' + f.name);
9128             
9129             valid = false;
9130
9131             if(!target && f.el.isVisible(true)){
9132                 target = f;
9133             }
9134            
9135         });
9136         
9137         if(this.errorMask && !valid){
9138             Roo.bootstrap.Form.popover.mask(this, target);
9139         }
9140         
9141         return valid;
9142     },
9143     
9144     /**
9145      * Returns true if any fields in this form have changed since their original load.
9146      * @return Boolean
9147      */
9148     isDirty : function(){
9149         var dirty = false;
9150         var items = this.getItems();
9151         items.each(function(f){
9152            if(f.isDirty()){
9153                dirty = true;
9154                return false;
9155            }
9156            return true;
9157         });
9158         return dirty;
9159     },
9160      /**
9161      * Performs a predefined action (submit or load) or custom actions you define on this form.
9162      * @param {String} actionName The name of the action type
9163      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
9164      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9165      * accept other config options):
9166      * <pre>
9167 Property          Type             Description
9168 ----------------  ---------------  ----------------------------------------------------------------------------------
9169 url               String           The url for the action (defaults to the form's url)
9170 method            String           The form method to use (defaults to the form's method, or POST if not defined)
9171 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
9172 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
9173                                    validate the form on the client (defaults to false)
9174      * </pre>
9175      * @return {BasicForm} this
9176      */
9177     doAction : function(action, options){
9178         if(typeof action == 'string'){
9179             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9180         }
9181         if(this.fireEvent('beforeaction', this, action) !== false){
9182             this.beforeAction(action);
9183             action.run.defer(100, action);
9184         }
9185         return this;
9186     },
9187
9188     // private
9189     beforeAction : function(action){
9190         var o = action.options;
9191         
9192         if(this.loadMask){
9193             
9194             if(this.maskBody){
9195                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9196             } else {
9197                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9198             }
9199         }
9200         // not really supported yet.. ??
9201
9202         //if(this.waitMsgTarget === true){
9203         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9204         //}else if(this.waitMsgTarget){
9205         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9206         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9207         //}else {
9208         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9209        // }
9210
9211     },
9212
9213     // private
9214     afterAction : function(action, success){
9215         this.activeAction = null;
9216         var o = action.options;
9217
9218         if(this.loadMask){
9219             
9220             if(this.maskBody){
9221                 Roo.get(document.body).unmask();
9222             } else {
9223                 this.el.unmask();
9224             }
9225         }
9226         
9227         //if(this.waitMsgTarget === true){
9228 //            this.el.unmask();
9229         //}else if(this.waitMsgTarget){
9230         //    this.waitMsgTarget.unmask();
9231         //}else{
9232         //    Roo.MessageBox.updateProgress(1);
9233         //    Roo.MessageBox.hide();
9234        // }
9235         //
9236         if(success){
9237             if(o.reset){
9238                 this.reset();
9239             }
9240             Roo.callback(o.success, o.scope, [this, action]);
9241             this.fireEvent('actioncomplete', this, action);
9242
9243         }else{
9244
9245             // failure condition..
9246             // we have a scenario where updates need confirming.
9247             // eg. if a locking scenario exists..
9248             // we look for { errors : { needs_confirm : true }} in the response.
9249             if (
9250                 (typeof(action.result) != 'undefined')  &&
9251                 (typeof(action.result.errors) != 'undefined')  &&
9252                 (typeof(action.result.errors.needs_confirm) != 'undefined')
9253            ){
9254                 var _t = this;
9255                 Roo.log("not supported yet");
9256                  /*
9257
9258                 Roo.MessageBox.confirm(
9259                     "Change requires confirmation",
9260                     action.result.errorMsg,
9261                     function(r) {
9262                         if (r != 'yes') {
9263                             return;
9264                         }
9265                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
9266                     }
9267
9268                 );
9269                 */
9270
9271
9272                 return;
9273             }
9274
9275             Roo.callback(o.failure, o.scope, [this, action]);
9276             // show an error message if no failed handler is set..
9277             if (!this.hasListener('actionfailed')) {
9278                 Roo.log("need to add dialog support");
9279                 /*
9280                 Roo.MessageBox.alert("Error",
9281                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9282                         action.result.errorMsg :
9283                         "Saving Failed, please check your entries or try again"
9284                 );
9285                 */
9286             }
9287
9288             this.fireEvent('actionfailed', this, action);
9289         }
9290
9291     },
9292     /**
9293      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9294      * @param {String} id The value to search for
9295      * @return Field
9296      */
9297     findField : function(id){
9298         var items = this.getItems();
9299         var field = items.get(id);
9300         if(!field){
9301              items.each(function(f){
9302                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9303                     field = f;
9304                     return false;
9305                 }
9306                 return true;
9307             });
9308         }
9309         return field || null;
9310     },
9311      /**
9312      * Mark fields in this form invalid in bulk.
9313      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9314      * @return {BasicForm} this
9315      */
9316     markInvalid : function(errors){
9317         if(errors instanceof Array){
9318             for(var i = 0, len = errors.length; i < len; i++){
9319                 var fieldError = errors[i];
9320                 var f = this.findField(fieldError.id);
9321                 if(f){
9322                     f.markInvalid(fieldError.msg);
9323                 }
9324             }
9325         }else{
9326             var field, id;
9327             for(id in errors){
9328                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9329                     field.markInvalid(errors[id]);
9330                 }
9331             }
9332         }
9333         //Roo.each(this.childForms || [], function (f) {
9334         //    f.markInvalid(errors);
9335         //});
9336
9337         return this;
9338     },
9339
9340     /**
9341      * Set values for fields in this form in bulk.
9342      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9343      * @return {BasicForm} this
9344      */
9345     setValues : function(values){
9346         if(values instanceof Array){ // array of objects
9347             for(var i = 0, len = values.length; i < len; i++){
9348                 var v = values[i];
9349                 var f = this.findField(v.id);
9350                 if(f){
9351                     f.setValue(v.value);
9352                     if(this.trackResetOnLoad){
9353                         f.originalValue = f.getValue();
9354                     }
9355                 }
9356             }
9357         }else{ // object hash
9358             var field, id;
9359             for(id in values){
9360                 if(typeof values[id] != 'function' && (field = this.findField(id))){
9361
9362                     if (field.setFromData &&
9363                         field.valueField &&
9364                         field.displayField &&
9365                         // combos' with local stores can
9366                         // be queried via setValue()
9367                         // to set their value..
9368                         (field.store && !field.store.isLocal)
9369                         ) {
9370                         // it's a combo
9371                         var sd = { };
9372                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9373                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9374                         field.setFromData(sd);
9375
9376                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9377                         
9378                         field.setFromData(values);
9379                         
9380                     } else {
9381                         field.setValue(values[id]);
9382                     }
9383
9384
9385                     if(this.trackResetOnLoad){
9386                         field.originalValue = field.getValue();
9387                     }
9388                 }
9389             }
9390         }
9391
9392         //Roo.each(this.childForms || [], function (f) {
9393         //    f.setValues(values);
9394         //});
9395
9396         return this;
9397     },
9398
9399     /**
9400      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9401      * they are returned as an array.
9402      * @param {Boolean} asString
9403      * @return {Object}
9404      */
9405     getValues : function(asString){
9406         //if (this.childForms) {
9407             // copy values from the child forms
9408         //    Roo.each(this.childForms, function (f) {
9409         //        this.setValues(f.getValues());
9410         //    }, this);
9411         //}
9412
9413
9414
9415         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
9416         if(asString === true){
9417             return fs;
9418         }
9419         return Roo.urlDecode(fs);
9420     },
9421
9422     /**
9423      * Returns the fields in this form as an object with key/value pairs.
9424      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
9425      * @return {Object}
9426      */
9427     getFieldValues : function(with_hidden)
9428     {
9429         var items = this.getItems();
9430         var ret = {};
9431         items.each(function(f){
9432             
9433             if (!f.getName()) {
9434                 return;
9435             }
9436             
9437             var v = f.getValue();
9438             
9439             if (f.inputType =='radio') {
9440                 if (typeof(ret[f.getName()]) == 'undefined') {
9441                     ret[f.getName()] = ''; // empty..
9442                 }
9443
9444                 if (!f.el.dom.checked) {
9445                     return;
9446
9447                 }
9448                 v = f.el.dom.value;
9449
9450             }
9451             
9452             if(f.xtype == 'MoneyField'){
9453                 ret[f.currencyName] = f.getCurrency();
9454             }
9455
9456             // not sure if this supported any more..
9457             if ((typeof(v) == 'object') && f.getRawValue) {
9458                 v = f.getRawValue() ; // dates..
9459             }
9460             // combo boxes where name != hiddenName...
9461             if (f.name !== false && f.name != '' && f.name != f.getName()) {
9462                 ret[f.name] = f.getRawValue();
9463             }
9464             ret[f.getName()] = v;
9465         });
9466
9467         return ret;
9468     },
9469
9470     /**
9471      * Clears all invalid messages in this form.
9472      * @return {BasicForm} this
9473      */
9474     clearInvalid : function(){
9475         var items = this.getItems();
9476
9477         items.each(function(f){
9478            f.clearInvalid();
9479         });
9480
9481         return this;
9482     },
9483
9484     /**
9485      * Resets this form.
9486      * @return {BasicForm} this
9487      */
9488     reset : function(){
9489         var items = this.getItems();
9490         items.each(function(f){
9491             f.reset();
9492         });
9493
9494         Roo.each(this.childForms || [], function (f) {
9495             f.reset();
9496         });
9497
9498
9499         return this;
9500     },
9501     
9502     getItems : function()
9503     {
9504         var r=new Roo.util.MixedCollection(false, function(o){
9505             return o.id || (o.id = Roo.id());
9506         });
9507         var iter = function(el) {
9508             if (el.inputEl) {
9509                 r.add(el);
9510             }
9511             if (!el.items) {
9512                 return;
9513             }
9514             Roo.each(el.items,function(e) {
9515                 iter(e);
9516             });
9517         };
9518
9519         iter(this);
9520         return r;
9521     },
9522     
9523     hideFields : function(items)
9524     {
9525         Roo.each(items, function(i){
9526             
9527             var f = this.findField(i);
9528             
9529             if(!f){
9530                 return;
9531             }
9532             
9533             f.hide();
9534             
9535         }, this);
9536     },
9537     
9538     showFields : function(items)
9539     {
9540         Roo.each(items, function(i){
9541             
9542             var f = this.findField(i);
9543             
9544             if(!f){
9545                 return;
9546             }
9547             
9548             f.show();
9549             
9550         }, this);
9551     }
9552
9553 });
9554
9555 Roo.apply(Roo.bootstrap.Form, {
9556     
9557     popover : {
9558         
9559         padding : 5,
9560         
9561         isApplied : false,
9562         
9563         isMasked : false,
9564         
9565         form : false,
9566         
9567         target : false,
9568         
9569         toolTip : false,
9570         
9571         intervalID : false,
9572         
9573         maskEl : false,
9574         
9575         apply : function()
9576         {
9577             if(this.isApplied){
9578                 return;
9579             }
9580             
9581             this.maskEl = {
9582                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
9583                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
9584                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
9585                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
9586             };
9587             
9588             this.maskEl.top.enableDisplayMode("block");
9589             this.maskEl.left.enableDisplayMode("block");
9590             this.maskEl.bottom.enableDisplayMode("block");
9591             this.maskEl.right.enableDisplayMode("block");
9592             
9593             this.toolTip = new Roo.bootstrap.Tooltip({
9594                 cls : 'roo-form-error-popover',
9595                 alignment : {
9596                     'left' : ['r-l', [-2,0], 'right'],
9597                     'right' : ['l-r', [2,0], 'left'],
9598                     'bottom' : ['tl-bl', [0,2], 'top'],
9599                     'top' : [ 'bl-tl', [0,-2], 'bottom']
9600                 }
9601             });
9602             
9603             this.toolTip.render(Roo.get(document.body));
9604
9605             this.toolTip.el.enableDisplayMode("block");
9606             
9607             Roo.get(document.body).on('click', function(){
9608                 this.unmask();
9609             }, this);
9610             
9611             Roo.get(document.body).on('touchstart', function(){
9612                 this.unmask();
9613             }, this);
9614             
9615             this.isApplied = true
9616         },
9617         
9618         mask : function(form, target)
9619         {
9620             this.form = form;
9621             
9622             this.target = target;
9623             
9624             if(!this.form.errorMask || !target.el){
9625                 return;
9626             }
9627             
9628             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
9629             
9630             Roo.log(scrollable);
9631             
9632             var ot = this.target.el.calcOffsetsTo(scrollable);
9633             
9634             var scrollTo = ot[1] - this.form.maskOffset;
9635             
9636             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
9637             
9638             scrollable.scrollTo('top', scrollTo);
9639             
9640             var box = this.target.el.getBox();
9641             Roo.log(box);
9642             var zIndex = Roo.bootstrap.Modal.zIndex++;
9643
9644             
9645             this.maskEl.top.setStyle('position', 'absolute');
9646             this.maskEl.top.setStyle('z-index', zIndex);
9647             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
9648             this.maskEl.top.setLeft(0);
9649             this.maskEl.top.setTop(0);
9650             this.maskEl.top.show();
9651             
9652             this.maskEl.left.setStyle('position', 'absolute');
9653             this.maskEl.left.setStyle('z-index', zIndex);
9654             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
9655             this.maskEl.left.setLeft(0);
9656             this.maskEl.left.setTop(box.y - this.padding);
9657             this.maskEl.left.show();
9658
9659             this.maskEl.bottom.setStyle('position', 'absolute');
9660             this.maskEl.bottom.setStyle('z-index', zIndex);
9661             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
9662             this.maskEl.bottom.setLeft(0);
9663             this.maskEl.bottom.setTop(box.bottom + this.padding);
9664             this.maskEl.bottom.show();
9665
9666             this.maskEl.right.setStyle('position', 'absolute');
9667             this.maskEl.right.setStyle('z-index', zIndex);
9668             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
9669             this.maskEl.right.setLeft(box.right + this.padding);
9670             this.maskEl.right.setTop(box.y - this.padding);
9671             this.maskEl.right.show();
9672
9673             this.toolTip.bindEl = this.target.el;
9674
9675             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
9676
9677             var tip = this.target.blankText;
9678
9679             if(this.target.getValue() !== '' ) {
9680                 
9681                 if (this.target.invalidText.length) {
9682                     tip = this.target.invalidText;
9683                 } else if (this.target.regexText.length){
9684                     tip = this.target.regexText;
9685                 }
9686             }
9687
9688             this.toolTip.show(tip);
9689
9690             this.intervalID = window.setInterval(function() {
9691                 Roo.bootstrap.Form.popover.unmask();
9692             }, 10000);
9693
9694             window.onwheel = function(){ return false;};
9695             
9696             (function(){ this.isMasked = true; }).defer(500, this);
9697             
9698         },
9699         
9700         unmask : function()
9701         {
9702             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
9703                 return;
9704             }
9705             
9706             this.maskEl.top.setStyle('position', 'absolute');
9707             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
9708             this.maskEl.top.hide();
9709
9710             this.maskEl.left.setStyle('position', 'absolute');
9711             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
9712             this.maskEl.left.hide();
9713
9714             this.maskEl.bottom.setStyle('position', 'absolute');
9715             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
9716             this.maskEl.bottom.hide();
9717
9718             this.maskEl.right.setStyle('position', 'absolute');
9719             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
9720             this.maskEl.right.hide();
9721             
9722             this.toolTip.hide();
9723             
9724             this.toolTip.el.hide();
9725             
9726             window.onwheel = function(){ return true;};
9727             
9728             if(this.intervalID){
9729                 window.clearInterval(this.intervalID);
9730                 this.intervalID = false;
9731             }
9732             
9733             this.isMasked = false;
9734             
9735         }
9736         
9737     }
9738     
9739 });
9740
9741 /*
9742  * Based on:
9743  * Ext JS Library 1.1.1
9744  * Copyright(c) 2006-2007, Ext JS, LLC.
9745  *
9746  * Originally Released Under LGPL - original licence link has changed is not relivant.
9747  *
9748  * Fork - LGPL
9749  * <script type="text/javascript">
9750  */
9751 /**
9752  * @class Roo.form.VTypes
9753  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
9754  * @singleton
9755  */
9756 Roo.form.VTypes = function(){
9757     // closure these in so they are only created once.
9758     var alpha = /^[a-zA-Z_]+$/;
9759     var alphanum = /^[a-zA-Z0-9_]+$/;
9760     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
9761     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
9762
9763     // All these messages and functions are configurable
9764     return {
9765         /**
9766          * The function used to validate email addresses
9767          * @param {String} value The email address
9768          */
9769         'email' : function(v){
9770             return email.test(v);
9771         },
9772         /**
9773          * The error text to display when the email validation function returns false
9774          * @type String
9775          */
9776         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
9777         /**
9778          * The keystroke filter mask to be applied on email input
9779          * @type RegExp
9780          */
9781         'emailMask' : /[a-z0-9_\.\-@]/i,
9782
9783         /**
9784          * The function used to validate URLs
9785          * @param {String} value The URL
9786          */
9787         'url' : function(v){
9788             return url.test(v);
9789         },
9790         /**
9791          * The error text to display when the url validation function returns false
9792          * @type String
9793          */
9794         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
9795         
9796         /**
9797          * The function used to validate alpha values
9798          * @param {String} value The value
9799          */
9800         'alpha' : function(v){
9801             return alpha.test(v);
9802         },
9803         /**
9804          * The error text to display when the alpha validation function returns false
9805          * @type String
9806          */
9807         'alphaText' : 'This field should only contain letters and _',
9808         /**
9809          * The keystroke filter mask to be applied on alpha input
9810          * @type RegExp
9811          */
9812         'alphaMask' : /[a-z_]/i,
9813
9814         /**
9815          * The function used to validate alphanumeric values
9816          * @param {String} value The value
9817          */
9818         'alphanum' : function(v){
9819             return alphanum.test(v);
9820         },
9821         /**
9822          * The error text to display when the alphanumeric validation function returns false
9823          * @type String
9824          */
9825         'alphanumText' : 'This field should only contain letters, numbers and _',
9826         /**
9827          * The keystroke filter mask to be applied on alphanumeric input
9828          * @type RegExp
9829          */
9830         'alphanumMask' : /[a-z0-9_]/i
9831     };
9832 }();/*
9833  * - LGPL
9834  *
9835  * Input
9836  * 
9837  */
9838
9839 /**
9840  * @class Roo.bootstrap.Input
9841  * @extends Roo.bootstrap.Component
9842  * Bootstrap Input class
9843  * @cfg {Boolean} disabled is it disabled
9844  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
9845  * @cfg {String} name name of the input
9846  * @cfg {string} fieldLabel - the label associated
9847  * @cfg {string} placeholder - placeholder to put in text.
9848  * @cfg {string}  before - input group add on before
9849  * @cfg {string} after - input group add on after
9850  * @cfg {string} size - (lg|sm) or leave empty..
9851  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
9852  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
9853  * @cfg {Number} md colspan out of 12 for computer-sized screens
9854  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
9855  * @cfg {string} value default value of the input
9856  * @cfg {Number} labelWidth set the width of label 
9857  * @cfg {Number} labellg set the width of label (1-12)
9858  * @cfg {Number} labelmd set the width of label (1-12)
9859  * @cfg {Number} labelsm set the width of label (1-12)
9860  * @cfg {Number} labelxs set the width of label (1-12)
9861  * @cfg {String} labelAlign (top|left)
9862  * @cfg {Boolean} readOnly Specifies that the field should be read-only
9863  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
9864  * @cfg {String} indicatorpos (left|right) default left
9865  * @cfg {String} capture (user|camera) use for file input only. (default empty)
9866  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
9867
9868  * @cfg {String} align (left|center|right) Default left
9869  * @cfg {Boolean} forceFeedback (true|false) Default false
9870  * 
9871  * @constructor
9872  * Create a new Input
9873  * @param {Object} config The config object
9874  */
9875
9876 Roo.bootstrap.Input = function(config){
9877     
9878     Roo.bootstrap.Input.superclass.constructor.call(this, config);
9879     
9880     this.addEvents({
9881         /**
9882          * @event focus
9883          * Fires when this field receives input focus.
9884          * @param {Roo.form.Field} this
9885          */
9886         focus : true,
9887         /**
9888          * @event blur
9889          * Fires when this field loses input focus.
9890          * @param {Roo.form.Field} this
9891          */
9892         blur : true,
9893         /**
9894          * @event specialkey
9895          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
9896          * {@link Roo.EventObject#getKey} to determine which key was pressed.
9897          * @param {Roo.form.Field} this
9898          * @param {Roo.EventObject} e The event object
9899          */
9900         specialkey : true,
9901         /**
9902          * @event change
9903          * Fires just before the field blurs if the field value has changed.
9904          * @param {Roo.form.Field} this
9905          * @param {Mixed} newValue The new value
9906          * @param {Mixed} oldValue The original value
9907          */
9908         change : true,
9909         /**
9910          * @event invalid
9911          * Fires after the field has been marked as invalid.
9912          * @param {Roo.form.Field} this
9913          * @param {String} msg The validation message
9914          */
9915         invalid : true,
9916         /**
9917          * @event valid
9918          * Fires after the field has been validated with no errors.
9919          * @param {Roo.form.Field} this
9920          */
9921         valid : true,
9922          /**
9923          * @event keyup
9924          * Fires after the key up
9925          * @param {Roo.form.Field} this
9926          * @param {Roo.EventObject}  e The event Object
9927          */
9928         keyup : true
9929     });
9930 };
9931
9932 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
9933      /**
9934      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
9935       automatic validation (defaults to "keyup").
9936      */
9937     validationEvent : "keyup",
9938      /**
9939      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
9940      */
9941     validateOnBlur : true,
9942     /**
9943      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
9944      */
9945     validationDelay : 250,
9946      /**
9947      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
9948      */
9949     focusClass : "x-form-focus",  // not needed???
9950     
9951        
9952     /**
9953      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9954      */
9955     invalidClass : "has-warning",
9956     
9957     /**
9958      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9959      */
9960     validClass : "has-success",
9961     
9962     /**
9963      * @cfg {Boolean} hasFeedback (true|false) default true
9964      */
9965     hasFeedback : true,
9966     
9967     /**
9968      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9969      */
9970     invalidFeedbackClass : "glyphicon-warning-sign",
9971     
9972     /**
9973      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9974      */
9975     validFeedbackClass : "glyphicon-ok",
9976     
9977     /**
9978      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
9979      */
9980     selectOnFocus : false,
9981     
9982      /**
9983      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
9984      */
9985     maskRe : null,
9986        /**
9987      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
9988      */
9989     vtype : null,
9990     
9991       /**
9992      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
9993      */
9994     disableKeyFilter : false,
9995     
9996        /**
9997      * @cfg {Boolean} disabled True to disable the field (defaults to false).
9998      */
9999     disabled : false,
10000      /**
10001      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10002      */
10003     allowBlank : true,
10004     /**
10005      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10006      */
10007     blankText : "Please complete this mandatory field",
10008     
10009      /**
10010      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10011      */
10012     minLength : 0,
10013     /**
10014      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10015      */
10016     maxLength : Number.MAX_VALUE,
10017     /**
10018      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10019      */
10020     minLengthText : "The minimum length for this field is {0}",
10021     /**
10022      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10023      */
10024     maxLengthText : "The maximum length for this field is {0}",
10025   
10026     
10027     /**
10028      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10029      * If available, this function will be called only after the basic validators all return true, and will be passed the
10030      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10031      */
10032     validator : null,
10033     /**
10034      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10035      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10036      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10037      */
10038     regex : null,
10039     /**
10040      * @cfg {String} regexText -- Depricated - use Invalid Text
10041      */
10042     regexText : "",
10043     
10044     /**
10045      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10046      */
10047     invalidText : "",
10048     
10049     
10050     
10051     autocomplete: false,
10052     
10053     
10054     fieldLabel : '',
10055     inputType : 'text',
10056     
10057     name : false,
10058     placeholder: false,
10059     before : false,
10060     after : false,
10061     size : false,
10062     hasFocus : false,
10063     preventMark: false,
10064     isFormField : true,
10065     value : '',
10066     labelWidth : 2,
10067     labelAlign : false,
10068     readOnly : false,
10069     align : false,
10070     formatedValue : false,
10071     forceFeedback : false,
10072     
10073     indicatorpos : 'left',
10074     
10075     labellg : 0,
10076     labelmd : 0,
10077     labelsm : 0,
10078     labelxs : 0,
10079     
10080     capture : '',
10081     accept : '',
10082     
10083     parentLabelAlign : function()
10084     {
10085         var parent = this;
10086         while (parent.parent()) {
10087             parent = parent.parent();
10088             if (typeof(parent.labelAlign) !='undefined') {
10089                 return parent.labelAlign;
10090             }
10091         }
10092         return 'left';
10093         
10094     },
10095     
10096     getAutoCreate : function()
10097     {
10098         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10099         
10100         var id = Roo.id();
10101         
10102         var cfg = {};
10103         
10104         if(this.inputType != 'hidden'){
10105             cfg.cls = 'form-group' //input-group
10106         }
10107         
10108         var input =  {
10109             tag: 'input',
10110             id : id,
10111             type : this.inputType,
10112             value : this.value,
10113             cls : 'form-control',
10114             placeholder : this.placeholder || '',
10115             autocomplete : this.autocomplete || 'new-password'
10116         };
10117         
10118         if(this.capture.length){
10119             input.capture = this.capture;
10120         }
10121         
10122         if(this.accept.length){
10123             input.accept = this.accept + "/*";
10124         }
10125         
10126         if(this.align){
10127             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10128         }
10129         
10130         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10131             input.maxLength = this.maxLength;
10132         }
10133         
10134         if (this.disabled) {
10135             input.disabled=true;
10136         }
10137         
10138         if (this.readOnly) {
10139             input.readonly=true;
10140         }
10141         
10142         if (this.name) {
10143             input.name = this.name;
10144         }
10145         
10146         if (this.size) {
10147             input.cls += ' input-' + this.size;
10148         }
10149         
10150         var settings=this;
10151         ['xs','sm','md','lg'].map(function(size){
10152             if (settings[size]) {
10153                 cfg.cls += ' col-' + size + '-' + settings[size];
10154             }
10155         });
10156         
10157         var inputblock = input;
10158         
10159         var feedback = {
10160             tag: 'span',
10161             cls: 'glyphicon form-control-feedback'
10162         };
10163             
10164         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10165             
10166             inputblock = {
10167                 cls : 'has-feedback',
10168                 cn :  [
10169                     input,
10170                     feedback
10171                 ] 
10172             };  
10173         }
10174         
10175         if (this.before || this.after) {
10176             
10177             inputblock = {
10178                 cls : 'input-group',
10179                 cn :  [] 
10180             };
10181             
10182             if (this.before && typeof(this.before) == 'string') {
10183                 
10184                 inputblock.cn.push({
10185                     tag :'span',
10186                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10187                     html : this.before
10188                 });
10189             }
10190             if (this.before && typeof(this.before) == 'object') {
10191                 this.before = Roo.factory(this.before);
10192                 
10193                 inputblock.cn.push({
10194                     tag :'span',
10195                     cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
10196                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10197                 });
10198             }
10199             
10200             inputblock.cn.push(input);
10201             
10202             if (this.after && typeof(this.after) == 'string') {
10203                 inputblock.cn.push({
10204                     tag :'span',
10205                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10206                     html : this.after
10207                 });
10208             }
10209             if (this.after && typeof(this.after) == 'object') {
10210                 this.after = Roo.factory(this.after);
10211                 
10212                 inputblock.cn.push({
10213                     tag :'span',
10214                     cls : 'roo-input-after input-group-append input-group-text input-group-' +
10215                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10216                 });
10217             }
10218             
10219             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10220                 inputblock.cls += ' has-feedback';
10221                 inputblock.cn.push(feedback);
10222             }
10223         };
10224         var indicator = {
10225             tag : 'i',
10226             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10227             tooltip : 'This field is required'
10228         };
10229         if (Roo.bootstrap.version == 4) {
10230             indicator = {
10231                 tag : 'i',
10232                 style : 'display-none'
10233             };
10234         }
10235         if (align ==='left' && this.fieldLabel.length) {
10236             
10237             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10238             
10239             cfg.cn = [
10240                 indicator,
10241                 {
10242                     tag: 'label',
10243                     'for' :  id,
10244                     cls : 'control-label col-form-label',
10245                     html : this.fieldLabel
10246
10247                 },
10248                 {
10249                     cls : "", 
10250                     cn: [
10251                         inputblock
10252                     ]
10253                 }
10254             ];
10255             
10256             var labelCfg = cfg.cn[1];
10257             var contentCfg = cfg.cn[2];
10258             
10259             if(this.indicatorpos == 'right'){
10260                 cfg.cn = [
10261                     {
10262                         tag: 'label',
10263                         'for' :  id,
10264                         cls : 'control-label col-form-label',
10265                         cn : [
10266                             {
10267                                 tag : 'span',
10268                                 html : this.fieldLabel
10269                             },
10270                             indicator
10271                         ]
10272                     },
10273                     {
10274                         cls : "",
10275                         cn: [
10276                             inputblock
10277                         ]
10278                     }
10279
10280                 ];
10281                 
10282                 labelCfg = cfg.cn[0];
10283                 contentCfg = cfg.cn[1];
10284             
10285             }
10286             
10287             if(this.labelWidth > 12){
10288                 labelCfg.style = "width: " + this.labelWidth + 'px';
10289             }
10290             
10291             if(this.labelWidth < 13 && this.labelmd == 0){
10292                 this.labelmd = this.labelWidth;
10293             }
10294             
10295             if(this.labellg > 0){
10296                 labelCfg.cls += ' col-lg-' + this.labellg;
10297                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10298             }
10299             
10300             if(this.labelmd > 0){
10301                 labelCfg.cls += ' col-md-' + this.labelmd;
10302                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10303             }
10304             
10305             if(this.labelsm > 0){
10306                 labelCfg.cls += ' col-sm-' + this.labelsm;
10307                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10308             }
10309             
10310             if(this.labelxs > 0){
10311                 labelCfg.cls += ' col-xs-' + this.labelxs;
10312                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10313             }
10314             
10315             
10316         } else if ( this.fieldLabel.length) {
10317                 
10318             cfg.cn = [
10319                 {
10320                     tag : 'i',
10321                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10322                     tooltip : 'This field is required'
10323                 },
10324                 {
10325                     tag: 'label',
10326                    //cls : 'input-group-addon',
10327                     html : this.fieldLabel
10328
10329                 },
10330
10331                inputblock
10332
10333            ];
10334            
10335            if(this.indicatorpos == 'right'){
10336                 
10337                 cfg.cn = [
10338                     {
10339                         tag: 'label',
10340                        //cls : 'input-group-addon',
10341                         html : this.fieldLabel
10342
10343                     },
10344                     {
10345                         tag : 'i',
10346                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10347                         tooltip : 'This field is required'
10348                     },
10349
10350                    inputblock
10351
10352                ];
10353
10354             }
10355
10356         } else {
10357             
10358             cfg.cn = [
10359
10360                     inputblock
10361
10362             ];
10363                 
10364                 
10365         };
10366         
10367         if (this.parentType === 'Navbar' &&  this.parent().bar) {
10368            cfg.cls += ' navbar-form';
10369         }
10370         
10371         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10372             // on BS4 we do this only if not form 
10373             cfg.cls += ' navbar-form';
10374             cfg.tag = 'li';
10375         }
10376         
10377         return cfg;
10378         
10379     },
10380     /**
10381      * return the real input element.
10382      */
10383     inputEl: function ()
10384     {
10385         return this.el.select('input.form-control',true).first();
10386     },
10387     
10388     tooltipEl : function()
10389     {
10390         return this.inputEl();
10391     },
10392     
10393     indicatorEl : function()
10394     {
10395         if (Roo.bootstrap.version == 4) {
10396             return false; // not enabled in v4 yet.
10397         }
10398         
10399         var indicator = this.el.select('i.roo-required-indicator',true).first();
10400         
10401         if(!indicator){
10402             return false;
10403         }
10404         
10405         return indicator;
10406         
10407     },
10408     
10409     setDisabled : function(v)
10410     {
10411         var i  = this.inputEl().dom;
10412         if (!v) {
10413             i.removeAttribute('disabled');
10414             return;
10415             
10416         }
10417         i.setAttribute('disabled','true');
10418     },
10419     initEvents : function()
10420     {
10421           
10422         this.inputEl().on("keydown" , this.fireKey,  this);
10423         this.inputEl().on("focus", this.onFocus,  this);
10424         this.inputEl().on("blur", this.onBlur,  this);
10425         
10426         this.inputEl().relayEvent('keyup', this);
10427         
10428         this.indicator = this.indicatorEl();
10429         
10430         if(this.indicator){
10431             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
10432         }
10433  
10434         // reference to original value for reset
10435         this.originalValue = this.getValue();
10436         //Roo.form.TextField.superclass.initEvents.call(this);
10437         if(this.validationEvent == 'keyup'){
10438             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
10439             this.inputEl().on('keyup', this.filterValidation, this);
10440         }
10441         else if(this.validationEvent !== false){
10442             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
10443         }
10444         
10445         if(this.selectOnFocus){
10446             this.on("focus", this.preFocus, this);
10447             
10448         }
10449         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
10450             this.inputEl().on("keypress", this.filterKeys, this);
10451         } else {
10452             this.inputEl().relayEvent('keypress', this);
10453         }
10454        /* if(this.grow){
10455             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
10456             this.el.on("click", this.autoSize,  this);
10457         }
10458         */
10459         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
10460             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
10461         }
10462         
10463         if (typeof(this.before) == 'object') {
10464             this.before.render(this.el.select('.roo-input-before',true).first());
10465         }
10466         if (typeof(this.after) == 'object') {
10467             this.after.render(this.el.select('.roo-input-after',true).first());
10468         }
10469         
10470         this.inputEl().on('change', this.onChange, this);
10471         
10472     },
10473     filterValidation : function(e){
10474         if(!e.isNavKeyPress()){
10475             this.validationTask.delay(this.validationDelay);
10476         }
10477     },
10478      /**
10479      * Validates the field value
10480      * @return {Boolean} True if the value is valid, else false
10481      */
10482     validate : function(){
10483         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
10484         if(this.disabled || this.validateValue(this.getRawValue())){
10485             this.markValid();
10486             return true;
10487         }
10488         
10489         this.markInvalid();
10490         return false;
10491     },
10492     
10493     
10494     /**
10495      * Validates a value according to the field's validation rules and marks the field as invalid
10496      * if the validation fails
10497      * @param {Mixed} value The value to validate
10498      * @return {Boolean} True if the value is valid, else false
10499      */
10500     validateValue : function(value)
10501     {
10502         if(this.getVisibilityEl().hasClass('hidden')){
10503             return true;
10504         }
10505         
10506         if(value.length < 1)  { // if it's blank
10507             if(this.allowBlank){
10508                 return true;
10509             }
10510             return false;
10511         }
10512         
10513         if(value.length < this.minLength){
10514             return false;
10515         }
10516         if(value.length > this.maxLength){
10517             return false;
10518         }
10519         if(this.vtype){
10520             var vt = Roo.form.VTypes;
10521             if(!vt[this.vtype](value, this)){
10522                 return false;
10523             }
10524         }
10525         if(typeof this.validator == "function"){
10526             var msg = this.validator(value);
10527             if(msg !== true){
10528                 return false;
10529             }
10530             if (typeof(msg) == 'string') {
10531                 this.invalidText = msg;
10532             }
10533         }
10534         
10535         if(this.regex && !this.regex.test(value)){
10536             return false;
10537         }
10538         
10539         return true;
10540     },
10541     
10542      // private
10543     fireKey : function(e){
10544         //Roo.log('field ' + e.getKey());
10545         if(e.isNavKeyPress()){
10546             this.fireEvent("specialkey", this, e);
10547         }
10548     },
10549     focus : function (selectText){
10550         if(this.rendered){
10551             this.inputEl().focus();
10552             if(selectText === true){
10553                 this.inputEl().dom.select();
10554             }
10555         }
10556         return this;
10557     } ,
10558     
10559     onFocus : function(){
10560         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10561            // this.el.addClass(this.focusClass);
10562         }
10563         if(!this.hasFocus){
10564             this.hasFocus = true;
10565             this.startValue = this.getValue();
10566             this.fireEvent("focus", this);
10567         }
10568     },
10569     
10570     beforeBlur : Roo.emptyFn,
10571
10572     
10573     // private
10574     onBlur : function(){
10575         this.beforeBlur();
10576         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10577             //this.el.removeClass(this.focusClass);
10578         }
10579         this.hasFocus = false;
10580         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
10581             this.validate();
10582         }
10583         var v = this.getValue();
10584         if(String(v) !== String(this.startValue)){
10585             this.fireEvent('change', this, v, this.startValue);
10586         }
10587         this.fireEvent("blur", this);
10588     },
10589     
10590     onChange : function(e)
10591     {
10592         var v = this.getValue();
10593         if(String(v) !== String(this.startValue)){
10594             this.fireEvent('change', this, v, this.startValue);
10595         }
10596         
10597     },
10598     
10599     /**
10600      * Resets the current field value to the originally loaded value and clears any validation messages
10601      */
10602     reset : function(){
10603         this.setValue(this.originalValue);
10604         this.validate();
10605     },
10606      /**
10607      * Returns the name of the field
10608      * @return {Mixed} name The name field
10609      */
10610     getName: function(){
10611         return this.name;
10612     },
10613      /**
10614      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
10615      * @return {Mixed} value The field value
10616      */
10617     getValue : function(){
10618         
10619         var v = this.inputEl().getValue();
10620         
10621         return v;
10622     },
10623     /**
10624      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
10625      * @return {Mixed} value The field value
10626      */
10627     getRawValue : function(){
10628         var v = this.inputEl().getValue();
10629         
10630         return v;
10631     },
10632     
10633     /**
10634      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
10635      * @param {Mixed} value The value to set
10636      */
10637     setRawValue : function(v){
10638         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
10639     },
10640     
10641     selectText : function(start, end){
10642         var v = this.getRawValue();
10643         if(v.length > 0){
10644             start = start === undefined ? 0 : start;
10645             end = end === undefined ? v.length : end;
10646             var d = this.inputEl().dom;
10647             if(d.setSelectionRange){
10648                 d.setSelectionRange(start, end);
10649             }else if(d.createTextRange){
10650                 var range = d.createTextRange();
10651                 range.moveStart("character", start);
10652                 range.moveEnd("character", v.length-end);
10653                 range.select();
10654             }
10655         }
10656     },
10657     
10658     /**
10659      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
10660      * @param {Mixed} value The value to set
10661      */
10662     setValue : function(v){
10663         this.value = v;
10664         if(this.rendered){
10665             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
10666             this.validate();
10667         }
10668     },
10669     
10670     /*
10671     processValue : function(value){
10672         if(this.stripCharsRe){
10673             var newValue = value.replace(this.stripCharsRe, '');
10674             if(newValue !== value){
10675                 this.setRawValue(newValue);
10676                 return newValue;
10677             }
10678         }
10679         return value;
10680     },
10681   */
10682     preFocus : function(){
10683         
10684         if(this.selectOnFocus){
10685             this.inputEl().dom.select();
10686         }
10687     },
10688     filterKeys : function(e){
10689         var k = e.getKey();
10690         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
10691             return;
10692         }
10693         var c = e.getCharCode(), cc = String.fromCharCode(c);
10694         if(Roo.isIE && (e.isSpecialKey() || !cc)){
10695             return;
10696         }
10697         if(!this.maskRe.test(cc)){
10698             e.stopEvent();
10699         }
10700     },
10701      /**
10702      * Clear any invalid styles/messages for this field
10703      */
10704     clearInvalid : function(){
10705         
10706         if(!this.el || this.preventMark){ // not rendered
10707             return;
10708         }
10709         
10710         
10711         this.el.removeClass([this.invalidClass, 'is-invalid']);
10712         
10713         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10714             
10715             var feedback = this.el.select('.form-control-feedback', true).first();
10716             
10717             if(feedback){
10718                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10719             }
10720             
10721         }
10722         
10723         if(this.indicator){
10724             this.indicator.removeClass('visible');
10725             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
10726         }
10727         
10728         this.fireEvent('valid', this);
10729     },
10730     
10731      /**
10732      * Mark this field as valid
10733      */
10734     markValid : function()
10735     {
10736         if(!this.el  || this.preventMark){ // not rendered...
10737             return;
10738         }
10739         
10740         this.el.removeClass([this.invalidClass, this.validClass]);
10741         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10742
10743         var feedback = this.el.select('.form-control-feedback', true).first();
10744             
10745         if(feedback){
10746             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10747         }
10748         
10749         if(this.indicator){
10750             this.indicator.removeClass('visible');
10751             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
10752         }
10753         
10754         if(this.disabled){
10755             return;
10756         }
10757         
10758         if(this.allowBlank && !this.getRawValue().length){
10759             return;
10760         }
10761         if (Roo.bootstrap.version == 3) {
10762             this.el.addClass(this.validClass);
10763         } else {
10764             this.inputEl().addClass('is-valid');
10765         }
10766
10767         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10768             
10769             var feedback = this.el.select('.form-control-feedback', true).first();
10770             
10771             if(feedback){
10772                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10773                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10774             }
10775             
10776         }
10777         
10778         this.fireEvent('valid', this);
10779     },
10780     
10781      /**
10782      * Mark this field as invalid
10783      * @param {String} msg The validation message
10784      */
10785     markInvalid : function(msg)
10786     {
10787         if(!this.el  || this.preventMark){ // not rendered
10788             return;
10789         }
10790         
10791         this.el.removeClass([this.invalidClass, this.validClass]);
10792         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10793         
10794         var feedback = this.el.select('.form-control-feedback', true).first();
10795             
10796         if(feedback){
10797             this.el.select('.form-control-feedback', true).first().removeClass(
10798                     [this.invalidFeedbackClass, this.validFeedbackClass]);
10799         }
10800
10801         if(this.disabled){
10802             return;
10803         }
10804         
10805         if(this.allowBlank && !this.getRawValue().length){
10806             return;
10807         }
10808         
10809         if(this.indicator){
10810             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
10811             this.indicator.addClass('visible');
10812         }
10813         if (Roo.bootstrap.version == 3) {
10814             this.el.addClass(this.invalidClass);
10815         } else {
10816             this.inputEl().addClass('is-invalid');
10817         }
10818         
10819         
10820         
10821         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10822             
10823             var feedback = this.el.select('.form-control-feedback', true).first();
10824             
10825             if(feedback){
10826                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10827                 
10828                 if(this.getValue().length || this.forceFeedback){
10829                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10830                 }
10831                 
10832             }
10833             
10834         }
10835         
10836         this.fireEvent('invalid', this, msg);
10837     },
10838     // private
10839     SafariOnKeyDown : function(event)
10840     {
10841         // this is a workaround for a password hang bug on chrome/ webkit.
10842         if (this.inputEl().dom.type != 'password') {
10843             return;
10844         }
10845         
10846         var isSelectAll = false;
10847         
10848         if(this.inputEl().dom.selectionEnd > 0){
10849             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
10850         }
10851         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
10852             event.preventDefault();
10853             this.setValue('');
10854             return;
10855         }
10856         
10857         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
10858             
10859             event.preventDefault();
10860             // this is very hacky as keydown always get's upper case.
10861             //
10862             var cc = String.fromCharCode(event.getCharCode());
10863             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
10864             
10865         }
10866     },
10867     adjustWidth : function(tag, w){
10868         tag = tag.toLowerCase();
10869         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
10870             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
10871                 if(tag == 'input'){
10872                     return w + 2;
10873                 }
10874                 if(tag == 'textarea'){
10875                     return w-2;
10876                 }
10877             }else if(Roo.isOpera){
10878                 if(tag == 'input'){
10879                     return w + 2;
10880                 }
10881                 if(tag == 'textarea'){
10882                     return w-2;
10883                 }
10884             }
10885         }
10886         return w;
10887     },
10888     
10889     setFieldLabel : function(v)
10890     {
10891         if(!this.rendered){
10892             return;
10893         }
10894         
10895         if(this.indicatorEl()){
10896             var ar = this.el.select('label > span',true);
10897             
10898             if (ar.elements.length) {
10899                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10900                 this.fieldLabel = v;
10901                 return;
10902             }
10903             
10904             var br = this.el.select('label',true);
10905             
10906             if(br.elements.length) {
10907                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10908                 this.fieldLabel = v;
10909                 return;
10910             }
10911             
10912             Roo.log('Cannot Found any of label > span || label in input');
10913             return;
10914         }
10915         
10916         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10917         this.fieldLabel = v;
10918         
10919         
10920     }
10921 });
10922
10923  
10924 /*
10925  * - LGPL
10926  *
10927  * Input
10928  * 
10929  */
10930
10931 /**
10932  * @class Roo.bootstrap.TextArea
10933  * @extends Roo.bootstrap.Input
10934  * Bootstrap TextArea class
10935  * @cfg {Number} cols Specifies the visible width of a text area
10936  * @cfg {Number} rows Specifies the visible number of lines in a text area
10937  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
10938  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
10939  * @cfg {string} html text
10940  * 
10941  * @constructor
10942  * Create a new TextArea
10943  * @param {Object} config The config object
10944  */
10945
10946 Roo.bootstrap.TextArea = function(config){
10947     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
10948    
10949 };
10950
10951 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
10952      
10953     cols : false,
10954     rows : 5,
10955     readOnly : false,
10956     warp : 'soft',
10957     resize : false,
10958     value: false,
10959     html: false,
10960     
10961     getAutoCreate : function(){
10962         
10963         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10964         
10965         var id = Roo.id();
10966         
10967         var cfg = {};
10968         
10969         if(this.inputType != 'hidden'){
10970             cfg.cls = 'form-group' //input-group
10971         }
10972         
10973         var input =  {
10974             tag: 'textarea',
10975             id : id,
10976             warp : this.warp,
10977             rows : this.rows,
10978             value : this.value || '',
10979             html: this.html || '',
10980             cls : 'form-control',
10981             placeholder : this.placeholder || '' 
10982             
10983         };
10984         
10985         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10986             input.maxLength = this.maxLength;
10987         }
10988         
10989         if(this.resize){
10990             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
10991         }
10992         
10993         if(this.cols){
10994             input.cols = this.cols;
10995         }
10996         
10997         if (this.readOnly) {
10998             input.readonly = true;
10999         }
11000         
11001         if (this.name) {
11002             input.name = this.name;
11003         }
11004         
11005         if (this.size) {
11006             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11007         }
11008         
11009         var settings=this;
11010         ['xs','sm','md','lg'].map(function(size){
11011             if (settings[size]) {
11012                 cfg.cls += ' col-' + size + '-' + settings[size];
11013             }
11014         });
11015         
11016         var inputblock = input;
11017         
11018         if(this.hasFeedback && !this.allowBlank){
11019             
11020             var feedback = {
11021                 tag: 'span',
11022                 cls: 'glyphicon form-control-feedback'
11023             };
11024
11025             inputblock = {
11026                 cls : 'has-feedback',
11027                 cn :  [
11028                     input,
11029                     feedback
11030                 ] 
11031             };  
11032         }
11033         
11034         
11035         if (this.before || this.after) {
11036             
11037             inputblock = {
11038                 cls : 'input-group',
11039                 cn :  [] 
11040             };
11041             if (this.before) {
11042                 inputblock.cn.push({
11043                     tag :'span',
11044                     cls : 'input-group-addon',
11045                     html : this.before
11046                 });
11047             }
11048             
11049             inputblock.cn.push(input);
11050             
11051             if(this.hasFeedback && !this.allowBlank){
11052                 inputblock.cls += ' has-feedback';
11053                 inputblock.cn.push(feedback);
11054             }
11055             
11056             if (this.after) {
11057                 inputblock.cn.push({
11058                     tag :'span',
11059                     cls : 'input-group-addon',
11060                     html : this.after
11061                 });
11062             }
11063             
11064         }
11065         
11066         if (align ==='left' && this.fieldLabel.length) {
11067             cfg.cn = [
11068                 {
11069                     tag: 'label',
11070                     'for' :  id,
11071                     cls : 'control-label',
11072                     html : this.fieldLabel
11073                 },
11074                 {
11075                     cls : "",
11076                     cn: [
11077                         inputblock
11078                     ]
11079                 }
11080
11081             ];
11082             
11083             if(this.labelWidth > 12){
11084                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11085             }
11086
11087             if(this.labelWidth < 13 && this.labelmd == 0){
11088                 this.labelmd = this.labelWidth;
11089             }
11090
11091             if(this.labellg > 0){
11092                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11093                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11094             }
11095
11096             if(this.labelmd > 0){
11097                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11098                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11099             }
11100
11101             if(this.labelsm > 0){
11102                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11103                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11104             }
11105
11106             if(this.labelxs > 0){
11107                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11108                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11109             }
11110             
11111         } else if ( this.fieldLabel.length) {
11112             cfg.cn = [
11113
11114                {
11115                    tag: 'label',
11116                    //cls : 'input-group-addon',
11117                    html : this.fieldLabel
11118
11119                },
11120
11121                inputblock
11122
11123            ];
11124
11125         } else {
11126
11127             cfg.cn = [
11128
11129                 inputblock
11130
11131             ];
11132                 
11133         }
11134         
11135         if (this.disabled) {
11136             input.disabled=true;
11137         }
11138         
11139         return cfg;
11140         
11141     },
11142     /**
11143      * return the real textarea element.
11144      */
11145     inputEl: function ()
11146     {
11147         return this.el.select('textarea.form-control',true).first();
11148     },
11149     
11150     /**
11151      * Clear any invalid styles/messages for this field
11152      */
11153     clearInvalid : function()
11154     {
11155         
11156         if(!this.el || this.preventMark){ // not rendered
11157             return;
11158         }
11159         
11160         var label = this.el.select('label', true).first();
11161         var icon = this.el.select('i.fa-star', true).first();
11162         
11163         if(label && icon){
11164             icon.remove();
11165         }
11166         this.el.removeClass( this.validClass);
11167         this.inputEl().removeClass('is-invalid');
11168          
11169         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11170             
11171             var feedback = this.el.select('.form-control-feedback', true).first();
11172             
11173             if(feedback){
11174                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11175             }
11176             
11177         }
11178         
11179         this.fireEvent('valid', this);
11180     },
11181     
11182      /**
11183      * Mark this field as valid
11184      */
11185     markValid : function()
11186     {
11187         if(!this.el  || this.preventMark){ // not rendered
11188             return;
11189         }
11190         
11191         this.el.removeClass([this.invalidClass, this.validClass]);
11192         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11193         
11194         var feedback = this.el.select('.form-control-feedback', true).first();
11195             
11196         if(feedback){
11197             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11198         }
11199
11200         if(this.disabled || this.allowBlank){
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         if (Roo.bootstrap.version == 3) {
11211             this.el.addClass(this.validClass);
11212         } else {
11213             this.inputEl().addClass('is-valid');
11214         }
11215         
11216         
11217         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11218             
11219             var feedback = this.el.select('.form-control-feedback', true).first();
11220             
11221             if(feedback){
11222                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11223                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11224             }
11225             
11226         }
11227         
11228         this.fireEvent('valid', this);
11229     },
11230     
11231      /**
11232      * Mark this field as invalid
11233      * @param {String} msg The validation message
11234      */
11235     markInvalid : function(msg)
11236     {
11237         if(!this.el  || this.preventMark){ // not rendered
11238             return;
11239         }
11240         
11241         this.el.removeClass([this.invalidClass, this.validClass]);
11242         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11243         
11244         var feedback = this.el.select('.form-control-feedback', true).first();
11245             
11246         if(feedback){
11247             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11248         }
11249
11250         if(this.disabled || this.allowBlank){
11251             return;
11252         }
11253         
11254         var label = this.el.select('label', true).first();
11255         var icon = this.el.select('i.fa-star', true).first();
11256         
11257         if(!this.getValue().length && label && !icon){
11258             this.el.createChild({
11259                 tag : 'i',
11260                 cls : 'text-danger fa fa-lg fa-star',
11261                 tooltip : 'This field is required',
11262                 style : 'margin-right:5px;'
11263             }, label, true);
11264         }
11265         
11266         if (Roo.bootstrap.version == 3) {
11267             this.el.addClass(this.invalidClass);
11268         } else {
11269             this.inputEl().addClass('is-invalid');
11270         }
11271         
11272         // fixme ... this may be depricated need to test..
11273         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11274             
11275             var feedback = this.el.select('.form-control-feedback', true).first();
11276             
11277             if(feedback){
11278                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11279                 
11280                 if(this.getValue().length || this.forceFeedback){
11281                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11282                 }
11283                 
11284             }
11285             
11286         }
11287         
11288         this.fireEvent('invalid', this, msg);
11289     }
11290 });
11291
11292  
11293 /*
11294  * - LGPL
11295  *
11296  * trigger field - base class for combo..
11297  * 
11298  */
11299  
11300 /**
11301  * @class Roo.bootstrap.TriggerField
11302  * @extends Roo.bootstrap.Input
11303  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11304  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11305  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11306  * for which you can provide a custom implementation.  For example:
11307  * <pre><code>
11308 var trigger = new Roo.bootstrap.TriggerField();
11309 trigger.onTriggerClick = myTriggerFn;
11310 trigger.applyTo('my-field');
11311 </code></pre>
11312  *
11313  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11314  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11315  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
11316  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11317  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11318
11319  * @constructor
11320  * Create a new TriggerField.
11321  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11322  * to the base TextField)
11323  */
11324 Roo.bootstrap.TriggerField = function(config){
11325     this.mimicing = false;
11326     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11327 };
11328
11329 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
11330     /**
11331      * @cfg {String} triggerClass A CSS class to apply to the trigger
11332      */
11333      /**
11334      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11335      */
11336     hideTrigger:false,
11337
11338     /**
11339      * @cfg {Boolean} removable (true|false) special filter default false
11340      */
11341     removable : false,
11342     
11343     /** @cfg {Boolean} grow @hide */
11344     /** @cfg {Number} growMin @hide */
11345     /** @cfg {Number} growMax @hide */
11346
11347     /**
11348      * @hide 
11349      * @method
11350      */
11351     autoSize: Roo.emptyFn,
11352     // private
11353     monitorTab : true,
11354     // private
11355     deferHeight : true,
11356
11357     
11358     actionMode : 'wrap',
11359     
11360     caret : false,
11361     
11362     
11363     getAutoCreate : function(){
11364        
11365         var align = this.labelAlign || this.parentLabelAlign();
11366         
11367         var id = Roo.id();
11368         
11369         var cfg = {
11370             cls: 'form-group' //input-group
11371         };
11372         
11373         
11374         var input =  {
11375             tag: 'input',
11376             id : id,
11377             type : this.inputType,
11378             cls : 'form-control',
11379             autocomplete: 'new-password',
11380             placeholder : this.placeholder || '' 
11381             
11382         };
11383         if (this.name) {
11384             input.name = this.name;
11385         }
11386         if (this.size) {
11387             input.cls += ' input-' + this.size;
11388         }
11389         
11390         if (this.disabled) {
11391             input.disabled=true;
11392         }
11393         
11394         var inputblock = input;
11395         
11396         if(this.hasFeedback && !this.allowBlank){
11397             
11398             var feedback = {
11399                 tag: 'span',
11400                 cls: 'glyphicon form-control-feedback'
11401             };
11402             
11403             if(this.removable && !this.editable && !this.tickable){
11404                 inputblock = {
11405                     cls : 'has-feedback',
11406                     cn :  [
11407                         inputblock,
11408                         {
11409                             tag: 'button',
11410                             html : 'x',
11411                             cls : 'roo-combo-removable-btn close'
11412                         },
11413                         feedback
11414                     ] 
11415                 };
11416             } else {
11417                 inputblock = {
11418                     cls : 'has-feedback',
11419                     cn :  [
11420                         inputblock,
11421                         feedback
11422                     ] 
11423                 };
11424             }
11425
11426         } else {
11427             if(this.removable && !this.editable && !this.tickable){
11428                 inputblock = {
11429                     cls : 'roo-removable',
11430                     cn :  [
11431                         inputblock,
11432                         {
11433                             tag: 'button',
11434                             html : 'x',
11435                             cls : 'roo-combo-removable-btn close'
11436                         }
11437                     ] 
11438                 };
11439             }
11440         }
11441         
11442         if (this.before || this.after) {
11443             
11444             inputblock = {
11445                 cls : 'input-group',
11446                 cn :  [] 
11447             };
11448             if (this.before) {
11449                 inputblock.cn.push({
11450                     tag :'span',
11451                     cls : 'input-group-addon input-group-prepend input-group-text',
11452                     html : this.before
11453                 });
11454             }
11455             
11456             inputblock.cn.push(input);
11457             
11458             if(this.hasFeedback && !this.allowBlank){
11459                 inputblock.cls += ' has-feedback';
11460                 inputblock.cn.push(feedback);
11461             }
11462             
11463             if (this.after) {
11464                 inputblock.cn.push({
11465                     tag :'span',
11466                     cls : 'input-group-addon input-group-append input-group-text',
11467                     html : this.after
11468                 });
11469             }
11470             
11471         };
11472         
11473       
11474         
11475         var ibwrap = inputblock;
11476         
11477         if(this.multiple){
11478             ibwrap = {
11479                 tag: 'ul',
11480                 cls: 'roo-select2-choices',
11481                 cn:[
11482                     {
11483                         tag: 'li',
11484                         cls: 'roo-select2-search-field',
11485                         cn: [
11486
11487                             inputblock
11488                         ]
11489                     }
11490                 ]
11491             };
11492                 
11493         }
11494         
11495         var combobox = {
11496             cls: 'roo-select2-container input-group',
11497             cn: [
11498                  {
11499                     tag: 'input',
11500                     type : 'hidden',
11501                     cls: 'form-hidden-field'
11502                 },
11503                 ibwrap
11504             ]
11505         };
11506         
11507         if(!this.multiple && this.showToggleBtn){
11508             
11509             var caret = {
11510                         tag: 'span',
11511                         cls: 'caret'
11512              };
11513             if (this.caret != false) {
11514                 caret = {
11515                      tag: 'i',
11516                      cls: 'fa fa-' + this.caret
11517                 };
11518                 
11519             }
11520             
11521             combobox.cn.push({
11522                 tag :'span',
11523                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
11524                 cn : [
11525                     Roo.bootstrap.version == 3 ? caret : '',
11526                     {
11527                         tag: 'span',
11528                         cls: 'combobox-clear',
11529                         cn  : [
11530                             {
11531                                 tag : 'i',
11532                                 cls: 'icon-remove'
11533                             }
11534                         ]
11535                     }
11536                 ]
11537
11538             })
11539         }
11540         
11541         if(this.multiple){
11542             combobox.cls += ' roo-select2-container-multi';
11543         }
11544          var indicator = {
11545             tag : 'i',
11546             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11547             tooltip : 'This field is required'
11548         };
11549         if (Roo.bootstrap.version == 4) {
11550             indicator = {
11551                 tag : 'i',
11552                 style : 'display:none'
11553             };
11554         }
11555         
11556         
11557         if (align ==='left' && this.fieldLabel.length) {
11558             
11559             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
11560
11561             cfg.cn = [
11562                 indicator,
11563                 {
11564                     tag: 'label',
11565                     'for' :  id,
11566                     cls : 'control-label',
11567                     html : this.fieldLabel
11568
11569                 },
11570                 {
11571                     cls : "", 
11572                     cn: [
11573                         combobox
11574                     ]
11575                 }
11576
11577             ];
11578             
11579             var labelCfg = cfg.cn[1];
11580             var contentCfg = cfg.cn[2];
11581             
11582             if(this.indicatorpos == 'right'){
11583                 cfg.cn = [
11584                     {
11585                         tag: 'label',
11586                         'for' :  id,
11587                         cls : 'control-label',
11588                         cn : [
11589                             {
11590                                 tag : 'span',
11591                                 html : this.fieldLabel
11592                             },
11593                             indicator
11594                         ]
11595                     },
11596                     {
11597                         cls : "", 
11598                         cn: [
11599                             combobox
11600                         ]
11601                     }
11602
11603                 ];
11604                 
11605                 labelCfg = cfg.cn[0];
11606                 contentCfg = cfg.cn[1];
11607             }
11608             
11609             if(this.labelWidth > 12){
11610                 labelCfg.style = "width: " + this.labelWidth + 'px';
11611             }
11612             
11613             if(this.labelWidth < 13 && this.labelmd == 0){
11614                 this.labelmd = this.labelWidth;
11615             }
11616             
11617             if(this.labellg > 0){
11618                 labelCfg.cls += ' col-lg-' + this.labellg;
11619                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11620             }
11621             
11622             if(this.labelmd > 0){
11623                 labelCfg.cls += ' col-md-' + this.labelmd;
11624                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11625             }
11626             
11627             if(this.labelsm > 0){
11628                 labelCfg.cls += ' col-sm-' + this.labelsm;
11629                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11630             }
11631             
11632             if(this.labelxs > 0){
11633                 labelCfg.cls += ' col-xs-' + this.labelxs;
11634                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11635             }
11636             
11637         } else if ( this.fieldLabel.length) {
11638 //                Roo.log(" label");
11639             cfg.cn = [
11640                 indicator,
11641                {
11642                    tag: 'label',
11643                    //cls : 'input-group-addon',
11644                    html : this.fieldLabel
11645
11646                },
11647
11648                combobox
11649
11650             ];
11651             
11652             if(this.indicatorpos == 'right'){
11653                 
11654                 cfg.cn = [
11655                     {
11656                        tag: 'label',
11657                        cn : [
11658                            {
11659                                tag : 'span',
11660                                html : this.fieldLabel
11661                            },
11662                            indicator
11663                        ]
11664
11665                     },
11666                     combobox
11667
11668                 ];
11669
11670             }
11671
11672         } else {
11673             
11674 //                Roo.log(" no label && no align");
11675                 cfg = combobox
11676                      
11677                 
11678         }
11679         
11680         var settings=this;
11681         ['xs','sm','md','lg'].map(function(size){
11682             if (settings[size]) {
11683                 cfg.cls += ' col-' + size + '-' + settings[size];
11684             }
11685         });
11686         
11687         return cfg;
11688         
11689     },
11690     
11691     
11692     
11693     // private
11694     onResize : function(w, h){
11695 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
11696 //        if(typeof w == 'number'){
11697 //            var x = w - this.trigger.getWidth();
11698 //            this.inputEl().setWidth(this.adjustWidth('input', x));
11699 //            this.trigger.setStyle('left', x+'px');
11700 //        }
11701     },
11702
11703     // private
11704     adjustSize : Roo.BoxComponent.prototype.adjustSize,
11705
11706     // private
11707     getResizeEl : function(){
11708         return this.inputEl();
11709     },
11710
11711     // private
11712     getPositionEl : function(){
11713         return this.inputEl();
11714     },
11715
11716     // private
11717     alignErrorIcon : function(){
11718         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
11719     },
11720
11721     // private
11722     initEvents : function(){
11723         
11724         this.createList();
11725         
11726         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
11727         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
11728         if(!this.multiple && this.showToggleBtn){
11729             this.trigger = this.el.select('span.dropdown-toggle',true).first();
11730             if(this.hideTrigger){
11731                 this.trigger.setDisplayed(false);
11732             }
11733             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
11734         }
11735         
11736         if(this.multiple){
11737             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
11738         }
11739         
11740         if(this.removable && !this.editable && !this.tickable){
11741             var close = this.closeTriggerEl();
11742             
11743             if(close){
11744                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
11745                 close.on('click', this.removeBtnClick, this, close);
11746             }
11747         }
11748         
11749         //this.trigger.addClassOnOver('x-form-trigger-over');
11750         //this.trigger.addClassOnClick('x-form-trigger-click');
11751         
11752         //if(!this.width){
11753         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
11754         //}
11755     },
11756     
11757     closeTriggerEl : function()
11758     {
11759         var close = this.el.select('.roo-combo-removable-btn', true).first();
11760         return close ? close : false;
11761     },
11762     
11763     removeBtnClick : function(e, h, el)
11764     {
11765         e.preventDefault();
11766         
11767         if(this.fireEvent("remove", this) !== false){
11768             this.reset();
11769             this.fireEvent("afterremove", this)
11770         }
11771     },
11772     
11773     createList : function()
11774     {
11775         this.list = Roo.get(document.body).createChild({
11776             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
11777             cls: 'typeahead typeahead-long dropdown-menu',
11778             style: 'display:none'
11779         });
11780         
11781         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
11782         
11783     },
11784
11785     // private
11786     initTrigger : function(){
11787        
11788     },
11789
11790     // private
11791     onDestroy : function(){
11792         if(this.trigger){
11793             this.trigger.removeAllListeners();
11794           //  this.trigger.remove();
11795         }
11796         //if(this.wrap){
11797         //    this.wrap.remove();
11798         //}
11799         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
11800     },
11801
11802     // private
11803     onFocus : function(){
11804         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
11805         /*
11806         if(!this.mimicing){
11807             this.wrap.addClass('x-trigger-wrap-focus');
11808             this.mimicing = true;
11809             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
11810             if(this.monitorTab){
11811                 this.el.on("keydown", this.checkTab, this);
11812             }
11813         }
11814         */
11815     },
11816
11817     // private
11818     checkTab : function(e){
11819         if(e.getKey() == e.TAB){
11820             this.triggerBlur();
11821         }
11822     },
11823
11824     // private
11825     onBlur : function(){
11826         // do nothing
11827     },
11828
11829     // private
11830     mimicBlur : function(e, t){
11831         /*
11832         if(!this.wrap.contains(t) && this.validateBlur()){
11833             this.triggerBlur();
11834         }
11835         */
11836     },
11837
11838     // private
11839     triggerBlur : function(){
11840         this.mimicing = false;
11841         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
11842         if(this.monitorTab){
11843             this.el.un("keydown", this.checkTab, this);
11844         }
11845         //this.wrap.removeClass('x-trigger-wrap-focus');
11846         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
11847     },
11848
11849     // private
11850     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
11851     validateBlur : function(e, t){
11852         return true;
11853     },
11854
11855     // private
11856     onDisable : function(){
11857         this.inputEl().dom.disabled = true;
11858         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
11859         //if(this.wrap){
11860         //    this.wrap.addClass('x-item-disabled');
11861         //}
11862     },
11863
11864     // private
11865     onEnable : function(){
11866         this.inputEl().dom.disabled = false;
11867         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
11868         //if(this.wrap){
11869         //    this.el.removeClass('x-item-disabled');
11870         //}
11871     },
11872
11873     // private
11874     onShow : function(){
11875         var ae = this.getActionEl();
11876         
11877         if(ae){
11878             ae.dom.style.display = '';
11879             ae.dom.style.visibility = 'visible';
11880         }
11881     },
11882
11883     // private
11884     
11885     onHide : function(){
11886         var ae = this.getActionEl();
11887         ae.dom.style.display = 'none';
11888     },
11889
11890     /**
11891      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
11892      * by an implementing function.
11893      * @method
11894      * @param {EventObject} e
11895      */
11896     onTriggerClick : Roo.emptyFn
11897 });
11898  /*
11899  * Based on:
11900  * Ext JS Library 1.1.1
11901  * Copyright(c) 2006-2007, Ext JS, LLC.
11902  *
11903  * Originally Released Under LGPL - original licence link has changed is not relivant.
11904  *
11905  * Fork - LGPL
11906  * <script type="text/javascript">
11907  */
11908
11909
11910 /**
11911  * @class Roo.data.SortTypes
11912  * @singleton
11913  * Defines the default sorting (casting?) comparison functions used when sorting data.
11914  */
11915 Roo.data.SortTypes = {
11916     /**
11917      * Default sort that does nothing
11918      * @param {Mixed} s The value being converted
11919      * @return {Mixed} The comparison value
11920      */
11921     none : function(s){
11922         return s;
11923     },
11924     
11925     /**
11926      * The regular expression used to strip tags
11927      * @type {RegExp}
11928      * @property
11929      */
11930     stripTagsRE : /<\/?[^>]+>/gi,
11931     
11932     /**
11933      * Strips all HTML tags to sort on text only
11934      * @param {Mixed} s The value being converted
11935      * @return {String} The comparison value
11936      */
11937     asText : function(s){
11938         return String(s).replace(this.stripTagsRE, "");
11939     },
11940     
11941     /**
11942      * Strips all HTML tags to sort on text only - Case insensitive
11943      * @param {Mixed} s The value being converted
11944      * @return {String} The comparison value
11945      */
11946     asUCText : function(s){
11947         return String(s).toUpperCase().replace(this.stripTagsRE, "");
11948     },
11949     
11950     /**
11951      * Case insensitive string
11952      * @param {Mixed} s The value being converted
11953      * @return {String} The comparison value
11954      */
11955     asUCString : function(s) {
11956         return String(s).toUpperCase();
11957     },
11958     
11959     /**
11960      * Date sorting
11961      * @param {Mixed} s The value being converted
11962      * @return {Number} The comparison value
11963      */
11964     asDate : function(s) {
11965         if(!s){
11966             return 0;
11967         }
11968         if(s instanceof Date){
11969             return s.getTime();
11970         }
11971         return Date.parse(String(s));
11972     },
11973     
11974     /**
11975      * Float sorting
11976      * @param {Mixed} s The value being converted
11977      * @return {Float} The comparison value
11978      */
11979     asFloat : function(s) {
11980         var val = parseFloat(String(s).replace(/,/g, ""));
11981         if(isNaN(val)) {
11982             val = 0;
11983         }
11984         return val;
11985     },
11986     
11987     /**
11988      * Integer sorting
11989      * @param {Mixed} s The value being converted
11990      * @return {Number} The comparison value
11991      */
11992     asInt : function(s) {
11993         var val = parseInt(String(s).replace(/,/g, ""));
11994         if(isNaN(val)) {
11995             val = 0;
11996         }
11997         return val;
11998     }
11999 };/*
12000  * Based on:
12001  * Ext JS Library 1.1.1
12002  * Copyright(c) 2006-2007, Ext JS, LLC.
12003  *
12004  * Originally Released Under LGPL - original licence link has changed is not relivant.
12005  *
12006  * Fork - LGPL
12007  * <script type="text/javascript">
12008  */
12009
12010 /**
12011 * @class Roo.data.Record
12012  * Instances of this class encapsulate both record <em>definition</em> information, and record
12013  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12014  * to access Records cached in an {@link Roo.data.Store} object.<br>
12015  * <p>
12016  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12017  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12018  * objects.<br>
12019  * <p>
12020  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12021  * @constructor
12022  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12023  * {@link #create}. The parameters are the same.
12024  * @param {Array} data An associative Array of data values keyed by the field name.
12025  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12026  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12027  * not specified an integer id is generated.
12028  */
12029 Roo.data.Record = function(data, id){
12030     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12031     this.data = data;
12032 };
12033
12034 /**
12035  * Generate a constructor for a specific record layout.
12036  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12037  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12038  * Each field definition object may contain the following properties: <ul>
12039  * <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,
12040  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12041  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12042  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12043  * is being used, then this is a string containing the javascript expression to reference the data relative to 
12044  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12045  * to the data item relative to the record element. If the mapping expression is the same as the field name,
12046  * this may be omitted.</p></li>
12047  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12048  * <ul><li>auto (Default, implies no conversion)</li>
12049  * <li>string</li>
12050  * <li>int</li>
12051  * <li>float</li>
12052  * <li>boolean</li>
12053  * <li>date</li></ul></p></li>
12054  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12055  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12056  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12057  * by the Reader into an object that will be stored in the Record. It is passed the
12058  * following parameters:<ul>
12059  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12060  * </ul></p></li>
12061  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12062  * </ul>
12063  * <br>usage:<br><pre><code>
12064 var TopicRecord = Roo.data.Record.create(
12065     {name: 'title', mapping: 'topic_title'},
12066     {name: 'author', mapping: 'username'},
12067     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12068     {name: 'lastPost', mapping: 'post_time', type: 'date'},
12069     {name: 'lastPoster', mapping: 'user2'},
12070     {name: 'excerpt', mapping: 'post_text'}
12071 );
12072
12073 var myNewRecord = new TopicRecord({
12074     title: 'Do my job please',
12075     author: 'noobie',
12076     totalPosts: 1,
12077     lastPost: new Date(),
12078     lastPoster: 'Animal',
12079     excerpt: 'No way dude!'
12080 });
12081 myStore.add(myNewRecord);
12082 </code></pre>
12083  * @method create
12084  * @static
12085  */
12086 Roo.data.Record.create = function(o){
12087     var f = function(){
12088         f.superclass.constructor.apply(this, arguments);
12089     };
12090     Roo.extend(f, Roo.data.Record);
12091     var p = f.prototype;
12092     p.fields = new Roo.util.MixedCollection(false, function(field){
12093         return field.name;
12094     });
12095     for(var i = 0, len = o.length; i < len; i++){
12096         p.fields.add(new Roo.data.Field(o[i]));
12097     }
12098     f.getField = function(name){
12099         return p.fields.get(name);  
12100     };
12101     return f;
12102 };
12103
12104 Roo.data.Record.AUTO_ID = 1000;
12105 Roo.data.Record.EDIT = 'edit';
12106 Roo.data.Record.REJECT = 'reject';
12107 Roo.data.Record.COMMIT = 'commit';
12108
12109 Roo.data.Record.prototype = {
12110     /**
12111      * Readonly flag - true if this record has been modified.
12112      * @type Boolean
12113      */
12114     dirty : false,
12115     editing : false,
12116     error: null,
12117     modified: null,
12118
12119     // private
12120     join : function(store){
12121         this.store = store;
12122     },
12123
12124     /**
12125      * Set the named field to the specified value.
12126      * @param {String} name The name of the field to set.
12127      * @param {Object} value The value to set the field to.
12128      */
12129     set : function(name, value){
12130         if(this.data[name] == value){
12131             return;
12132         }
12133         this.dirty = true;
12134         if(!this.modified){
12135             this.modified = {};
12136         }
12137         if(typeof this.modified[name] == 'undefined'){
12138             this.modified[name] = this.data[name];
12139         }
12140         this.data[name] = value;
12141         if(!this.editing && this.store){
12142             this.store.afterEdit(this);
12143         }       
12144     },
12145
12146     /**
12147      * Get the value of the named field.
12148      * @param {String} name The name of the field to get the value of.
12149      * @return {Object} The value of the field.
12150      */
12151     get : function(name){
12152         return this.data[name]; 
12153     },
12154
12155     // private
12156     beginEdit : function(){
12157         this.editing = true;
12158         this.modified = {}; 
12159     },
12160
12161     // private
12162     cancelEdit : function(){
12163         this.editing = false;
12164         delete this.modified;
12165     },
12166
12167     // private
12168     endEdit : function(){
12169         this.editing = false;
12170         if(this.dirty && this.store){
12171             this.store.afterEdit(this);
12172         }
12173     },
12174
12175     /**
12176      * Usually called by the {@link Roo.data.Store} which owns the Record.
12177      * Rejects all changes made to the Record since either creation, or the last commit operation.
12178      * Modified fields are reverted to their original values.
12179      * <p>
12180      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
12181      * of reject operations.
12182      */
12183     reject : function(){
12184         var m = this.modified;
12185         for(var n in m){
12186             if(typeof m[n] != "function"){
12187                 this.data[n] = m[n];
12188             }
12189         }
12190         this.dirty = false;
12191         delete this.modified;
12192         this.editing = false;
12193         if(this.store){
12194             this.store.afterReject(this);
12195         }
12196     },
12197
12198     /**
12199      * Usually called by the {@link Roo.data.Store} which owns the Record.
12200      * Commits all changes made to the Record since either creation, or the last commit operation.
12201      * <p>
12202      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
12203      * of commit operations.
12204      */
12205     commit : function(){
12206         this.dirty = false;
12207         delete this.modified;
12208         this.editing = false;
12209         if(this.store){
12210             this.store.afterCommit(this);
12211         }
12212     },
12213
12214     // private
12215     hasError : function(){
12216         return this.error != null;
12217     },
12218
12219     // private
12220     clearError : function(){
12221         this.error = null;
12222     },
12223
12224     /**
12225      * Creates a copy of this record.
12226      * @param {String} id (optional) A new record id if you don't want to use this record's id
12227      * @return {Record}
12228      */
12229     copy : function(newId) {
12230         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
12231     }
12232 };/*
12233  * Based on:
12234  * Ext JS Library 1.1.1
12235  * Copyright(c) 2006-2007, Ext JS, LLC.
12236  *
12237  * Originally Released Under LGPL - original licence link has changed is not relivant.
12238  *
12239  * Fork - LGPL
12240  * <script type="text/javascript">
12241  */
12242
12243
12244
12245 /**
12246  * @class Roo.data.Store
12247  * @extends Roo.util.Observable
12248  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
12249  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
12250  * <p>
12251  * 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
12252  * has no knowledge of the format of the data returned by the Proxy.<br>
12253  * <p>
12254  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
12255  * instances from the data object. These records are cached and made available through accessor functions.
12256  * @constructor
12257  * Creates a new Store.
12258  * @param {Object} config A config object containing the objects needed for the Store to access data,
12259  * and read the data into Records.
12260  */
12261 Roo.data.Store = function(config){
12262     this.data = new Roo.util.MixedCollection(false);
12263     this.data.getKey = function(o){
12264         return o.id;
12265     };
12266     this.baseParams = {};
12267     // private
12268     this.paramNames = {
12269         "start" : "start",
12270         "limit" : "limit",
12271         "sort" : "sort",
12272         "dir" : "dir",
12273         "multisort" : "_multisort"
12274     };
12275
12276     if(config && config.data){
12277         this.inlineData = config.data;
12278         delete config.data;
12279     }
12280
12281     Roo.apply(this, config);
12282     
12283     if(this.reader){ // reader passed
12284         this.reader = Roo.factory(this.reader, Roo.data);
12285         this.reader.xmodule = this.xmodule || false;
12286         if(!this.recordType){
12287             this.recordType = this.reader.recordType;
12288         }
12289         if(this.reader.onMetaChange){
12290             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
12291         }
12292     }
12293
12294     if(this.recordType){
12295         this.fields = this.recordType.prototype.fields;
12296     }
12297     this.modified = [];
12298
12299     this.addEvents({
12300         /**
12301          * @event datachanged
12302          * Fires when the data cache has changed, and a widget which is using this Store
12303          * as a Record cache should refresh its view.
12304          * @param {Store} this
12305          */
12306         datachanged : true,
12307         /**
12308          * @event metachange
12309          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
12310          * @param {Store} this
12311          * @param {Object} meta The JSON metadata
12312          */
12313         metachange : true,
12314         /**
12315          * @event add
12316          * Fires when Records have been added to the Store
12317          * @param {Store} this
12318          * @param {Roo.data.Record[]} records The array of Records added
12319          * @param {Number} index The index at which the record(s) were added
12320          */
12321         add : true,
12322         /**
12323          * @event remove
12324          * Fires when a Record has been removed from the Store
12325          * @param {Store} this
12326          * @param {Roo.data.Record} record The Record that was removed
12327          * @param {Number} index The index at which the record was removed
12328          */
12329         remove : true,
12330         /**
12331          * @event update
12332          * Fires when a Record has been updated
12333          * @param {Store} this
12334          * @param {Roo.data.Record} record The Record that was updated
12335          * @param {String} operation The update operation being performed.  Value may be one of:
12336          * <pre><code>
12337  Roo.data.Record.EDIT
12338  Roo.data.Record.REJECT
12339  Roo.data.Record.COMMIT
12340          * </code></pre>
12341          */
12342         update : true,
12343         /**
12344          * @event clear
12345          * Fires when the data cache has been cleared.
12346          * @param {Store} this
12347          */
12348         clear : true,
12349         /**
12350          * @event beforeload
12351          * Fires before a request is made for a new data object.  If the beforeload handler returns false
12352          * the load action will be canceled.
12353          * @param {Store} this
12354          * @param {Object} options The loading options that were specified (see {@link #load} for details)
12355          */
12356         beforeload : true,
12357         /**
12358          * @event beforeloadadd
12359          * Fires after a new set of Records has been loaded.
12360          * @param {Store} this
12361          * @param {Roo.data.Record[]} records The Records that were loaded
12362          * @param {Object} options The loading options that were specified (see {@link #load} for details)
12363          */
12364         beforeloadadd : true,
12365         /**
12366          * @event load
12367          * Fires after a new set of Records has been loaded, before they are added to the store.
12368          * @param {Store} this
12369          * @param {Roo.data.Record[]} records The Records that were loaded
12370          * @param {Object} options The loading options that were specified (see {@link #load} for details)
12371          * @params {Object} return from reader
12372          */
12373         load : true,
12374         /**
12375          * @event loadexception
12376          * Fires if an exception occurs in the Proxy during loading.
12377          * Called with the signature of the Proxy's "loadexception" event.
12378          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
12379          * 
12380          * @param {Proxy} 
12381          * @param {Object} return from JsonData.reader() - success, totalRecords, records
12382          * @param {Object} load options 
12383          * @param {Object} jsonData from your request (normally this contains the Exception)
12384          */
12385         loadexception : true
12386     });
12387     
12388     if(this.proxy){
12389         this.proxy = Roo.factory(this.proxy, Roo.data);
12390         this.proxy.xmodule = this.xmodule || false;
12391         this.relayEvents(this.proxy,  ["loadexception"]);
12392     }
12393     this.sortToggle = {};
12394     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
12395
12396     Roo.data.Store.superclass.constructor.call(this);
12397
12398     if(this.inlineData){
12399         this.loadData(this.inlineData);
12400         delete this.inlineData;
12401     }
12402 };
12403
12404 Roo.extend(Roo.data.Store, Roo.util.Observable, {
12405      /**
12406     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
12407     * without a remote query - used by combo/forms at present.
12408     */
12409     
12410     /**
12411     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
12412     */
12413     /**
12414     * @cfg {Array} data Inline data to be loaded when the store is initialized.
12415     */
12416     /**
12417     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
12418     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
12419     */
12420     /**
12421     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
12422     * on any HTTP request
12423     */
12424     /**
12425     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
12426     */
12427     /**
12428     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
12429     */
12430     multiSort: false,
12431     /**
12432     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
12433     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
12434     */
12435     remoteSort : false,
12436
12437     /**
12438     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
12439      * loaded or when a record is removed. (defaults to false).
12440     */
12441     pruneModifiedRecords : false,
12442
12443     // private
12444     lastOptions : null,
12445
12446     /**
12447      * Add Records to the Store and fires the add event.
12448      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
12449      */
12450     add : function(records){
12451         records = [].concat(records);
12452         for(var i = 0, len = records.length; i < len; i++){
12453             records[i].join(this);
12454         }
12455         var index = this.data.length;
12456         this.data.addAll(records);
12457         this.fireEvent("add", this, records, index);
12458     },
12459
12460     /**
12461      * Remove a Record from the Store and fires the remove event.
12462      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
12463      */
12464     remove : function(record){
12465         var index = this.data.indexOf(record);
12466         this.data.removeAt(index);
12467  
12468         if(this.pruneModifiedRecords){
12469             this.modified.remove(record);
12470         }
12471         this.fireEvent("remove", this, record, index);
12472     },
12473
12474     /**
12475      * Remove all Records from the Store and fires the clear event.
12476      */
12477     removeAll : function(){
12478         this.data.clear();
12479         if(this.pruneModifiedRecords){
12480             this.modified = [];
12481         }
12482         this.fireEvent("clear", this);
12483     },
12484
12485     /**
12486      * Inserts Records to the Store at the given index and fires the add event.
12487      * @param {Number} index The start index at which to insert the passed Records.
12488      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
12489      */
12490     insert : function(index, records){
12491         records = [].concat(records);
12492         for(var i = 0, len = records.length; i < len; i++){
12493             this.data.insert(index, records[i]);
12494             records[i].join(this);
12495         }
12496         this.fireEvent("add", this, records, index);
12497     },
12498
12499     /**
12500      * Get the index within the cache of the passed Record.
12501      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
12502      * @return {Number} The index of the passed Record. Returns -1 if not found.
12503      */
12504     indexOf : function(record){
12505         return this.data.indexOf(record);
12506     },
12507
12508     /**
12509      * Get the index within the cache of the Record with the passed id.
12510      * @param {String} id The id of the Record to find.
12511      * @return {Number} The index of the Record. Returns -1 if not found.
12512      */
12513     indexOfId : function(id){
12514         return this.data.indexOfKey(id);
12515     },
12516
12517     /**
12518      * Get the Record with the specified id.
12519      * @param {String} id The id of the Record to find.
12520      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
12521      */
12522     getById : function(id){
12523         return this.data.key(id);
12524     },
12525
12526     /**
12527      * Get the Record at the specified index.
12528      * @param {Number} index The index of the Record to find.
12529      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
12530      */
12531     getAt : function(index){
12532         return this.data.itemAt(index);
12533     },
12534
12535     /**
12536      * Returns a range of Records between specified indices.
12537      * @param {Number} startIndex (optional) The starting index (defaults to 0)
12538      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
12539      * @return {Roo.data.Record[]} An array of Records
12540      */
12541     getRange : function(start, end){
12542         return this.data.getRange(start, end);
12543     },
12544
12545     // private
12546     storeOptions : function(o){
12547         o = Roo.apply({}, o);
12548         delete o.callback;
12549         delete o.scope;
12550         this.lastOptions = o;
12551     },
12552
12553     /**
12554      * Loads the Record cache from the configured Proxy using the configured Reader.
12555      * <p>
12556      * If using remote paging, then the first load call must specify the <em>start</em>
12557      * and <em>limit</em> properties in the options.params property to establish the initial
12558      * position within the dataset, and the number of Records to cache on each read from the Proxy.
12559      * <p>
12560      * <strong>It is important to note that for remote data sources, loading is asynchronous,
12561      * and this call will return before the new data has been loaded. Perform any post-processing
12562      * in a callback function, or in a "load" event handler.</strong>
12563      * <p>
12564      * @param {Object} options An object containing properties which control loading options:<ul>
12565      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
12566      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
12567      * passed the following arguments:<ul>
12568      * <li>r : Roo.data.Record[]</li>
12569      * <li>options: Options object from the load call</li>
12570      * <li>success: Boolean success indicator</li></ul></li>
12571      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
12572      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
12573      * </ul>
12574      */
12575     load : function(options){
12576         options = options || {};
12577         if(this.fireEvent("beforeload", this, options) !== false){
12578             this.storeOptions(options);
12579             var p = Roo.apply(options.params || {}, this.baseParams);
12580             // if meta was not loaded from remote source.. try requesting it.
12581             if (!this.reader.metaFromRemote) {
12582                 p._requestMeta = 1;
12583             }
12584             if(this.sortInfo && this.remoteSort){
12585                 var pn = this.paramNames;
12586                 p[pn["sort"]] = this.sortInfo.field;
12587                 p[pn["dir"]] = this.sortInfo.direction;
12588             }
12589             if (this.multiSort) {
12590                 var pn = this.paramNames;
12591                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
12592             }
12593             
12594             this.proxy.load(p, this.reader, this.loadRecords, this, options);
12595         }
12596     },
12597
12598     /**
12599      * Reloads the Record cache from the configured Proxy using the configured Reader and
12600      * the options from the last load operation performed.
12601      * @param {Object} options (optional) An object containing properties which may override the options
12602      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
12603      * the most recently used options are reused).
12604      */
12605     reload : function(options){
12606         this.load(Roo.applyIf(options||{}, this.lastOptions));
12607     },
12608
12609     // private
12610     // Called as a callback by the Reader during a load operation.
12611     loadRecords : function(o, options, success){
12612         if(!o || success === false){
12613             if(success !== false){
12614                 this.fireEvent("load", this, [], options, o);
12615             }
12616             if(options.callback){
12617                 options.callback.call(options.scope || this, [], options, false);
12618             }
12619             return;
12620         }
12621         // if data returned failure - throw an exception.
12622         if (o.success === false) {
12623             // show a message if no listener is registered.
12624             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
12625                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
12626             }
12627             // loadmask wil be hooked into this..
12628             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
12629             return;
12630         }
12631         var r = o.records, t = o.totalRecords || r.length;
12632         
12633         this.fireEvent("beforeloadadd", this, r, options, o);
12634         
12635         if(!options || options.add !== true){
12636             if(this.pruneModifiedRecords){
12637                 this.modified = [];
12638             }
12639             for(var i = 0, len = r.length; i < len; i++){
12640                 r[i].join(this);
12641             }
12642             if(this.snapshot){
12643                 this.data = this.snapshot;
12644                 delete this.snapshot;
12645             }
12646             this.data.clear();
12647             this.data.addAll(r);
12648             this.totalLength = t;
12649             this.applySort();
12650             this.fireEvent("datachanged", this);
12651         }else{
12652             this.totalLength = Math.max(t, this.data.length+r.length);
12653             this.add(r);
12654         }
12655         
12656         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
12657                 
12658             var e = new Roo.data.Record({});
12659
12660             e.set(this.parent.displayField, this.parent.emptyTitle);
12661             e.set(this.parent.valueField, '');
12662
12663             this.insert(0, e);
12664         }
12665             
12666         this.fireEvent("load", this, r, options, o);
12667         if(options.callback){
12668             options.callback.call(options.scope || this, r, options, true);
12669         }
12670     },
12671
12672
12673     /**
12674      * Loads data from a passed data block. A Reader which understands the format of the data
12675      * must have been configured in the constructor.
12676      * @param {Object} data The data block from which to read the Records.  The format of the data expected
12677      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
12678      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
12679      */
12680     loadData : function(o, append){
12681         var r = this.reader.readRecords(o);
12682         this.loadRecords(r, {add: append}, true);
12683     },
12684     
12685      /**
12686      * using 'cn' the nested child reader read the child array into it's child stores.
12687      * @param {Object} rec The record with a 'children array
12688      */
12689     loadDataFromChildren : function(rec)
12690     {
12691         this.loadData(this.reader.toLoadData(rec));
12692     },
12693     
12694
12695     /**
12696      * Gets the number of cached records.
12697      * <p>
12698      * <em>If using paging, this may not be the total size of the dataset. If the data object
12699      * used by the Reader contains the dataset size, then the getTotalCount() function returns
12700      * the data set size</em>
12701      */
12702     getCount : function(){
12703         return this.data.length || 0;
12704     },
12705
12706     /**
12707      * Gets the total number of records in the dataset as returned by the server.
12708      * <p>
12709      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
12710      * the dataset size</em>
12711      */
12712     getTotalCount : function(){
12713         return this.totalLength || 0;
12714     },
12715
12716     /**
12717      * Returns the sort state of the Store as an object with two properties:
12718      * <pre><code>
12719  field {String} The name of the field by which the Records are sorted
12720  direction {String} The sort order, "ASC" or "DESC"
12721      * </code></pre>
12722      */
12723     getSortState : function(){
12724         return this.sortInfo;
12725     },
12726
12727     // private
12728     applySort : function(){
12729         if(this.sortInfo && !this.remoteSort){
12730             var s = this.sortInfo, f = s.field;
12731             var st = this.fields.get(f).sortType;
12732             var fn = function(r1, r2){
12733                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
12734                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
12735             };
12736             this.data.sort(s.direction, fn);
12737             if(this.snapshot && this.snapshot != this.data){
12738                 this.snapshot.sort(s.direction, fn);
12739             }
12740         }
12741     },
12742
12743     /**
12744      * Sets the default sort column and order to be used by the next load operation.
12745      * @param {String} fieldName The name of the field to sort by.
12746      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
12747      */
12748     setDefaultSort : function(field, dir){
12749         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
12750     },
12751
12752     /**
12753      * Sort the Records.
12754      * If remote sorting is used, the sort is performed on the server, and the cache is
12755      * reloaded. If local sorting is used, the cache is sorted internally.
12756      * @param {String} fieldName The name of the field to sort by.
12757      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
12758      */
12759     sort : function(fieldName, dir){
12760         var f = this.fields.get(fieldName);
12761         if(!dir){
12762             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
12763             
12764             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
12765                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
12766             }else{
12767                 dir = f.sortDir;
12768             }
12769         }
12770         this.sortToggle[f.name] = dir;
12771         this.sortInfo = {field: f.name, direction: dir};
12772         if(!this.remoteSort){
12773             this.applySort();
12774             this.fireEvent("datachanged", this);
12775         }else{
12776             this.load(this.lastOptions);
12777         }
12778     },
12779
12780     /**
12781      * Calls the specified function for each of the Records in the cache.
12782      * @param {Function} fn The function to call. The Record is passed as the first parameter.
12783      * Returning <em>false</em> aborts and exits the iteration.
12784      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
12785      */
12786     each : function(fn, scope){
12787         this.data.each(fn, scope);
12788     },
12789
12790     /**
12791      * Gets all records modified since the last commit.  Modified records are persisted across load operations
12792      * (e.g., during paging).
12793      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
12794      */
12795     getModifiedRecords : function(){
12796         return this.modified;
12797     },
12798
12799     // private
12800     createFilterFn : function(property, value, anyMatch){
12801         if(!value.exec){ // not a regex
12802             value = String(value);
12803             if(value.length == 0){
12804                 return false;
12805             }
12806             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
12807         }
12808         return function(r){
12809             return value.test(r.data[property]);
12810         };
12811     },
12812
12813     /**
12814      * Sums the value of <i>property</i> for each record between start and end and returns the result.
12815      * @param {String} property A field on your records
12816      * @param {Number} start The record index to start at (defaults to 0)
12817      * @param {Number} end The last record index to include (defaults to length - 1)
12818      * @return {Number} The sum
12819      */
12820     sum : function(property, start, end){
12821         var rs = this.data.items, v = 0;
12822         start = start || 0;
12823         end = (end || end === 0) ? end : rs.length-1;
12824
12825         for(var i = start; i <= end; i++){
12826             v += (rs[i].data[property] || 0);
12827         }
12828         return v;
12829     },
12830
12831     /**
12832      * Filter the records by a specified property.
12833      * @param {String} field A field on your records
12834      * @param {String/RegExp} value Either a string that the field
12835      * should start with or a RegExp to test against the field
12836      * @param {Boolean} anyMatch True to match any part not just the beginning
12837      */
12838     filter : function(property, value, anyMatch){
12839         var fn = this.createFilterFn(property, value, anyMatch);
12840         return fn ? this.filterBy(fn) : this.clearFilter();
12841     },
12842
12843     /**
12844      * Filter by a function. The specified function will be called with each
12845      * record in this data source. If the function returns true the record is included,
12846      * otherwise it is filtered.
12847      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
12848      * @param {Object} scope (optional) The scope of the function (defaults to this)
12849      */
12850     filterBy : function(fn, scope){
12851         this.snapshot = this.snapshot || this.data;
12852         this.data = this.queryBy(fn, scope||this);
12853         this.fireEvent("datachanged", this);
12854     },
12855
12856     /**
12857      * Query the records by a specified property.
12858      * @param {String} field A field on your records
12859      * @param {String/RegExp} value Either a string that the field
12860      * should start with or a RegExp to test against the field
12861      * @param {Boolean} anyMatch True to match any part not just the beginning
12862      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
12863      */
12864     query : function(property, value, anyMatch){
12865         var fn = this.createFilterFn(property, value, anyMatch);
12866         return fn ? this.queryBy(fn) : this.data.clone();
12867     },
12868
12869     /**
12870      * Query by a function. The specified function will be called with each
12871      * record in this data source. If the function returns true the record is included
12872      * in the results.
12873      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
12874      * @param {Object} scope (optional) The scope of the function (defaults to this)
12875       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
12876      **/
12877     queryBy : function(fn, scope){
12878         var data = this.snapshot || this.data;
12879         return data.filterBy(fn, scope||this);
12880     },
12881
12882     /**
12883      * Collects unique values for a particular dataIndex from this store.
12884      * @param {String} dataIndex The property to collect
12885      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
12886      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
12887      * @return {Array} An array of the unique values
12888      **/
12889     collect : function(dataIndex, allowNull, bypassFilter){
12890         var d = (bypassFilter === true && this.snapshot) ?
12891                 this.snapshot.items : this.data.items;
12892         var v, sv, r = [], l = {};
12893         for(var i = 0, len = d.length; i < len; i++){
12894             v = d[i].data[dataIndex];
12895             sv = String(v);
12896             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
12897                 l[sv] = true;
12898                 r[r.length] = v;
12899             }
12900         }
12901         return r;
12902     },
12903
12904     /**
12905      * Revert to a view of the Record cache with no filtering applied.
12906      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
12907      */
12908     clearFilter : function(suppressEvent){
12909         if(this.snapshot && this.snapshot != this.data){
12910             this.data = this.snapshot;
12911             delete this.snapshot;
12912             if(suppressEvent !== true){
12913                 this.fireEvent("datachanged", this);
12914             }
12915         }
12916     },
12917
12918     // private
12919     afterEdit : function(record){
12920         if(this.modified.indexOf(record) == -1){
12921             this.modified.push(record);
12922         }
12923         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
12924     },
12925     
12926     // private
12927     afterReject : function(record){
12928         this.modified.remove(record);
12929         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
12930     },
12931
12932     // private
12933     afterCommit : function(record){
12934         this.modified.remove(record);
12935         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
12936     },
12937
12938     /**
12939      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
12940      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
12941      */
12942     commitChanges : function(){
12943         var m = this.modified.slice(0);
12944         this.modified = [];
12945         for(var i = 0, len = m.length; i < len; i++){
12946             m[i].commit();
12947         }
12948     },
12949
12950     /**
12951      * Cancel outstanding changes on all changed records.
12952      */
12953     rejectChanges : function(){
12954         var m = this.modified.slice(0);
12955         this.modified = [];
12956         for(var i = 0, len = m.length; i < len; i++){
12957             m[i].reject();
12958         }
12959     },
12960
12961     onMetaChange : function(meta, rtype, o){
12962         this.recordType = rtype;
12963         this.fields = rtype.prototype.fields;
12964         delete this.snapshot;
12965         this.sortInfo = meta.sortInfo || this.sortInfo;
12966         this.modified = [];
12967         this.fireEvent('metachange', this, this.reader.meta);
12968     },
12969     
12970     moveIndex : function(data, type)
12971     {
12972         var index = this.indexOf(data);
12973         
12974         var newIndex = index + type;
12975         
12976         this.remove(data);
12977         
12978         this.insert(newIndex, data);
12979         
12980     }
12981 });/*
12982  * Based on:
12983  * Ext JS Library 1.1.1
12984  * Copyright(c) 2006-2007, Ext JS, LLC.
12985  *
12986  * Originally Released Under LGPL - original licence link has changed is not relivant.
12987  *
12988  * Fork - LGPL
12989  * <script type="text/javascript">
12990  */
12991
12992 /**
12993  * @class Roo.data.SimpleStore
12994  * @extends Roo.data.Store
12995  * Small helper class to make creating Stores from Array data easier.
12996  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
12997  * @cfg {Array} fields An array of field definition objects, or field name strings.
12998  * @cfg {Object} an existing reader (eg. copied from another store)
12999  * @cfg {Array} data The multi-dimensional array of data
13000  * @constructor
13001  * @param {Object} config
13002  */
13003 Roo.data.SimpleStore = function(config)
13004 {
13005     Roo.data.SimpleStore.superclass.constructor.call(this, {
13006         isLocal : true,
13007         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13008                 id: config.id
13009             },
13010             Roo.data.Record.create(config.fields)
13011         ),
13012         proxy : new Roo.data.MemoryProxy(config.data)
13013     });
13014     this.load();
13015 };
13016 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13017  * Based on:
13018  * Ext JS Library 1.1.1
13019  * Copyright(c) 2006-2007, Ext JS, LLC.
13020  *
13021  * Originally Released Under LGPL - original licence link has changed is not relivant.
13022  *
13023  * Fork - LGPL
13024  * <script type="text/javascript">
13025  */
13026
13027 /**
13028 /**
13029  * @extends Roo.data.Store
13030  * @class Roo.data.JsonStore
13031  * Small helper class to make creating Stores for JSON data easier. <br/>
13032 <pre><code>
13033 var store = new Roo.data.JsonStore({
13034     url: 'get-images.php',
13035     root: 'images',
13036     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13037 });
13038 </code></pre>
13039  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13040  * JsonReader and HttpProxy (unless inline data is provided).</b>
13041  * @cfg {Array} fields An array of field definition objects, or field name strings.
13042  * @constructor
13043  * @param {Object} config
13044  */
13045 Roo.data.JsonStore = function(c){
13046     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13047         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13048         reader: new Roo.data.JsonReader(c, c.fields)
13049     }));
13050 };
13051 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13052  * Based on:
13053  * Ext JS Library 1.1.1
13054  * Copyright(c) 2006-2007, Ext JS, LLC.
13055  *
13056  * Originally Released Under LGPL - original licence link has changed is not relivant.
13057  *
13058  * Fork - LGPL
13059  * <script type="text/javascript">
13060  */
13061
13062  
13063 Roo.data.Field = function(config){
13064     if(typeof config == "string"){
13065         config = {name: config};
13066     }
13067     Roo.apply(this, config);
13068     
13069     if(!this.type){
13070         this.type = "auto";
13071     }
13072     
13073     var st = Roo.data.SortTypes;
13074     // named sortTypes are supported, here we look them up
13075     if(typeof this.sortType == "string"){
13076         this.sortType = st[this.sortType];
13077     }
13078     
13079     // set default sortType for strings and dates
13080     if(!this.sortType){
13081         switch(this.type){
13082             case "string":
13083                 this.sortType = st.asUCString;
13084                 break;
13085             case "date":
13086                 this.sortType = st.asDate;
13087                 break;
13088             default:
13089                 this.sortType = st.none;
13090         }
13091     }
13092
13093     // define once
13094     var stripRe = /[\$,%]/g;
13095
13096     // prebuilt conversion function for this field, instead of
13097     // switching every time we're reading a value
13098     if(!this.convert){
13099         var cv, dateFormat = this.dateFormat;
13100         switch(this.type){
13101             case "":
13102             case "auto":
13103             case undefined:
13104                 cv = function(v){ return v; };
13105                 break;
13106             case "string":
13107                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
13108                 break;
13109             case "int":
13110                 cv = function(v){
13111                     return v !== undefined && v !== null && v !== '' ?
13112                            parseInt(String(v).replace(stripRe, ""), 10) : '';
13113                     };
13114                 break;
13115             case "float":
13116                 cv = function(v){
13117                     return v !== undefined && v !== null && v !== '' ?
13118                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
13119                     };
13120                 break;
13121             case "bool":
13122             case "boolean":
13123                 cv = function(v){ return v === true || v === "true" || v == 1; };
13124                 break;
13125             case "date":
13126                 cv = function(v){
13127                     if(!v){
13128                         return '';
13129                     }
13130                     if(v instanceof Date){
13131                         return v;
13132                     }
13133                     if(dateFormat){
13134                         if(dateFormat == "timestamp"){
13135                             return new Date(v*1000);
13136                         }
13137                         return Date.parseDate(v, dateFormat);
13138                     }
13139                     var parsed = Date.parse(v);
13140                     return parsed ? new Date(parsed) : null;
13141                 };
13142              break;
13143             
13144         }
13145         this.convert = cv;
13146     }
13147 };
13148
13149 Roo.data.Field.prototype = {
13150     dateFormat: null,
13151     defaultValue: "",
13152     mapping: null,
13153     sortType : null,
13154     sortDir : "ASC"
13155 };/*
13156  * Based on:
13157  * Ext JS Library 1.1.1
13158  * Copyright(c) 2006-2007, Ext JS, LLC.
13159  *
13160  * Originally Released Under LGPL - original licence link has changed is not relivant.
13161  *
13162  * Fork - LGPL
13163  * <script type="text/javascript">
13164  */
13165  
13166 // Base class for reading structured data from a data source.  This class is intended to be
13167 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
13168
13169 /**
13170  * @class Roo.data.DataReader
13171  * Base class for reading structured data from a data source.  This class is intended to be
13172  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
13173  */
13174
13175 Roo.data.DataReader = function(meta, recordType){
13176     
13177     this.meta = meta;
13178     
13179     this.recordType = recordType instanceof Array ? 
13180         Roo.data.Record.create(recordType) : recordType;
13181 };
13182
13183 Roo.data.DataReader.prototype = {
13184     
13185     
13186     readerType : 'Data',
13187      /**
13188      * Create an empty record
13189      * @param {Object} data (optional) - overlay some values
13190      * @return {Roo.data.Record} record created.
13191      */
13192     newRow :  function(d) {
13193         var da =  {};
13194         this.recordType.prototype.fields.each(function(c) {
13195             switch( c.type) {
13196                 case 'int' : da[c.name] = 0; break;
13197                 case 'date' : da[c.name] = new Date(); break;
13198                 case 'float' : da[c.name] = 0.0; break;
13199                 case 'boolean' : da[c.name] = false; break;
13200                 default : da[c.name] = ""; break;
13201             }
13202             
13203         });
13204         return new this.recordType(Roo.apply(da, d));
13205     }
13206     
13207     
13208 };/*
13209  * Based on:
13210  * Ext JS Library 1.1.1
13211  * Copyright(c) 2006-2007, Ext JS, LLC.
13212  *
13213  * Originally Released Under LGPL - original licence link has changed is not relivant.
13214  *
13215  * Fork - LGPL
13216  * <script type="text/javascript">
13217  */
13218
13219 /**
13220  * @class Roo.data.DataProxy
13221  * @extends Roo.data.Observable
13222  * This class is an abstract base class for implementations which provide retrieval of
13223  * unformatted data objects.<br>
13224  * <p>
13225  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
13226  * (of the appropriate type which knows how to parse the data object) to provide a block of
13227  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
13228  * <p>
13229  * Custom implementations must implement the load method as described in
13230  * {@link Roo.data.HttpProxy#load}.
13231  */
13232 Roo.data.DataProxy = function(){
13233     this.addEvents({
13234         /**
13235          * @event beforeload
13236          * Fires before a network request is made to retrieve a data object.
13237          * @param {Object} This DataProxy object.
13238          * @param {Object} params The params parameter to the load function.
13239          */
13240         beforeload : true,
13241         /**
13242          * @event load
13243          * Fires before the load method's callback is called.
13244          * @param {Object} This DataProxy object.
13245          * @param {Object} o The data object.
13246          * @param {Object} arg The callback argument object passed to the load function.
13247          */
13248         load : true,
13249         /**
13250          * @event loadexception
13251          * Fires if an Exception occurs during data retrieval.
13252          * @param {Object} This DataProxy object.
13253          * @param {Object} o The data object.
13254          * @param {Object} arg The callback argument object passed to the load function.
13255          * @param {Object} e The Exception.
13256          */
13257         loadexception : true
13258     });
13259     Roo.data.DataProxy.superclass.constructor.call(this);
13260 };
13261
13262 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
13263
13264     /**
13265      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
13266      */
13267 /*
13268  * Based on:
13269  * Ext JS Library 1.1.1
13270  * Copyright(c) 2006-2007, Ext JS, LLC.
13271  *
13272  * Originally Released Under LGPL - original licence link has changed is not relivant.
13273  *
13274  * Fork - LGPL
13275  * <script type="text/javascript">
13276  */
13277 /**
13278  * @class Roo.data.MemoryProxy
13279  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
13280  * to the Reader when its load method is called.
13281  * @constructor
13282  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
13283  */
13284 Roo.data.MemoryProxy = function(data){
13285     if (data.data) {
13286         data = data.data;
13287     }
13288     Roo.data.MemoryProxy.superclass.constructor.call(this);
13289     this.data = data;
13290 };
13291
13292 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
13293     
13294     /**
13295      * Load data from the requested source (in this case an in-memory
13296      * data object passed to the constructor), read the data object into
13297      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
13298      * process that block using the passed callback.
13299      * @param {Object} params This parameter is not used by the MemoryProxy class.
13300      * @param {Roo.data.DataReader} reader The Reader object which converts the data
13301      * object into a block of Roo.data.Records.
13302      * @param {Function} callback The function into which to pass the block of Roo.data.records.
13303      * The function must be passed <ul>
13304      * <li>The Record block object</li>
13305      * <li>The "arg" argument from the load function</li>
13306      * <li>A boolean success indicator</li>
13307      * </ul>
13308      * @param {Object} scope The scope in which to call the callback
13309      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
13310      */
13311     load : function(params, reader, callback, scope, arg){
13312         params = params || {};
13313         var result;
13314         try {
13315             result = reader.readRecords(params.data ? params.data :this.data);
13316         }catch(e){
13317             this.fireEvent("loadexception", this, arg, null, e);
13318             callback.call(scope, null, arg, false);
13319             return;
13320         }
13321         callback.call(scope, result, arg, true);
13322     },
13323     
13324     // private
13325     update : function(params, records){
13326         
13327     }
13328 });/*
13329  * Based on:
13330  * Ext JS Library 1.1.1
13331  * Copyright(c) 2006-2007, Ext JS, LLC.
13332  *
13333  * Originally Released Under LGPL - original licence link has changed is not relivant.
13334  *
13335  * Fork - LGPL
13336  * <script type="text/javascript">
13337  */
13338 /**
13339  * @class Roo.data.HttpProxy
13340  * @extends Roo.data.DataProxy
13341  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
13342  * configured to reference a certain URL.<br><br>
13343  * <p>
13344  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
13345  * from which the running page was served.<br><br>
13346  * <p>
13347  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
13348  * <p>
13349  * Be aware that to enable the browser to parse an XML document, the server must set
13350  * the Content-Type header in the HTTP response to "text/xml".
13351  * @constructor
13352  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
13353  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
13354  * will be used to make the request.
13355  */
13356 Roo.data.HttpProxy = function(conn){
13357     Roo.data.HttpProxy.superclass.constructor.call(this);
13358     // is conn a conn config or a real conn?
13359     this.conn = conn;
13360     this.useAjax = !conn || !conn.events;
13361   
13362 };
13363
13364 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
13365     // thse are take from connection...
13366     
13367     /**
13368      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
13369      */
13370     /**
13371      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
13372      * extra parameters to each request made by this object. (defaults to undefined)
13373      */
13374     /**
13375      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
13376      *  to each request made by this object. (defaults to undefined)
13377      */
13378     /**
13379      * @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)
13380      */
13381     /**
13382      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
13383      */
13384      /**
13385      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
13386      * @type Boolean
13387      */
13388   
13389
13390     /**
13391      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
13392      * @type Boolean
13393      */
13394     /**
13395      * Return the {@link Roo.data.Connection} object being used by this Proxy.
13396      * @return {Connection} The Connection object. This object may be used to subscribe to events on
13397      * a finer-grained basis than the DataProxy events.
13398      */
13399     getConnection : function(){
13400         return this.useAjax ? Roo.Ajax : this.conn;
13401     },
13402
13403     /**
13404      * Load data from the configured {@link Roo.data.Connection}, read the data object into
13405      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
13406      * process that block using the passed callback.
13407      * @param {Object} params An object containing properties which are to be used as HTTP parameters
13408      * for the request to the remote server.
13409      * @param {Roo.data.DataReader} reader The Reader object which converts the data
13410      * object into a block of Roo.data.Records.
13411      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
13412      * The function must be passed <ul>
13413      * <li>The Record block object</li>
13414      * <li>The "arg" argument from the load function</li>
13415      * <li>A boolean success indicator</li>
13416      * </ul>
13417      * @param {Object} scope The scope in which to call the callback
13418      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
13419      */
13420     load : function(params, reader, callback, scope, arg){
13421         if(this.fireEvent("beforeload", this, params) !== false){
13422             var  o = {
13423                 params : params || {},
13424                 request: {
13425                     callback : callback,
13426                     scope : scope,
13427                     arg : arg
13428                 },
13429                 reader: reader,
13430                 callback : this.loadResponse,
13431                 scope: this
13432             };
13433             if(this.useAjax){
13434                 Roo.applyIf(o, this.conn);
13435                 if(this.activeRequest){
13436                     Roo.Ajax.abort(this.activeRequest);
13437                 }
13438                 this.activeRequest = Roo.Ajax.request(o);
13439             }else{
13440                 this.conn.request(o);
13441             }
13442         }else{
13443             callback.call(scope||this, null, arg, false);
13444         }
13445     },
13446
13447     // private
13448     loadResponse : function(o, success, response){
13449         delete this.activeRequest;
13450         if(!success){
13451             this.fireEvent("loadexception", this, o, response);
13452             o.request.callback.call(o.request.scope, null, o.request.arg, false);
13453             return;
13454         }
13455         var result;
13456         try {
13457             result = o.reader.read(response);
13458         }catch(e){
13459             this.fireEvent("loadexception", this, o, response, e);
13460             o.request.callback.call(o.request.scope, null, o.request.arg, false);
13461             return;
13462         }
13463         
13464         this.fireEvent("load", this, o, o.request.arg);
13465         o.request.callback.call(o.request.scope, result, o.request.arg, true);
13466     },
13467
13468     // private
13469     update : function(dataSet){
13470
13471     },
13472
13473     // private
13474     updateResponse : function(dataSet){
13475
13476     }
13477 });/*
13478  * Based on:
13479  * Ext JS Library 1.1.1
13480  * Copyright(c) 2006-2007, Ext JS, LLC.
13481  *
13482  * Originally Released Under LGPL - original licence link has changed is not relivant.
13483  *
13484  * Fork - LGPL
13485  * <script type="text/javascript">
13486  */
13487
13488 /**
13489  * @class Roo.data.ScriptTagProxy
13490  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
13491  * other than the originating domain of the running page.<br><br>
13492  * <p>
13493  * <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
13494  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
13495  * <p>
13496  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
13497  * source code that is used as the source inside a &lt;script> tag.<br><br>
13498  * <p>
13499  * In order for the browser to process the returned data, the server must wrap the data object
13500  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
13501  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
13502  * depending on whether the callback name was passed:
13503  * <p>
13504  * <pre><code>
13505 boolean scriptTag = false;
13506 String cb = request.getParameter("callback");
13507 if (cb != null) {
13508     scriptTag = true;
13509     response.setContentType("text/javascript");
13510 } else {
13511     response.setContentType("application/x-json");
13512 }
13513 Writer out = response.getWriter();
13514 if (scriptTag) {
13515     out.write(cb + "(");
13516 }
13517 out.print(dataBlock.toJsonString());
13518 if (scriptTag) {
13519     out.write(");");
13520 }
13521 </pre></code>
13522  *
13523  * @constructor
13524  * @param {Object} config A configuration object.
13525  */
13526 Roo.data.ScriptTagProxy = function(config){
13527     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
13528     Roo.apply(this, config);
13529     this.head = document.getElementsByTagName("head")[0];
13530 };
13531
13532 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
13533
13534 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
13535     /**
13536      * @cfg {String} url The URL from which to request the data object.
13537      */
13538     /**
13539      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
13540      */
13541     timeout : 30000,
13542     /**
13543      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
13544      * the server the name of the callback function set up by the load call to process the returned data object.
13545      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
13546      * javascript output which calls this named function passing the data object as its only parameter.
13547      */
13548     callbackParam : "callback",
13549     /**
13550      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
13551      * name to the request.
13552      */
13553     nocache : true,
13554
13555     /**
13556      * Load data from the configured URL, read the data object into
13557      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
13558      * process that block using the passed callback.
13559      * @param {Object} params An object containing properties which are to be used as HTTP parameters
13560      * for the request to the remote server.
13561      * @param {Roo.data.DataReader} reader The Reader object which converts the data
13562      * object into a block of Roo.data.Records.
13563      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
13564      * The function must be passed <ul>
13565      * <li>The Record block object</li>
13566      * <li>The "arg" argument from the load function</li>
13567      * <li>A boolean success indicator</li>
13568      * </ul>
13569      * @param {Object} scope The scope in which to call the callback
13570      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
13571      */
13572     load : function(params, reader, callback, scope, arg){
13573         if(this.fireEvent("beforeload", this, params) !== false){
13574
13575             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
13576
13577             var url = this.url;
13578             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
13579             if(this.nocache){
13580                 url += "&_dc=" + (new Date().getTime());
13581             }
13582             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
13583             var trans = {
13584                 id : transId,
13585                 cb : "stcCallback"+transId,
13586                 scriptId : "stcScript"+transId,
13587                 params : params,
13588                 arg : arg,
13589                 url : url,
13590                 callback : callback,
13591                 scope : scope,
13592                 reader : reader
13593             };
13594             var conn = this;
13595
13596             window[trans.cb] = function(o){
13597                 conn.handleResponse(o, trans);
13598             };
13599
13600             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
13601
13602             if(this.autoAbort !== false){
13603                 this.abort();
13604             }
13605
13606             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
13607
13608             var script = document.createElement("script");
13609             script.setAttribute("src", url);
13610             script.setAttribute("type", "text/javascript");
13611             script.setAttribute("id", trans.scriptId);
13612             this.head.appendChild(script);
13613
13614             this.trans = trans;
13615         }else{
13616             callback.call(scope||this, null, arg, false);
13617         }
13618     },
13619
13620     // private
13621     isLoading : function(){
13622         return this.trans ? true : false;
13623     },
13624
13625     /**
13626      * Abort the current server request.
13627      */
13628     abort : function(){
13629         if(this.isLoading()){
13630             this.destroyTrans(this.trans);
13631         }
13632     },
13633
13634     // private
13635     destroyTrans : function(trans, isLoaded){
13636         this.head.removeChild(document.getElementById(trans.scriptId));
13637         clearTimeout(trans.timeoutId);
13638         if(isLoaded){
13639             window[trans.cb] = undefined;
13640             try{
13641                 delete window[trans.cb];
13642             }catch(e){}
13643         }else{
13644             // if hasn't been loaded, wait for load to remove it to prevent script error
13645             window[trans.cb] = function(){
13646                 window[trans.cb] = undefined;
13647                 try{
13648                     delete window[trans.cb];
13649                 }catch(e){}
13650             };
13651         }
13652     },
13653
13654     // private
13655     handleResponse : function(o, trans){
13656         this.trans = false;
13657         this.destroyTrans(trans, true);
13658         var result;
13659         try {
13660             result = trans.reader.readRecords(o);
13661         }catch(e){
13662             this.fireEvent("loadexception", this, o, trans.arg, e);
13663             trans.callback.call(trans.scope||window, null, trans.arg, false);
13664             return;
13665         }
13666         this.fireEvent("load", this, o, trans.arg);
13667         trans.callback.call(trans.scope||window, result, trans.arg, true);
13668     },
13669
13670     // private
13671     handleFailure : function(trans){
13672         this.trans = false;
13673         this.destroyTrans(trans, false);
13674         this.fireEvent("loadexception", this, null, trans.arg);
13675         trans.callback.call(trans.scope||window, null, trans.arg, false);
13676     }
13677 });/*
13678  * Based on:
13679  * Ext JS Library 1.1.1
13680  * Copyright(c) 2006-2007, Ext JS, LLC.
13681  *
13682  * Originally Released Under LGPL - original licence link has changed is not relivant.
13683  *
13684  * Fork - LGPL
13685  * <script type="text/javascript">
13686  */
13687
13688 /**
13689  * @class Roo.data.JsonReader
13690  * @extends Roo.data.DataReader
13691  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
13692  * based on mappings in a provided Roo.data.Record constructor.
13693  * 
13694  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
13695  * in the reply previously. 
13696  * 
13697  * <p>
13698  * Example code:
13699  * <pre><code>
13700 var RecordDef = Roo.data.Record.create([
13701     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
13702     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
13703 ]);
13704 var myReader = new Roo.data.JsonReader({
13705     totalProperty: "results",    // The property which contains the total dataset size (optional)
13706     root: "rows",                // The property which contains an Array of row objects
13707     id: "id"                     // The property within each row object that provides an ID for the record (optional)
13708 }, RecordDef);
13709 </code></pre>
13710  * <p>
13711  * This would consume a JSON file like this:
13712  * <pre><code>
13713 { 'results': 2, 'rows': [
13714     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
13715     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
13716 }
13717 </code></pre>
13718  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
13719  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
13720  * paged from the remote server.
13721  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
13722  * @cfg {String} root name of the property which contains the Array of row objects.
13723  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
13724  * @cfg {Array} fields Array of field definition objects
13725  * @constructor
13726  * Create a new JsonReader
13727  * @param {Object} meta Metadata configuration options
13728  * @param {Object} recordType Either an Array of field definition objects,
13729  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
13730  */
13731 Roo.data.JsonReader = function(meta, recordType){
13732     
13733     meta = meta || {};
13734     // set some defaults:
13735     Roo.applyIf(meta, {
13736         totalProperty: 'total',
13737         successProperty : 'success',
13738         root : 'data',
13739         id : 'id'
13740     });
13741     
13742     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
13743 };
13744 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
13745     
13746     readerType : 'Json',
13747     
13748     /**
13749      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
13750      * Used by Store query builder to append _requestMeta to params.
13751      * 
13752      */
13753     metaFromRemote : false,
13754     /**
13755      * This method is only used by a DataProxy which has retrieved data from a remote server.
13756      * @param {Object} response The XHR object which contains the JSON data in its responseText.
13757      * @return {Object} data A data block which is used by an Roo.data.Store object as
13758      * a cache of Roo.data.Records.
13759      */
13760     read : function(response){
13761         var json = response.responseText;
13762        
13763         var o = /* eval:var:o */ eval("("+json+")");
13764         if(!o) {
13765             throw {message: "JsonReader.read: Json object not found"};
13766         }
13767         
13768         if(o.metaData){
13769             
13770             delete this.ef;
13771             this.metaFromRemote = true;
13772             this.meta = o.metaData;
13773             this.recordType = Roo.data.Record.create(o.metaData.fields);
13774             this.onMetaChange(this.meta, this.recordType, o);
13775         }
13776         return this.readRecords(o);
13777     },
13778
13779     // private function a store will implement
13780     onMetaChange : function(meta, recordType, o){
13781
13782     },
13783
13784     /**
13785          * @ignore
13786          */
13787     simpleAccess: function(obj, subsc) {
13788         return obj[subsc];
13789     },
13790
13791         /**
13792          * @ignore
13793          */
13794     getJsonAccessor: function(){
13795         var re = /[\[\.]/;
13796         return function(expr) {
13797             try {
13798                 return(re.test(expr))
13799                     ? new Function("obj", "return obj." + expr)
13800                     : function(obj){
13801                         return obj[expr];
13802                     };
13803             } catch(e){}
13804             return Roo.emptyFn;
13805         };
13806     }(),
13807
13808     /**
13809      * Create a data block containing Roo.data.Records from an XML document.
13810      * @param {Object} o An object which contains an Array of row objects in the property specified
13811      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
13812      * which contains the total size of the dataset.
13813      * @return {Object} data A data block which is used by an Roo.data.Store object as
13814      * a cache of Roo.data.Records.
13815      */
13816     readRecords : function(o){
13817         /**
13818          * After any data loads, the raw JSON data is available for further custom processing.
13819          * @type Object
13820          */
13821         this.o = o;
13822         var s = this.meta, Record = this.recordType,
13823             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
13824
13825 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
13826         if (!this.ef) {
13827             if(s.totalProperty) {
13828                     this.getTotal = this.getJsonAccessor(s.totalProperty);
13829                 }
13830                 if(s.successProperty) {
13831                     this.getSuccess = this.getJsonAccessor(s.successProperty);
13832                 }
13833                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
13834                 if (s.id) {
13835                         var g = this.getJsonAccessor(s.id);
13836                         this.getId = function(rec) {
13837                                 var r = g(rec);  
13838                                 return (r === undefined || r === "") ? null : r;
13839                         };
13840                 } else {
13841                         this.getId = function(){return null;};
13842                 }
13843             this.ef = [];
13844             for(var jj = 0; jj < fl; jj++){
13845                 f = fi[jj];
13846                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
13847                 this.ef[jj] = this.getJsonAccessor(map);
13848             }
13849         }
13850
13851         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
13852         if(s.totalProperty){
13853             var vt = parseInt(this.getTotal(o), 10);
13854             if(!isNaN(vt)){
13855                 totalRecords = vt;
13856             }
13857         }
13858         if(s.successProperty){
13859             var vs = this.getSuccess(o);
13860             if(vs === false || vs === 'false'){
13861                 success = false;
13862             }
13863         }
13864         var records = [];
13865         for(var i = 0; i < c; i++){
13866                 var n = root[i];
13867             var values = {};
13868             var id = this.getId(n);
13869             for(var j = 0; j < fl; j++){
13870                 f = fi[j];
13871             var v = this.ef[j](n);
13872             if (!f.convert) {
13873                 Roo.log('missing convert for ' + f.name);
13874                 Roo.log(f);
13875                 continue;
13876             }
13877             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
13878             }
13879             var record = new Record(values, id);
13880             record.json = n;
13881             records[i] = record;
13882         }
13883         return {
13884             raw : o,
13885             success : success,
13886             records : records,
13887             totalRecords : totalRecords
13888         };
13889     },
13890     // used when loading children.. @see loadDataFromChildren
13891     toLoadData: function(rec)
13892     {
13893         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
13894         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
13895         return { data : data, total : data.length };
13896         
13897     }
13898 });/*
13899  * Based on:
13900  * Ext JS Library 1.1.1
13901  * Copyright(c) 2006-2007, Ext JS, LLC.
13902  *
13903  * Originally Released Under LGPL - original licence link has changed is not relivant.
13904  *
13905  * Fork - LGPL
13906  * <script type="text/javascript">
13907  */
13908
13909 /**
13910  * @class Roo.data.ArrayReader
13911  * @extends Roo.data.DataReader
13912  * Data reader class to create an Array of Roo.data.Record objects from an Array.
13913  * Each element of that Array represents a row of data fields. The
13914  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
13915  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
13916  * <p>
13917  * Example code:.
13918  * <pre><code>
13919 var RecordDef = Roo.data.Record.create([
13920     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
13921     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
13922 ]);
13923 var myReader = new Roo.data.ArrayReader({
13924     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
13925 }, RecordDef);
13926 </code></pre>
13927  * <p>
13928  * This would consume an Array like this:
13929  * <pre><code>
13930 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
13931   </code></pre>
13932  
13933  * @constructor
13934  * Create a new JsonReader
13935  * @param {Object} meta Metadata configuration options.
13936  * @param {Object|Array} recordType Either an Array of field definition objects
13937  * 
13938  * @cfg {Array} fields Array of field definition objects
13939  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
13940  * as specified to {@link Roo.data.Record#create},
13941  * or an {@link Roo.data.Record} object
13942  *
13943  * 
13944  * created using {@link Roo.data.Record#create}.
13945  */
13946 Roo.data.ArrayReader = function(meta, recordType)
13947 {    
13948     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
13949 };
13950
13951 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
13952     
13953       /**
13954      * Create a data block containing Roo.data.Records from an XML document.
13955      * @param {Object} o An Array of row objects which represents the dataset.
13956      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
13957      * a cache of Roo.data.Records.
13958      */
13959     readRecords : function(o)
13960     {
13961         var sid = this.meta ? this.meta.id : null;
13962         var recordType = this.recordType, fields = recordType.prototype.fields;
13963         var records = [];
13964         var root = o;
13965         for(var i = 0; i < root.length; i++){
13966                 var n = root[i];
13967             var values = {};
13968             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
13969             for(var j = 0, jlen = fields.length; j < jlen; j++){
13970                 var f = fields.items[j];
13971                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
13972                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
13973                 v = f.convert(v);
13974                 values[f.name] = v;
13975             }
13976             var record = new recordType(values, id);
13977             record.json = n;
13978             records[records.length] = record;
13979         }
13980         return {
13981             records : records,
13982             totalRecords : records.length
13983         };
13984     },
13985     // used when loading children.. @see loadDataFromChildren
13986     toLoadData: function(rec)
13987     {
13988         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
13989         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
13990         
13991     }
13992     
13993     
13994 });/*
13995  * - LGPL
13996  * * 
13997  */
13998
13999 /**
14000  * @class Roo.bootstrap.ComboBox
14001  * @extends Roo.bootstrap.TriggerField
14002  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14003  * @cfg {Boolean} append (true|false) default false
14004  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14005  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14006  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14007  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14008  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14009  * @cfg {Boolean} animate default true
14010  * @cfg {Boolean} emptyResultText only for touch device
14011  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14012  * @cfg {String} emptyTitle default ''
14013  * @constructor
14014  * Create a new ComboBox.
14015  * @param {Object} config Configuration options
14016  */
14017 Roo.bootstrap.ComboBox = function(config){
14018     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14019     this.addEvents({
14020         /**
14021          * @event expand
14022          * Fires when the dropdown list is expanded
14023         * @param {Roo.bootstrap.ComboBox} combo This combo box
14024         */
14025         'expand' : true,
14026         /**
14027          * @event collapse
14028          * Fires when the dropdown list is collapsed
14029         * @param {Roo.bootstrap.ComboBox} combo This combo box
14030         */
14031         'collapse' : true,
14032         /**
14033          * @event beforeselect
14034          * Fires before a list item is selected. Return false to cancel the selection.
14035         * @param {Roo.bootstrap.ComboBox} combo This combo box
14036         * @param {Roo.data.Record} record The data record returned from the underlying store
14037         * @param {Number} index The index of the selected item in the dropdown list
14038         */
14039         'beforeselect' : true,
14040         /**
14041          * @event select
14042          * Fires when a list item is selected
14043         * @param {Roo.bootstrap.ComboBox} combo This combo box
14044         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14045         * @param {Number} index The index of the selected item in the dropdown list
14046         */
14047         'select' : true,
14048         /**
14049          * @event beforequery
14050          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14051          * The event object passed has these properties:
14052         * @param {Roo.bootstrap.ComboBox} combo This combo box
14053         * @param {String} query The query
14054         * @param {Boolean} forceAll true to force "all" query
14055         * @param {Boolean} cancel true to cancel the query
14056         * @param {Object} e The query event object
14057         */
14058         'beforequery': true,
14059          /**
14060          * @event add
14061          * Fires when the 'add' icon is pressed (add a listener to enable add button)
14062         * @param {Roo.bootstrap.ComboBox} combo This combo box
14063         */
14064         'add' : true,
14065         /**
14066          * @event edit
14067          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14068         * @param {Roo.bootstrap.ComboBox} combo This combo box
14069         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14070         */
14071         'edit' : true,
14072         /**
14073          * @event remove
14074          * Fires when the remove value from the combobox array
14075         * @param {Roo.bootstrap.ComboBox} combo This combo box
14076         */
14077         'remove' : true,
14078         /**
14079          * @event afterremove
14080          * Fires when the remove value from the combobox array
14081         * @param {Roo.bootstrap.ComboBox} combo This combo box
14082         */
14083         'afterremove' : true,
14084         /**
14085          * @event specialfilter
14086          * Fires when specialfilter
14087             * @param {Roo.bootstrap.ComboBox} combo This combo box
14088             */
14089         'specialfilter' : true,
14090         /**
14091          * @event tick
14092          * Fires when tick the element
14093             * @param {Roo.bootstrap.ComboBox} combo This combo box
14094             */
14095         'tick' : true,
14096         /**
14097          * @event touchviewdisplay
14098          * Fires when touch view require special display (default is using displayField)
14099             * @param {Roo.bootstrap.ComboBox} combo This combo box
14100             * @param {Object} cfg set html .
14101             */
14102         'touchviewdisplay' : true
14103         
14104     });
14105     
14106     this.item = [];
14107     this.tickItems = [];
14108     
14109     this.selectedIndex = -1;
14110     if(this.mode == 'local'){
14111         if(config.queryDelay === undefined){
14112             this.queryDelay = 10;
14113         }
14114         if(config.minChars === undefined){
14115             this.minChars = 0;
14116         }
14117     }
14118 };
14119
14120 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
14121      
14122     /**
14123      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
14124      * rendering into an Roo.Editor, defaults to false)
14125      */
14126     /**
14127      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
14128      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
14129      */
14130     /**
14131      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
14132      */
14133     /**
14134      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
14135      * the dropdown list (defaults to undefined, with no header element)
14136      */
14137
14138      /**
14139      * @cfg {String/Roo.Template} tpl The template to use to render the output
14140      */
14141      
14142      /**
14143      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
14144      */
14145     listWidth: undefined,
14146     /**
14147      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
14148      * mode = 'remote' or 'text' if mode = 'local')
14149      */
14150     displayField: undefined,
14151     
14152     /**
14153      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
14154      * mode = 'remote' or 'value' if mode = 'local'). 
14155      * Note: use of a valueField requires the user make a selection
14156      * in order for a value to be mapped.
14157      */
14158     valueField: undefined,
14159     /**
14160      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
14161      */
14162     modalTitle : '',
14163     
14164     /**
14165      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
14166      * field's data value (defaults to the underlying DOM element's name)
14167      */
14168     hiddenName: undefined,
14169     /**
14170      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
14171      */
14172     listClass: '',
14173     /**
14174      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
14175      */
14176     selectedClass: 'active',
14177     
14178     /**
14179      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14180      */
14181     shadow:'sides',
14182     /**
14183      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
14184      * anchor positions (defaults to 'tl-bl')
14185      */
14186     listAlign: 'tl-bl?',
14187     /**
14188      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
14189      */
14190     maxHeight: 300,
14191     /**
14192      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
14193      * query specified by the allQuery config option (defaults to 'query')
14194      */
14195     triggerAction: 'query',
14196     /**
14197      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
14198      * (defaults to 4, does not apply if editable = false)
14199      */
14200     minChars : 4,
14201     /**
14202      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
14203      * delay (typeAheadDelay) if it matches a known value (defaults to false)
14204      */
14205     typeAhead: false,
14206     /**
14207      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
14208      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
14209      */
14210     queryDelay: 500,
14211     /**
14212      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
14213      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
14214      */
14215     pageSize: 0,
14216     /**
14217      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
14218      * when editable = true (defaults to false)
14219      */
14220     selectOnFocus:false,
14221     /**
14222      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
14223      */
14224     queryParam: 'query',
14225     /**
14226      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
14227      * when mode = 'remote' (defaults to 'Loading...')
14228      */
14229     loadingText: 'Loading...',
14230     /**
14231      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
14232      */
14233     resizable: false,
14234     /**
14235      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
14236      */
14237     handleHeight : 8,
14238     /**
14239      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
14240      * traditional select (defaults to true)
14241      */
14242     editable: true,
14243     /**
14244      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
14245      */
14246     allQuery: '',
14247     /**
14248      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
14249      */
14250     mode: 'remote',
14251     /**
14252      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
14253      * listWidth has a higher value)
14254      */
14255     minListWidth : 70,
14256     /**
14257      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
14258      * allow the user to set arbitrary text into the field (defaults to false)
14259      */
14260     forceSelection:false,
14261     /**
14262      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
14263      * if typeAhead = true (defaults to 250)
14264      */
14265     typeAheadDelay : 250,
14266     /**
14267      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
14268      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
14269      */
14270     valueNotFoundText : undefined,
14271     /**
14272      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
14273      */
14274     blockFocus : false,
14275     
14276     /**
14277      * @cfg {Boolean} disableClear Disable showing of clear button.
14278      */
14279     disableClear : false,
14280     /**
14281      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
14282      */
14283     alwaysQuery : false,
14284     
14285     /**
14286      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
14287      */
14288     multiple : false,
14289     
14290     /**
14291      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
14292      */
14293     invalidClass : "has-warning",
14294     
14295     /**
14296      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
14297      */
14298     validClass : "has-success",
14299     
14300     /**
14301      * @cfg {Boolean} specialFilter (true|false) special filter default false
14302      */
14303     specialFilter : false,
14304     
14305     /**
14306      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
14307      */
14308     mobileTouchView : true,
14309     
14310     /**
14311      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
14312      */
14313     useNativeIOS : false,
14314     
14315     /**
14316      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
14317      */
14318     mobile_restrict_height : false,
14319     
14320     ios_options : false,
14321     
14322     //private
14323     addicon : false,
14324     editicon: false,
14325     
14326     page: 0,
14327     hasQuery: false,
14328     append: false,
14329     loadNext: false,
14330     autoFocus : true,
14331     tickable : false,
14332     btnPosition : 'right',
14333     triggerList : true,
14334     showToggleBtn : true,
14335     animate : true,
14336     emptyResultText: 'Empty',
14337     triggerText : 'Select',
14338     emptyTitle : '',
14339     
14340     // element that contains real text value.. (when hidden is used..)
14341     
14342     getAutoCreate : function()
14343     {   
14344         var cfg = false;
14345         //render
14346         /*
14347          * Render classic select for iso
14348          */
14349         
14350         if(Roo.isIOS && this.useNativeIOS){
14351             cfg = this.getAutoCreateNativeIOS();
14352             return cfg;
14353         }
14354         
14355         /*
14356          * Touch Devices
14357          */
14358         
14359         if(Roo.isTouch && this.mobileTouchView){
14360             cfg = this.getAutoCreateTouchView();
14361             return cfg;;
14362         }
14363         
14364         /*
14365          *  Normal ComboBox
14366          */
14367         if(!this.tickable){
14368             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
14369             return cfg;
14370         }
14371         
14372         /*
14373          *  ComboBox with tickable selections
14374          */
14375              
14376         var align = this.labelAlign || this.parentLabelAlign();
14377         
14378         cfg = {
14379             cls : 'form-group roo-combobox-tickable' //input-group
14380         };
14381         
14382         var btn_text_select = '';
14383         var btn_text_done = '';
14384         var btn_text_cancel = '';
14385         
14386         if (this.btn_text_show) {
14387             btn_text_select = 'Select';
14388             btn_text_done = 'Done';
14389             btn_text_cancel = 'Cancel'; 
14390         }
14391         
14392         var buttons = {
14393             tag : 'div',
14394             cls : 'tickable-buttons',
14395             cn : [
14396                 {
14397                     tag : 'button',
14398                     type : 'button',
14399                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
14400                     //html : this.triggerText
14401                     html: btn_text_select
14402                 },
14403                 {
14404                     tag : 'button',
14405                     type : 'button',
14406                     name : 'ok',
14407                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
14408                     //html : 'Done'
14409                     html: btn_text_done
14410                 },
14411                 {
14412                     tag : 'button',
14413                     type : 'button',
14414                     name : 'cancel',
14415                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
14416                     //html : 'Cancel'
14417                     html: btn_text_cancel
14418                 }
14419             ]
14420         };
14421         
14422         if(this.editable){
14423             buttons.cn.unshift({
14424                 tag: 'input',
14425                 cls: 'roo-select2-search-field-input'
14426             });
14427         }
14428         
14429         var _this = this;
14430         
14431         Roo.each(buttons.cn, function(c){
14432             if (_this.size) {
14433                 c.cls += ' btn-' + _this.size;
14434             }
14435
14436             if (_this.disabled) {
14437                 c.disabled = true;
14438             }
14439         });
14440         
14441         var box = {
14442             tag: 'div',
14443             style : 'display: contents',
14444             cn: [
14445                 {
14446                     tag: 'input',
14447                     type : 'hidden',
14448                     cls: 'form-hidden-field'
14449                 },
14450                 {
14451                     tag: 'ul',
14452                     cls: 'roo-select2-choices',
14453                     cn:[
14454                         {
14455                             tag: 'li',
14456                             cls: 'roo-select2-search-field',
14457                             cn: [
14458                                 buttons
14459                             ]
14460                         }
14461                     ]
14462                 }
14463             ]
14464         };
14465         
14466         var combobox = {
14467             cls: 'roo-select2-container input-group roo-select2-container-multi',
14468             cn: [
14469                 
14470                 box
14471 //                {
14472 //                    tag: 'ul',
14473 //                    cls: 'typeahead typeahead-long dropdown-menu',
14474 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
14475 //                }
14476             ]
14477         };
14478         
14479         if(this.hasFeedback && !this.allowBlank){
14480             
14481             var feedback = {
14482                 tag: 'span',
14483                 cls: 'glyphicon form-control-feedback'
14484             };
14485
14486             combobox.cn.push(feedback);
14487         }
14488         
14489         var indicator = {
14490             tag : 'i',
14491             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
14492             tooltip : 'This field is required'
14493         };
14494         if (Roo.bootstrap.version == 4) {
14495             indicator = {
14496                 tag : 'i',
14497                 style : 'display:none'
14498             };
14499         }
14500         if (align ==='left' && this.fieldLabel.length) {
14501             
14502             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
14503             
14504             cfg.cn = [
14505                 indicator,
14506                 {
14507                     tag: 'label',
14508                     'for' :  id,
14509                     cls : 'control-label col-form-label',
14510                     html : this.fieldLabel
14511
14512                 },
14513                 {
14514                     cls : "", 
14515                     cn: [
14516                         combobox
14517                     ]
14518                 }
14519
14520             ];
14521             
14522             var labelCfg = cfg.cn[1];
14523             var contentCfg = cfg.cn[2];
14524             
14525
14526             if(this.indicatorpos == 'right'){
14527                 
14528                 cfg.cn = [
14529                     {
14530                         tag: 'label',
14531                         'for' :  id,
14532                         cls : 'control-label col-form-label',
14533                         cn : [
14534                             {
14535                                 tag : 'span',
14536                                 html : this.fieldLabel
14537                             },
14538                             indicator
14539                         ]
14540                     },
14541                     {
14542                         cls : "",
14543                         cn: [
14544                             combobox
14545                         ]
14546                     }
14547
14548                 ];
14549                 
14550                 
14551                 
14552                 labelCfg = cfg.cn[0];
14553                 contentCfg = cfg.cn[1];
14554             
14555             }
14556             
14557             if(this.labelWidth > 12){
14558                 labelCfg.style = "width: " + this.labelWidth + 'px';
14559             }
14560             
14561             if(this.labelWidth < 13 && this.labelmd == 0){
14562                 this.labelmd = this.labelWidth;
14563             }
14564             
14565             if(this.labellg > 0){
14566                 labelCfg.cls += ' col-lg-' + this.labellg;
14567                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14568             }
14569             
14570             if(this.labelmd > 0){
14571                 labelCfg.cls += ' col-md-' + this.labelmd;
14572                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14573             }
14574             
14575             if(this.labelsm > 0){
14576                 labelCfg.cls += ' col-sm-' + this.labelsm;
14577                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14578             }
14579             
14580             if(this.labelxs > 0){
14581                 labelCfg.cls += ' col-xs-' + this.labelxs;
14582                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14583             }
14584                 
14585                 
14586         } else if ( this.fieldLabel.length) {
14587 //                Roo.log(" label");
14588                  cfg.cn = [
14589                    indicator,
14590                     {
14591                         tag: 'label',
14592                         //cls : 'input-group-addon',
14593                         html : this.fieldLabel
14594                     },
14595                     combobox
14596                 ];
14597                 
14598                 if(this.indicatorpos == 'right'){
14599                     cfg.cn = [
14600                         {
14601                             tag: 'label',
14602                             //cls : 'input-group-addon',
14603                             html : this.fieldLabel
14604                         },
14605                         indicator,
14606                         combobox
14607                     ];
14608                     
14609                 }
14610
14611         } else {
14612             
14613 //                Roo.log(" no label && no align");
14614                 cfg = combobox
14615                      
14616                 
14617         }
14618          
14619         var settings=this;
14620         ['xs','sm','md','lg'].map(function(size){
14621             if (settings[size]) {
14622                 cfg.cls += ' col-' + size + '-' + settings[size];
14623             }
14624         });
14625         
14626         return cfg;
14627         
14628     },
14629     
14630     _initEventsCalled : false,
14631     
14632     // private
14633     initEvents: function()
14634     {   
14635         if (this._initEventsCalled) { // as we call render... prevent looping...
14636             return;
14637         }
14638         this._initEventsCalled = true;
14639         
14640         if (!this.store) {
14641             throw "can not find store for combo";
14642         }
14643         
14644         this.indicator = this.indicatorEl();
14645         
14646         this.store = Roo.factory(this.store, Roo.data);
14647         this.store.parent = this;
14648         
14649         // if we are building from html. then this element is so complex, that we can not really
14650         // use the rendered HTML.
14651         // so we have to trash and replace the previous code.
14652         if (Roo.XComponent.build_from_html) {
14653             // remove this element....
14654             var e = this.el.dom, k=0;
14655             while (e ) { e = e.previousSibling;  ++k;}
14656
14657             this.el.remove();
14658             
14659             this.el=false;
14660             this.rendered = false;
14661             
14662             this.render(this.parent().getChildContainer(true), k);
14663         }
14664         
14665         if(Roo.isIOS && this.useNativeIOS){
14666             this.initIOSView();
14667             return;
14668         }
14669         
14670         /*
14671          * Touch Devices
14672          */
14673         
14674         if(Roo.isTouch && this.mobileTouchView){
14675             this.initTouchView();
14676             return;
14677         }
14678         
14679         if(this.tickable){
14680             this.initTickableEvents();
14681             return;
14682         }
14683         
14684         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
14685         
14686         if(this.hiddenName){
14687             
14688             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14689             
14690             this.hiddenField.dom.value =
14691                 this.hiddenValue !== undefined ? this.hiddenValue :
14692                 this.value !== undefined ? this.value : '';
14693
14694             // prevent input submission
14695             this.el.dom.removeAttribute('name');
14696             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14697              
14698              
14699         }
14700         //if(Roo.isGecko){
14701         //    this.el.dom.setAttribute('autocomplete', 'off');
14702         //}
14703         
14704         var cls = 'x-combo-list';
14705         
14706         //this.list = new Roo.Layer({
14707         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
14708         //});
14709         
14710         var _this = this;
14711         
14712         (function(){
14713             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
14714             _this.list.setWidth(lw);
14715         }).defer(100);
14716         
14717         this.list.on('mouseover', this.onViewOver, this);
14718         this.list.on('mousemove', this.onViewMove, this);
14719         this.list.on('scroll', this.onViewScroll, this);
14720         
14721         /*
14722         this.list.swallowEvent('mousewheel');
14723         this.assetHeight = 0;
14724
14725         if(this.title){
14726             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
14727             this.assetHeight += this.header.getHeight();
14728         }
14729
14730         this.innerList = this.list.createChild({cls:cls+'-inner'});
14731         this.innerList.on('mouseover', this.onViewOver, this);
14732         this.innerList.on('mousemove', this.onViewMove, this);
14733         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14734         
14735         if(this.allowBlank && !this.pageSize && !this.disableClear){
14736             this.footer = this.list.createChild({cls:cls+'-ft'});
14737             this.pageTb = new Roo.Toolbar(this.footer);
14738            
14739         }
14740         if(this.pageSize){
14741             this.footer = this.list.createChild({cls:cls+'-ft'});
14742             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
14743                     {pageSize: this.pageSize});
14744             
14745         }
14746         
14747         if (this.pageTb && this.allowBlank && !this.disableClear) {
14748             var _this = this;
14749             this.pageTb.add(new Roo.Toolbar.Fill(), {
14750                 cls: 'x-btn-icon x-btn-clear',
14751                 text: '&#160;',
14752                 handler: function()
14753                 {
14754                     _this.collapse();
14755                     _this.clearValue();
14756                     _this.onSelect(false, -1);
14757                 }
14758             });
14759         }
14760         if (this.footer) {
14761             this.assetHeight += this.footer.getHeight();
14762         }
14763         */
14764             
14765         if(!this.tpl){
14766             this.tpl = Roo.bootstrap.version == 4 ?
14767                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
14768                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
14769         }
14770
14771         this.view = new Roo.View(this.list, this.tpl, {
14772             singleSelect:true, store: this.store, selectedClass: this.selectedClass
14773         });
14774         //this.view.wrapEl.setDisplayed(false);
14775         this.view.on('click', this.onViewClick, this);
14776         
14777         
14778         this.store.on('beforeload', this.onBeforeLoad, this);
14779         this.store.on('load', this.onLoad, this);
14780         this.store.on('loadexception', this.onLoadException, this);
14781         /*
14782         if(this.resizable){
14783             this.resizer = new Roo.Resizable(this.list,  {
14784                pinned:true, handles:'se'
14785             });
14786             this.resizer.on('resize', function(r, w, h){
14787                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
14788                 this.listWidth = w;
14789                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
14790                 this.restrictHeight();
14791             }, this);
14792             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
14793         }
14794         */
14795         if(!this.editable){
14796             this.editable = true;
14797             this.setEditable(false);
14798         }
14799         
14800         /*
14801         
14802         if (typeof(this.events.add.listeners) != 'undefined') {
14803             
14804             this.addicon = this.wrap.createChild(
14805                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
14806        
14807             this.addicon.on('click', function(e) {
14808                 this.fireEvent('add', this);
14809             }, this);
14810         }
14811         if (typeof(this.events.edit.listeners) != 'undefined') {
14812             
14813             this.editicon = this.wrap.createChild(
14814                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
14815             if (this.addicon) {
14816                 this.editicon.setStyle('margin-left', '40px');
14817             }
14818             this.editicon.on('click', function(e) {
14819                 
14820                 // we fire even  if inothing is selected..
14821                 this.fireEvent('edit', this, this.lastData );
14822                 
14823             }, this);
14824         }
14825         */
14826         
14827         this.keyNav = new Roo.KeyNav(this.inputEl(), {
14828             "up" : function(e){
14829                 this.inKeyMode = true;
14830                 this.selectPrev();
14831             },
14832
14833             "down" : function(e){
14834                 if(!this.isExpanded()){
14835                     this.onTriggerClick();
14836                 }else{
14837                     this.inKeyMode = true;
14838                     this.selectNext();
14839                 }
14840             },
14841
14842             "enter" : function(e){
14843 //                this.onViewClick();
14844                 //return true;
14845                 this.collapse();
14846                 
14847                 if(this.fireEvent("specialkey", this, e)){
14848                     this.onViewClick(false);
14849                 }
14850                 
14851                 return true;
14852             },
14853
14854             "esc" : function(e){
14855                 this.collapse();
14856             },
14857
14858             "tab" : function(e){
14859                 this.collapse();
14860                 
14861                 if(this.fireEvent("specialkey", this, e)){
14862                     this.onViewClick(false);
14863                 }
14864                 
14865                 return true;
14866             },
14867
14868             scope : this,
14869
14870             doRelay : function(foo, bar, hname){
14871                 if(hname == 'down' || this.scope.isExpanded()){
14872                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14873                 }
14874                 return true;
14875             },
14876
14877             forceKeyDown: true
14878         });
14879         
14880         
14881         this.queryDelay = Math.max(this.queryDelay || 10,
14882                 this.mode == 'local' ? 10 : 250);
14883         
14884         
14885         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14886         
14887         if(this.typeAhead){
14888             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14889         }
14890         if(this.editable !== false){
14891             this.inputEl().on("keyup", this.onKeyUp, this);
14892         }
14893         if(this.forceSelection){
14894             this.inputEl().on('blur', this.doForce, this);
14895         }
14896         
14897         if(this.multiple){
14898             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14899             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14900         }
14901     },
14902     
14903     initTickableEvents: function()
14904     {   
14905         this.createList();
14906         
14907         if(this.hiddenName){
14908             
14909             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14910             
14911             this.hiddenField.dom.value =
14912                 this.hiddenValue !== undefined ? this.hiddenValue :
14913                 this.value !== undefined ? this.value : '';
14914
14915             // prevent input submission
14916             this.el.dom.removeAttribute('name');
14917             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14918              
14919              
14920         }
14921         
14922 //        this.list = this.el.select('ul.dropdown-menu',true).first();
14923         
14924         this.choices = this.el.select('ul.roo-select2-choices', true).first();
14925         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14926         if(this.triggerList){
14927             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
14928         }
14929          
14930         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
14931         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
14932         
14933         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
14934         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
14935         
14936         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
14937         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
14938         
14939         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
14940         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
14941         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
14942         
14943         this.okBtn.hide();
14944         this.cancelBtn.hide();
14945         
14946         var _this = this;
14947         
14948         (function(){
14949             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
14950             _this.list.setWidth(lw);
14951         }).defer(100);
14952         
14953         this.list.on('mouseover', this.onViewOver, this);
14954         this.list.on('mousemove', this.onViewMove, this);
14955         
14956         this.list.on('scroll', this.onViewScroll, this);
14957         
14958         if(!this.tpl){
14959             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
14960                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
14961         }
14962
14963         this.view = new Roo.View(this.list, this.tpl, {
14964             singleSelect:true,
14965             tickable:true,
14966             parent:this,
14967             store: this.store,
14968             selectedClass: this.selectedClass
14969         });
14970         
14971         //this.view.wrapEl.setDisplayed(false);
14972         this.view.on('click', this.onViewClick, this);
14973         
14974         
14975         
14976         this.store.on('beforeload', this.onBeforeLoad, this);
14977         this.store.on('load', this.onLoad, this);
14978         this.store.on('loadexception', this.onLoadException, this);
14979         
14980         if(this.editable){
14981             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
14982                 "up" : function(e){
14983                     this.inKeyMode = true;
14984                     this.selectPrev();
14985                 },
14986
14987                 "down" : function(e){
14988                     this.inKeyMode = true;
14989                     this.selectNext();
14990                 },
14991
14992                 "enter" : function(e){
14993                     if(this.fireEvent("specialkey", this, e)){
14994                         this.onViewClick(false);
14995                     }
14996                     
14997                     return true;
14998                 },
14999
15000                 "esc" : function(e){
15001                     this.onTickableFooterButtonClick(e, false, false);
15002                 },
15003
15004                 "tab" : function(e){
15005                     this.fireEvent("specialkey", this, e);
15006                     
15007                     this.onTickableFooterButtonClick(e, false, false);
15008                     
15009                     return true;
15010                 },
15011
15012                 scope : this,
15013
15014                 doRelay : function(e, fn, key){
15015                     if(this.scope.isExpanded()){
15016                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15017                     }
15018                     return true;
15019                 },
15020
15021                 forceKeyDown: true
15022             });
15023         }
15024         
15025         this.queryDelay = Math.max(this.queryDelay || 10,
15026                 this.mode == 'local' ? 10 : 250);
15027         
15028         
15029         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15030         
15031         if(this.typeAhead){
15032             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15033         }
15034         
15035         if(this.editable !== false){
15036             this.tickableInputEl().on("keyup", this.onKeyUp, this);
15037         }
15038         
15039         this.indicator = this.indicatorEl();
15040         
15041         if(this.indicator){
15042             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15043             this.indicator.hide();
15044         }
15045         
15046     },
15047
15048     onDestroy : function(){
15049         if(this.view){
15050             this.view.setStore(null);
15051             this.view.el.removeAllListeners();
15052             this.view.el.remove();
15053             this.view.purgeListeners();
15054         }
15055         if(this.list){
15056             this.list.dom.innerHTML  = '';
15057         }
15058         
15059         if(this.store){
15060             this.store.un('beforeload', this.onBeforeLoad, this);
15061             this.store.un('load', this.onLoad, this);
15062             this.store.un('loadexception', this.onLoadException, this);
15063         }
15064         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15065     },
15066
15067     // private
15068     fireKey : function(e){
15069         if(e.isNavKeyPress() && !this.list.isVisible()){
15070             this.fireEvent("specialkey", this, e);
15071         }
15072     },
15073
15074     // private
15075     onResize: function(w, h){
15076 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
15077 //        
15078 //        if(typeof w != 'number'){
15079 //            // we do not handle it!?!?
15080 //            return;
15081 //        }
15082 //        var tw = this.trigger.getWidth();
15083 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
15084 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
15085 //        var x = w - tw;
15086 //        this.inputEl().setWidth( this.adjustWidth('input', x));
15087 //            
15088 //        //this.trigger.setStyle('left', x+'px');
15089 //        
15090 //        if(this.list && this.listWidth === undefined){
15091 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
15092 //            this.list.setWidth(lw);
15093 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15094 //        }
15095         
15096     
15097         
15098     },
15099
15100     /**
15101      * Allow or prevent the user from directly editing the field text.  If false is passed,
15102      * the user will only be able to select from the items defined in the dropdown list.  This method
15103      * is the runtime equivalent of setting the 'editable' config option at config time.
15104      * @param {Boolean} value True to allow the user to directly edit the field text
15105      */
15106     setEditable : function(value){
15107         if(value == this.editable){
15108             return;
15109         }
15110         this.editable = value;
15111         if(!value){
15112             this.inputEl().dom.setAttribute('readOnly', true);
15113             this.inputEl().on('mousedown', this.onTriggerClick,  this);
15114             this.inputEl().addClass('x-combo-noedit');
15115         }else{
15116             this.inputEl().dom.setAttribute('readOnly', false);
15117             this.inputEl().un('mousedown', this.onTriggerClick,  this);
15118             this.inputEl().removeClass('x-combo-noedit');
15119         }
15120     },
15121
15122     // private
15123     
15124     onBeforeLoad : function(combo,opts){
15125         if(!this.hasFocus){
15126             return;
15127         }
15128          if (!opts.add) {
15129             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
15130          }
15131         this.restrictHeight();
15132         this.selectedIndex = -1;
15133     },
15134
15135     // private
15136     onLoad : function(){
15137         
15138         this.hasQuery = false;
15139         
15140         if(!this.hasFocus){
15141             return;
15142         }
15143         
15144         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
15145             this.loading.hide();
15146         }
15147         
15148         if(this.store.getCount() > 0){
15149             
15150             this.expand();
15151             this.restrictHeight();
15152             if(this.lastQuery == this.allQuery){
15153                 if(this.editable && !this.tickable){
15154                     this.inputEl().dom.select();
15155                 }
15156                 
15157                 if(
15158                     !this.selectByValue(this.value, true) &&
15159                     this.autoFocus && 
15160                     (
15161                         !this.store.lastOptions ||
15162                         typeof(this.store.lastOptions.add) == 'undefined' || 
15163                         this.store.lastOptions.add != true
15164                     )
15165                 ){
15166                     this.select(0, true);
15167                 }
15168             }else{
15169                 if(this.autoFocus){
15170                     this.selectNext();
15171                 }
15172                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
15173                     this.taTask.delay(this.typeAheadDelay);
15174                 }
15175             }
15176         }else{
15177             this.onEmptyResults();
15178         }
15179         
15180         //this.el.focus();
15181     },
15182     // private
15183     onLoadException : function()
15184     {
15185         this.hasQuery = false;
15186         
15187         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
15188             this.loading.hide();
15189         }
15190         
15191         if(this.tickable && this.editable){
15192             return;
15193         }
15194         
15195         this.collapse();
15196         // only causes errors at present
15197         //Roo.log(this.store.reader.jsonData);
15198         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
15199             // fixme
15200             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
15201         //}
15202         
15203         
15204     },
15205     // private
15206     onTypeAhead : function(){
15207         if(this.store.getCount() > 0){
15208             var r = this.store.getAt(0);
15209             var newValue = r.data[this.displayField];
15210             var len = newValue.length;
15211             var selStart = this.getRawValue().length;
15212             
15213             if(selStart != len){
15214                 this.setRawValue(newValue);
15215                 this.selectText(selStart, newValue.length);
15216             }
15217         }
15218     },
15219
15220     // private
15221     onSelect : function(record, index){
15222         
15223         if(this.fireEvent('beforeselect', this, record, index) !== false){
15224         
15225             this.setFromData(index > -1 ? record.data : false);
15226             
15227             this.collapse();
15228             this.fireEvent('select', this, record, index);
15229         }
15230     },
15231
15232     /**
15233      * Returns the currently selected field value or empty string if no value is set.
15234      * @return {String} value The selected value
15235      */
15236     getValue : function()
15237     {
15238         if(Roo.isIOS && this.useNativeIOS){
15239             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
15240         }
15241         
15242         if(this.multiple){
15243             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
15244         }
15245         
15246         if(this.valueField){
15247             return typeof this.value != 'undefined' ? this.value : '';
15248         }else{
15249             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
15250         }
15251     },
15252     
15253     getRawValue : function()
15254     {
15255         if(Roo.isIOS && this.useNativeIOS){
15256             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
15257         }
15258         
15259         var v = this.inputEl().getValue();
15260         
15261         return v;
15262     },
15263
15264     /**
15265      * Clears any text/value currently set in the field
15266      */
15267     clearValue : function(){
15268         
15269         if(this.hiddenField){
15270             this.hiddenField.dom.value = '';
15271         }
15272         this.value = '';
15273         this.setRawValue('');
15274         this.lastSelectionText = '';
15275         this.lastData = false;
15276         
15277         var close = this.closeTriggerEl();
15278         
15279         if(close){
15280             close.hide();
15281         }
15282         
15283         this.validate();
15284         
15285     },
15286
15287     /**
15288      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
15289      * will be displayed in the field.  If the value does not match the data value of an existing item,
15290      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
15291      * Otherwise the field will be blank (although the value will still be set).
15292      * @param {String} value The value to match
15293      */
15294     setValue : function(v)
15295     {
15296         if(Roo.isIOS && this.useNativeIOS){
15297             this.setIOSValue(v);
15298             return;
15299         }
15300         
15301         if(this.multiple){
15302             this.syncValue();
15303             return;
15304         }
15305         
15306         var text = v;
15307         if(this.valueField){
15308             var r = this.findRecord(this.valueField, v);
15309             if(r){
15310                 text = r.data[this.displayField];
15311             }else if(this.valueNotFoundText !== undefined){
15312                 text = this.valueNotFoundText;
15313             }
15314         }
15315         this.lastSelectionText = text;
15316         if(this.hiddenField){
15317             this.hiddenField.dom.value = v;
15318         }
15319         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
15320         this.value = v;
15321         
15322         var close = this.closeTriggerEl();
15323         
15324         if(close){
15325             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
15326         }
15327         
15328         this.validate();
15329     },
15330     /**
15331      * @property {Object} the last set data for the element
15332      */
15333     
15334     lastData : false,
15335     /**
15336      * Sets the value of the field based on a object which is related to the record format for the store.
15337      * @param {Object} value the value to set as. or false on reset?
15338      */
15339     setFromData : function(o){
15340         
15341         if(this.multiple){
15342             this.addItem(o);
15343             return;
15344         }
15345             
15346         var dv = ''; // display value
15347         var vv = ''; // value value..
15348         this.lastData = o;
15349         if (this.displayField) {
15350             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
15351         } else {
15352             // this is an error condition!!!
15353             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
15354         }
15355         
15356         if(this.valueField){
15357             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
15358         }
15359         
15360         var close = this.closeTriggerEl();
15361         
15362         if(close){
15363             if(dv.length || vv * 1 > 0){
15364                 close.show() ;
15365                 this.blockFocus=true;
15366             } else {
15367                 close.hide();
15368             }             
15369         }
15370         
15371         if(this.hiddenField){
15372             this.hiddenField.dom.value = vv;
15373             
15374             this.lastSelectionText = dv;
15375             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
15376             this.value = vv;
15377             return;
15378         }
15379         // no hidden field.. - we store the value in 'value', but still display
15380         // display field!!!!
15381         this.lastSelectionText = dv;
15382         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
15383         this.value = vv;
15384         
15385         
15386         
15387     },
15388     // private
15389     reset : function(){
15390         // overridden so that last data is reset..
15391         
15392         if(this.multiple){
15393             this.clearItem();
15394             return;
15395         }
15396         
15397         this.setValue(this.originalValue);
15398         //this.clearInvalid();
15399         this.lastData = false;
15400         if (this.view) {
15401             this.view.clearSelections();
15402         }
15403         
15404         this.validate();
15405     },
15406     // private
15407     findRecord : function(prop, value){
15408         var record;
15409         if(this.store.getCount() > 0){
15410             this.store.each(function(r){
15411                 if(r.data[prop] == value){
15412                     record = r;
15413                     return false;
15414                 }
15415                 return true;
15416             });
15417         }
15418         return record;
15419     },
15420     
15421     getName: function()
15422     {
15423         // returns hidden if it's set..
15424         if (!this.rendered) {return ''};
15425         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
15426         
15427     },
15428     // private
15429     onViewMove : function(e, t){
15430         this.inKeyMode = false;
15431     },
15432
15433     // private
15434     onViewOver : function(e, t){
15435         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
15436             return;
15437         }
15438         var item = this.view.findItemFromChild(t);
15439         
15440         if(item){
15441             var index = this.view.indexOf(item);
15442             this.select(index, false);
15443         }
15444     },
15445
15446     // private
15447     onViewClick : function(view, doFocus, el, e)
15448     {
15449         var index = this.view.getSelectedIndexes()[0];
15450         
15451         var r = this.store.getAt(index);
15452         
15453         if(this.tickable){
15454             
15455             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
15456                 return;
15457             }
15458             
15459             var rm = false;
15460             var _this = this;
15461             
15462             Roo.each(this.tickItems, function(v,k){
15463                 
15464                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
15465                     Roo.log(v);
15466                     _this.tickItems.splice(k, 1);
15467                     
15468                     if(typeof(e) == 'undefined' && view == false){
15469                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
15470                     }
15471                     
15472                     rm = true;
15473                     return;
15474                 }
15475             });
15476             
15477             if(rm){
15478                 return;
15479             }
15480             
15481             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
15482                 this.tickItems.push(r.data);
15483             }
15484             
15485             if(typeof(e) == 'undefined' && view == false){
15486                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
15487             }
15488                     
15489             return;
15490         }
15491         
15492         if(r){
15493             this.onSelect(r, index);
15494         }
15495         if(doFocus !== false && !this.blockFocus){
15496             this.inputEl().focus();
15497         }
15498     },
15499
15500     // private
15501     restrictHeight : function(){
15502         //this.innerList.dom.style.height = '';
15503         //var inner = this.innerList.dom;
15504         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
15505         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
15506         //this.list.beginUpdate();
15507         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
15508         this.list.alignTo(this.inputEl(), this.listAlign);
15509         this.list.alignTo(this.inputEl(), this.listAlign);
15510         //this.list.endUpdate();
15511     },
15512
15513     // private
15514     onEmptyResults : function(){
15515         
15516         if(this.tickable && this.editable){
15517             this.hasFocus = false;
15518             this.restrictHeight();
15519             return;
15520         }
15521         
15522         this.collapse();
15523     },
15524
15525     /**
15526      * Returns true if the dropdown list is expanded, else false.
15527      */
15528     isExpanded : function(){
15529         return this.list.isVisible();
15530     },
15531
15532     /**
15533      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
15534      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
15535      * @param {String} value The data value of the item to select
15536      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
15537      * selected item if it is not currently in view (defaults to true)
15538      * @return {Boolean} True if the value matched an item in the list, else false
15539      */
15540     selectByValue : function(v, scrollIntoView){
15541         if(v !== undefined && v !== null){
15542             var r = this.findRecord(this.valueField || this.displayField, v);
15543             if(r){
15544                 this.select(this.store.indexOf(r), scrollIntoView);
15545                 return true;
15546             }
15547         }
15548         return false;
15549     },
15550
15551     /**
15552      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
15553      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
15554      * @param {Number} index The zero-based index of the list item to select
15555      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
15556      * selected item if it is not currently in view (defaults to true)
15557      */
15558     select : function(index, scrollIntoView){
15559         this.selectedIndex = index;
15560         this.view.select(index);
15561         if(scrollIntoView !== false){
15562             var el = this.view.getNode(index);
15563             /*
15564              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
15565              */
15566             if(el){
15567                 this.list.scrollChildIntoView(el, false);
15568             }
15569         }
15570     },
15571
15572     // private
15573     selectNext : function(){
15574         var ct = this.store.getCount();
15575         if(ct > 0){
15576             if(this.selectedIndex == -1){
15577                 this.select(0);
15578             }else if(this.selectedIndex < ct-1){
15579                 this.select(this.selectedIndex+1);
15580             }
15581         }
15582     },
15583
15584     // private
15585     selectPrev : function(){
15586         var ct = this.store.getCount();
15587         if(ct > 0){
15588             if(this.selectedIndex == -1){
15589                 this.select(0);
15590             }else if(this.selectedIndex != 0){
15591                 this.select(this.selectedIndex-1);
15592             }
15593         }
15594     },
15595
15596     // private
15597     onKeyUp : function(e){
15598         if(this.editable !== false && !e.isSpecialKey()){
15599             this.lastKey = e.getKey();
15600             this.dqTask.delay(this.queryDelay);
15601         }
15602     },
15603
15604     // private
15605     validateBlur : function(){
15606         return !this.list || !this.list.isVisible();   
15607     },
15608
15609     // private
15610     initQuery : function(){
15611         
15612         var v = this.getRawValue();
15613         
15614         if(this.tickable && this.editable){
15615             v = this.tickableInputEl().getValue();
15616         }
15617         
15618         this.doQuery(v);
15619     },
15620
15621     // private
15622     doForce : function(){
15623         if(this.inputEl().dom.value.length > 0){
15624             this.inputEl().dom.value =
15625                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
15626              
15627         }
15628     },
15629
15630     /**
15631      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
15632      * query allowing the query action to be canceled if needed.
15633      * @param {String} query The SQL query to execute
15634      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
15635      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
15636      * saved in the current store (defaults to false)
15637      */
15638     doQuery : function(q, forceAll){
15639         
15640         if(q === undefined || q === null){
15641             q = '';
15642         }
15643         var qe = {
15644             query: q,
15645             forceAll: forceAll,
15646             combo: this,
15647             cancel:false
15648         };
15649         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
15650             return false;
15651         }
15652         q = qe.query;
15653         
15654         forceAll = qe.forceAll;
15655         if(forceAll === true || (q.length >= this.minChars)){
15656             
15657             this.hasQuery = true;
15658             
15659             if(this.lastQuery != q || this.alwaysQuery){
15660                 this.lastQuery = q;
15661                 if(this.mode == 'local'){
15662                     this.selectedIndex = -1;
15663                     if(forceAll){
15664                         this.store.clearFilter();
15665                     }else{
15666                         
15667                         if(this.specialFilter){
15668                             this.fireEvent('specialfilter', this);
15669                             this.onLoad();
15670                             return;
15671                         }
15672                         
15673                         this.store.filter(this.displayField, q);
15674                     }
15675                     
15676                     this.store.fireEvent("datachanged", this.store);
15677                     
15678                     this.onLoad();
15679                     
15680                     
15681                 }else{
15682                     
15683                     this.store.baseParams[this.queryParam] = q;
15684                     
15685                     var options = {params : this.getParams(q)};
15686                     
15687                     if(this.loadNext){
15688                         options.add = true;
15689                         options.params.start = this.page * this.pageSize;
15690                     }
15691                     
15692                     this.store.load(options);
15693                     
15694                     /*
15695                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
15696                      *  we should expand the list on onLoad
15697                      *  so command out it
15698                      */
15699 //                    this.expand();
15700                 }
15701             }else{
15702                 this.selectedIndex = -1;
15703                 this.onLoad();   
15704             }
15705         }
15706         
15707         this.loadNext = false;
15708     },
15709     
15710     // private
15711     getParams : function(q){
15712         var p = {};
15713         //p[this.queryParam] = q;
15714         
15715         if(this.pageSize){
15716             p.start = 0;
15717             p.limit = this.pageSize;
15718         }
15719         return p;
15720     },
15721
15722     /**
15723      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
15724      */
15725     collapse : function(){
15726         if(!this.isExpanded()){
15727             return;
15728         }
15729         
15730         this.list.hide();
15731         
15732         this.hasFocus = false;
15733         
15734         if(this.tickable){
15735             this.okBtn.hide();
15736             this.cancelBtn.hide();
15737             this.trigger.show();
15738             
15739             if(this.editable){
15740                 this.tickableInputEl().dom.value = '';
15741                 this.tickableInputEl().blur();
15742             }
15743             
15744         }
15745         
15746         Roo.get(document).un('mousedown', this.collapseIf, this);
15747         Roo.get(document).un('mousewheel', this.collapseIf, this);
15748         if (!this.editable) {
15749             Roo.get(document).un('keydown', this.listKeyPress, this);
15750         }
15751         this.fireEvent('collapse', this);
15752         
15753         this.validate();
15754     },
15755
15756     // private
15757     collapseIf : function(e){
15758         var in_combo  = e.within(this.el);
15759         var in_list =  e.within(this.list);
15760         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
15761         
15762         if (in_combo || in_list || is_list) {
15763             //e.stopPropagation();
15764             return;
15765         }
15766         
15767         if(this.tickable){
15768             this.onTickableFooterButtonClick(e, false, false);
15769         }
15770
15771         this.collapse();
15772         
15773     },
15774
15775     /**
15776      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
15777      */
15778     expand : function(){
15779        
15780         if(this.isExpanded() || !this.hasFocus){
15781             return;
15782         }
15783         
15784         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
15785         this.list.setWidth(lw);
15786         
15787         Roo.log('expand');
15788         
15789         this.list.show();
15790         
15791         this.restrictHeight();
15792         
15793         if(this.tickable){
15794             
15795             this.tickItems = Roo.apply([], this.item);
15796             
15797             this.okBtn.show();
15798             this.cancelBtn.show();
15799             this.trigger.hide();
15800             
15801             if(this.editable){
15802                 this.tickableInputEl().focus();
15803             }
15804             
15805         }
15806         
15807         Roo.get(document).on('mousedown', this.collapseIf, this);
15808         Roo.get(document).on('mousewheel', this.collapseIf, this);
15809         if (!this.editable) {
15810             Roo.get(document).on('keydown', this.listKeyPress, this);
15811         }
15812         
15813         this.fireEvent('expand', this);
15814     },
15815
15816     // private
15817     // Implements the default empty TriggerField.onTriggerClick function
15818     onTriggerClick : function(e)
15819     {
15820         Roo.log('trigger click');
15821         
15822         if(this.disabled || !this.triggerList){
15823             return;
15824         }
15825         
15826         this.page = 0;
15827         this.loadNext = false;
15828         
15829         if(this.isExpanded()){
15830             this.collapse();
15831             if (!this.blockFocus) {
15832                 this.inputEl().focus();
15833             }
15834             
15835         }else {
15836             this.hasFocus = true;
15837             if(this.triggerAction == 'all') {
15838                 this.doQuery(this.allQuery, true);
15839             } else {
15840                 this.doQuery(this.getRawValue());
15841             }
15842             if (!this.blockFocus) {
15843                 this.inputEl().focus();
15844             }
15845         }
15846     },
15847     
15848     onTickableTriggerClick : function(e)
15849     {
15850         if(this.disabled){
15851             return;
15852         }
15853         
15854         this.page = 0;
15855         this.loadNext = false;
15856         this.hasFocus = true;
15857         
15858         if(this.triggerAction == 'all') {
15859             this.doQuery(this.allQuery, true);
15860         } else {
15861             this.doQuery(this.getRawValue());
15862         }
15863     },
15864     
15865     onSearchFieldClick : function(e)
15866     {
15867         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
15868             this.onTickableFooterButtonClick(e, false, false);
15869             return;
15870         }
15871         
15872         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
15873             return;
15874         }
15875         
15876         this.page = 0;
15877         this.loadNext = false;
15878         this.hasFocus = true;
15879         
15880         if(this.triggerAction == 'all') {
15881             this.doQuery(this.allQuery, true);
15882         } else {
15883             this.doQuery(this.getRawValue());
15884         }
15885     },
15886     
15887     listKeyPress : function(e)
15888     {
15889         //Roo.log('listkeypress');
15890         // scroll to first matching element based on key pres..
15891         if (e.isSpecialKey()) {
15892             return false;
15893         }
15894         var k = String.fromCharCode(e.getKey()).toUpperCase();
15895         //Roo.log(k);
15896         var match  = false;
15897         var csel = this.view.getSelectedNodes();
15898         var cselitem = false;
15899         if (csel.length) {
15900             var ix = this.view.indexOf(csel[0]);
15901             cselitem  = this.store.getAt(ix);
15902             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
15903                 cselitem = false;
15904             }
15905             
15906         }
15907         
15908         this.store.each(function(v) { 
15909             if (cselitem) {
15910                 // start at existing selection.
15911                 if (cselitem.id == v.id) {
15912                     cselitem = false;
15913                 }
15914                 return true;
15915             }
15916                 
15917             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
15918                 match = this.store.indexOf(v);
15919                 return false;
15920             }
15921             return true;
15922         }, this);
15923         
15924         if (match === false) {
15925             return true; // no more action?
15926         }
15927         // scroll to?
15928         this.view.select(match);
15929         var sn = Roo.get(this.view.getSelectedNodes()[0]);
15930         sn.scrollIntoView(sn.dom.parentNode, false);
15931     },
15932     
15933     onViewScroll : function(e, t){
15934         
15935         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){
15936             return;
15937         }
15938         
15939         this.hasQuery = true;
15940         
15941         this.loading = this.list.select('.loading', true).first();
15942         
15943         if(this.loading === null){
15944             this.list.createChild({
15945                 tag: 'div',
15946                 cls: 'loading roo-select2-more-results roo-select2-active',
15947                 html: 'Loading more results...'
15948             });
15949             
15950             this.loading = this.list.select('.loading', true).first();
15951             
15952             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
15953             
15954             this.loading.hide();
15955         }
15956         
15957         this.loading.show();
15958         
15959         var _combo = this;
15960         
15961         this.page++;
15962         this.loadNext = true;
15963         
15964         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
15965         
15966         return;
15967     },
15968     
15969     addItem : function(o)
15970     {   
15971         var dv = ''; // display value
15972         
15973         if (this.displayField) {
15974             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
15975         } else {
15976             // this is an error condition!!!
15977             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
15978         }
15979         
15980         if(!dv.length){
15981             return;
15982         }
15983         
15984         var choice = this.choices.createChild({
15985             tag: 'li',
15986             cls: 'roo-select2-search-choice',
15987             cn: [
15988                 {
15989                     tag: 'div',
15990                     html: dv
15991                 },
15992                 {
15993                     tag: 'a',
15994                     href: '#',
15995                     cls: 'roo-select2-search-choice-close fa fa-times',
15996                     tabindex: '-1'
15997                 }
15998             ]
15999             
16000         }, this.searchField);
16001         
16002         var close = choice.select('a.roo-select2-search-choice-close', true).first();
16003         
16004         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16005         
16006         this.item.push(o);
16007         
16008         this.lastData = o;
16009         
16010         this.syncValue();
16011         
16012         this.inputEl().dom.value = '';
16013         
16014         this.validate();
16015     },
16016     
16017     onRemoveItem : function(e, _self, o)
16018     {
16019         e.preventDefault();
16020         
16021         this.lastItem = Roo.apply([], this.item);
16022         
16023         var index = this.item.indexOf(o.data) * 1;
16024         
16025         if( index < 0){
16026             Roo.log('not this item?!');
16027             return;
16028         }
16029         
16030         this.item.splice(index, 1);
16031         o.item.remove();
16032         
16033         this.syncValue();
16034         
16035         this.fireEvent('remove', this, e);
16036         
16037         this.validate();
16038         
16039     },
16040     
16041     syncValue : function()
16042     {
16043         if(!this.item.length){
16044             this.clearValue();
16045             return;
16046         }
16047             
16048         var value = [];
16049         var _this = this;
16050         Roo.each(this.item, function(i){
16051             if(_this.valueField){
16052                 value.push(i[_this.valueField]);
16053                 return;
16054             }
16055
16056             value.push(i);
16057         });
16058
16059         this.value = value.join(',');
16060
16061         if(this.hiddenField){
16062             this.hiddenField.dom.value = this.value;
16063         }
16064         
16065         this.store.fireEvent("datachanged", this.store);
16066         
16067         this.validate();
16068     },
16069     
16070     clearItem : function()
16071     {
16072         if(!this.multiple){
16073             return;
16074         }
16075         
16076         this.item = [];
16077         
16078         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
16079            c.remove();
16080         });
16081         
16082         this.syncValue();
16083         
16084         this.validate();
16085         
16086         if(this.tickable && !Roo.isTouch){
16087             this.view.refresh();
16088         }
16089     },
16090     
16091     inputEl: function ()
16092     {
16093         if(Roo.isIOS && this.useNativeIOS){
16094             return this.el.select('select.roo-ios-select', true).first();
16095         }
16096         
16097         if(Roo.isTouch && this.mobileTouchView){
16098             return this.el.select('input.form-control',true).first();
16099         }
16100         
16101         if(this.tickable){
16102             return this.searchField;
16103         }
16104         
16105         return this.el.select('input.form-control',true).first();
16106     },
16107     
16108     onTickableFooterButtonClick : function(e, btn, el)
16109     {
16110         e.preventDefault();
16111         
16112         this.lastItem = Roo.apply([], this.item);
16113         
16114         if(btn && btn.name == 'cancel'){
16115             this.tickItems = Roo.apply([], this.item);
16116             this.collapse();
16117             return;
16118         }
16119         
16120         this.clearItem();
16121         
16122         var _this = this;
16123         
16124         Roo.each(this.tickItems, function(o){
16125             _this.addItem(o);
16126         });
16127         
16128         this.collapse();
16129         
16130     },
16131     
16132     validate : function()
16133     {
16134         if(this.getVisibilityEl().hasClass('hidden')){
16135             return true;
16136         }
16137         
16138         var v = this.getRawValue();
16139         
16140         if(this.multiple){
16141             v = this.getValue();
16142         }
16143         
16144         if(this.disabled || this.allowBlank || v.length){
16145             this.markValid();
16146             return true;
16147         }
16148         
16149         this.markInvalid();
16150         return false;
16151     },
16152     
16153     tickableInputEl : function()
16154     {
16155         if(!this.tickable || !this.editable){
16156             return this.inputEl();
16157         }
16158         
16159         return this.inputEl().select('.roo-select2-search-field-input', true).first();
16160     },
16161     
16162     
16163     getAutoCreateTouchView : function()
16164     {
16165         var id = Roo.id();
16166         
16167         var cfg = {
16168             cls: 'form-group' //input-group
16169         };
16170         
16171         var input =  {
16172             tag: 'input',
16173             id : id,
16174             type : this.inputType,
16175             cls : 'form-control x-combo-noedit',
16176             autocomplete: 'new-password',
16177             placeholder : this.placeholder || '',
16178             readonly : true
16179         };
16180         
16181         if (this.name) {
16182             input.name = this.name;
16183         }
16184         
16185         if (this.size) {
16186             input.cls += ' input-' + this.size;
16187         }
16188         
16189         if (this.disabled) {
16190             input.disabled = true;
16191         }
16192         
16193         var inputblock = {
16194             cls : '',
16195             cn : [
16196                 input
16197             ]
16198         };
16199         
16200         if(this.before){
16201             inputblock.cls += ' input-group';
16202             
16203             inputblock.cn.unshift({
16204                 tag :'span',
16205                 cls : 'input-group-addon input-group-prepend input-group-text',
16206                 html : this.before
16207             });
16208         }
16209         
16210         if(this.removable && !this.multiple){
16211             inputblock.cls += ' roo-removable';
16212             
16213             inputblock.cn.push({
16214                 tag: 'button',
16215                 html : 'x',
16216                 cls : 'roo-combo-removable-btn close'
16217             });
16218         }
16219
16220         if(this.hasFeedback && !this.allowBlank){
16221             
16222             inputblock.cls += ' has-feedback';
16223             
16224             inputblock.cn.push({
16225                 tag: 'span',
16226                 cls: 'glyphicon form-control-feedback'
16227             });
16228             
16229         }
16230         
16231         if (this.after) {
16232             
16233             inputblock.cls += (this.before) ? '' : ' input-group';
16234             
16235             inputblock.cn.push({
16236                 tag :'span',
16237                 cls : 'input-group-addon input-group-append input-group-text',
16238                 html : this.after
16239             });
16240         }
16241
16242         
16243         var ibwrap = inputblock;
16244         
16245         if(this.multiple){
16246             ibwrap = {
16247                 tag: 'ul',
16248                 cls: 'roo-select2-choices',
16249                 cn:[
16250                     {
16251                         tag: 'li',
16252                         cls: 'roo-select2-search-field',
16253                         cn: [
16254
16255                             inputblock
16256                         ]
16257                     }
16258                 ]
16259             };
16260         
16261             
16262         }
16263         
16264         var combobox = {
16265             cls: 'roo-select2-container input-group roo-touchview-combobox ',
16266             cn: [
16267                 {
16268                     tag: 'input',
16269                     type : 'hidden',
16270                     cls: 'form-hidden-field'
16271                 },
16272                 ibwrap
16273             ]
16274         };
16275         
16276         if(!this.multiple && this.showToggleBtn){
16277             
16278             var caret = {
16279                 cls: 'caret'
16280             };
16281             
16282             if (this.caret != false) {
16283                 caret = {
16284                      tag: 'i',
16285                      cls: 'fa fa-' + this.caret
16286                 };
16287                 
16288             }
16289             
16290             combobox.cn.push({
16291                 tag :'span',
16292                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
16293                 cn : [
16294                     Roo.bootstrap.version == 3 ? caret : '',
16295                     {
16296                         tag: 'span',
16297                         cls: 'combobox-clear',
16298                         cn  : [
16299                             {
16300                                 tag : 'i',
16301                                 cls: 'icon-remove'
16302                             }
16303                         ]
16304                     }
16305                 ]
16306
16307             })
16308         }
16309         
16310         if(this.multiple){
16311             combobox.cls += ' roo-select2-container-multi';
16312         }
16313         
16314         var align = this.labelAlign || this.parentLabelAlign();
16315         
16316         if (align ==='left' && this.fieldLabel.length) {
16317
16318             cfg.cn = [
16319                 {
16320                    tag : 'i',
16321                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
16322                    tooltip : 'This field is required'
16323                 },
16324                 {
16325                     tag: 'label',
16326                     cls : 'control-label col-form-label',
16327                     html : this.fieldLabel
16328
16329                 },
16330                 {
16331                     cls : '', 
16332                     cn: [
16333                         combobox
16334                     ]
16335                 }
16336             ];
16337             
16338             var labelCfg = cfg.cn[1];
16339             var contentCfg = cfg.cn[2];
16340             
16341
16342             if(this.indicatorpos == 'right'){
16343                 cfg.cn = [
16344                     {
16345                         tag: 'label',
16346                         'for' :  id,
16347                         cls : 'control-label col-form-label',
16348                         cn : [
16349                             {
16350                                 tag : 'span',
16351                                 html : this.fieldLabel
16352                             },
16353                             {
16354                                 tag : 'i',
16355                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
16356                                 tooltip : 'This field is required'
16357                             }
16358                         ]
16359                     },
16360                     {
16361                         cls : "",
16362                         cn: [
16363                             combobox
16364                         ]
16365                     }
16366
16367                 ];
16368                 
16369                 labelCfg = cfg.cn[0];
16370                 contentCfg = cfg.cn[1];
16371             }
16372             
16373            
16374             
16375             if(this.labelWidth > 12){
16376                 labelCfg.style = "width: " + this.labelWidth + 'px';
16377             }
16378             
16379             if(this.labelWidth < 13 && this.labelmd == 0){
16380                 this.labelmd = this.labelWidth;
16381             }
16382             
16383             if(this.labellg > 0){
16384                 labelCfg.cls += ' col-lg-' + this.labellg;
16385                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
16386             }
16387             
16388             if(this.labelmd > 0){
16389                 labelCfg.cls += ' col-md-' + this.labelmd;
16390                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
16391             }
16392             
16393             if(this.labelsm > 0){
16394                 labelCfg.cls += ' col-sm-' + this.labelsm;
16395                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
16396             }
16397             
16398             if(this.labelxs > 0){
16399                 labelCfg.cls += ' col-xs-' + this.labelxs;
16400                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
16401             }
16402                 
16403                 
16404         } else if ( this.fieldLabel.length) {
16405             cfg.cn = [
16406                 {
16407                    tag : 'i',
16408                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
16409                    tooltip : 'This field is required'
16410                 },
16411                 {
16412                     tag: 'label',
16413                     cls : 'control-label',
16414                     html : this.fieldLabel
16415
16416                 },
16417                 {
16418                     cls : '', 
16419                     cn: [
16420                         combobox
16421                     ]
16422                 }
16423             ];
16424             
16425             if(this.indicatorpos == 'right'){
16426                 cfg.cn = [
16427                     {
16428                         tag: 'label',
16429                         cls : 'control-label',
16430                         html : this.fieldLabel,
16431                         cn : [
16432                             {
16433                                tag : 'i',
16434                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
16435                                tooltip : 'This field is required'
16436                             }
16437                         ]
16438                     },
16439                     {
16440                         cls : '', 
16441                         cn: [
16442                             combobox
16443                         ]
16444                     }
16445                 ];
16446             }
16447         } else {
16448             cfg.cn = combobox;    
16449         }
16450         
16451         
16452         var settings = this;
16453         
16454         ['xs','sm','md','lg'].map(function(size){
16455             if (settings[size]) {
16456                 cfg.cls += ' col-' + size + '-' + settings[size];
16457             }
16458         });
16459         
16460         return cfg;
16461     },
16462     
16463     initTouchView : function()
16464     {
16465         this.renderTouchView();
16466         
16467         this.touchViewEl.on('scroll', function(){
16468             this.el.dom.scrollTop = 0;
16469         }, this);
16470         
16471         this.originalValue = this.getValue();
16472         
16473         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
16474         
16475         this.inputEl().on("click", this.showTouchView, this);
16476         if (this.triggerEl) {
16477             this.triggerEl.on("click", this.showTouchView, this);
16478         }
16479         
16480         
16481         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
16482         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
16483         
16484         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
16485         
16486         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
16487         this.store.on('load', this.onTouchViewLoad, this);
16488         this.store.on('loadexception', this.onTouchViewLoadException, this);
16489         
16490         if(this.hiddenName){
16491             
16492             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16493             
16494             this.hiddenField.dom.value =
16495                 this.hiddenValue !== undefined ? this.hiddenValue :
16496                 this.value !== undefined ? this.value : '';
16497         
16498             this.el.dom.removeAttribute('name');
16499             this.hiddenField.dom.setAttribute('name', this.hiddenName);
16500         }
16501         
16502         if(this.multiple){
16503             this.choices = this.el.select('ul.roo-select2-choices', true).first();
16504             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16505         }
16506         
16507         if(this.removable && !this.multiple){
16508             var close = this.closeTriggerEl();
16509             if(close){
16510                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
16511                 close.on('click', this.removeBtnClick, this, close);
16512             }
16513         }
16514         /*
16515          * fix the bug in Safari iOS8
16516          */
16517         this.inputEl().on("focus", function(e){
16518             document.activeElement.blur();
16519         }, this);
16520         
16521         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
16522         
16523         return;
16524         
16525         
16526     },
16527     
16528     renderTouchView : function()
16529     {
16530         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
16531         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16532         
16533         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
16534         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16535         
16536         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
16537         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16538         this.touchViewBodyEl.setStyle('overflow', 'auto');
16539         
16540         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
16541         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16542         
16543         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
16544         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16545         
16546     },
16547     
16548     showTouchView : function()
16549     {
16550         if(this.disabled){
16551             return;
16552         }
16553         
16554         this.touchViewHeaderEl.hide();
16555
16556         if(this.modalTitle.length){
16557             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
16558             this.touchViewHeaderEl.show();
16559         }
16560
16561         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
16562         this.touchViewEl.show();
16563
16564         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
16565         
16566         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
16567         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
16568
16569         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
16570
16571         if(this.modalTitle.length){
16572             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
16573         }
16574         
16575         this.touchViewBodyEl.setHeight(bodyHeight);
16576
16577         if(this.animate){
16578             var _this = this;
16579             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
16580         }else{
16581             this.touchViewEl.addClass('in');
16582         }
16583         
16584         if(this._touchViewMask){
16585             Roo.get(document.body).addClass("x-body-masked");
16586             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
16587             this._touchViewMask.setStyle('z-index', 10000);
16588             this._touchViewMask.addClass('show');
16589         }
16590         
16591         this.doTouchViewQuery();
16592         
16593     },
16594     
16595     hideTouchView : function()
16596     {
16597         this.touchViewEl.removeClass('in');
16598
16599         if(this.animate){
16600             var _this = this;
16601             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
16602         }else{
16603             this.touchViewEl.setStyle('display', 'none');
16604         }
16605         
16606         if(this._touchViewMask){
16607             this._touchViewMask.removeClass('show');
16608             Roo.get(document.body).removeClass("x-body-masked");
16609         }
16610     },
16611     
16612     setTouchViewValue : function()
16613     {
16614         if(this.multiple){
16615             this.clearItem();
16616         
16617             var _this = this;
16618
16619             Roo.each(this.tickItems, function(o){
16620                 this.addItem(o);
16621             }, this);
16622         }
16623         
16624         this.hideTouchView();
16625     },
16626     
16627     doTouchViewQuery : function()
16628     {
16629         var qe = {
16630             query: '',
16631             forceAll: true,
16632             combo: this,
16633             cancel:false
16634         };
16635         
16636         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
16637             return false;
16638         }
16639         
16640         if(!this.alwaysQuery || this.mode == 'local'){
16641             this.onTouchViewLoad();
16642             return;
16643         }
16644         
16645         this.store.load();
16646     },
16647     
16648     onTouchViewBeforeLoad : function(combo,opts)
16649     {
16650         return;
16651     },
16652
16653     // private
16654     onTouchViewLoad : function()
16655     {
16656         if(this.store.getCount() < 1){
16657             this.onTouchViewEmptyResults();
16658             return;
16659         }
16660         
16661         this.clearTouchView();
16662         
16663         var rawValue = this.getRawValue();
16664         
16665         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
16666         
16667         this.tickItems = [];
16668         
16669         this.store.data.each(function(d, rowIndex){
16670             var row = this.touchViewListGroup.createChild(template);
16671             
16672             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
16673                 row.addClass(d.data.cls);
16674             }
16675             
16676             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
16677                 var cfg = {
16678                     data : d.data,
16679                     html : d.data[this.displayField]
16680                 };
16681                 
16682                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
16683                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
16684                 }
16685             }
16686             row.removeClass('selected');
16687             if(!this.multiple && this.valueField &&
16688                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
16689             {
16690                 // radio buttons..
16691                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16692                 row.addClass('selected');
16693             }
16694             
16695             if(this.multiple && this.valueField &&
16696                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
16697             {
16698                 
16699                 // checkboxes...
16700                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16701                 this.tickItems.push(d.data);
16702             }
16703             
16704             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
16705             
16706         }, this);
16707         
16708         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
16709         
16710         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
16711
16712         if(this.modalTitle.length){
16713             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
16714         }
16715
16716         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
16717         
16718         if(this.mobile_restrict_height && listHeight < bodyHeight){
16719             this.touchViewBodyEl.setHeight(listHeight);
16720         }
16721         
16722         var _this = this;
16723         
16724         if(firstChecked && listHeight > bodyHeight){
16725             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
16726         }
16727         
16728     },
16729     
16730     onTouchViewLoadException : function()
16731     {
16732         this.hideTouchView();
16733     },
16734     
16735     onTouchViewEmptyResults : function()
16736     {
16737         this.clearTouchView();
16738         
16739         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
16740         
16741         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
16742         
16743     },
16744     
16745     clearTouchView : function()
16746     {
16747         this.touchViewListGroup.dom.innerHTML = '';
16748     },
16749     
16750     onTouchViewClick : function(e, el, o)
16751     {
16752         e.preventDefault();
16753         
16754         var row = o.row;
16755         var rowIndex = o.rowIndex;
16756         
16757         var r = this.store.getAt(rowIndex);
16758         
16759         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
16760             
16761             if(!this.multiple){
16762                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
16763                     c.dom.removeAttribute('checked');
16764                 }, this);
16765
16766                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16767
16768                 this.setFromData(r.data);
16769
16770                 var close = this.closeTriggerEl();
16771
16772                 if(close){
16773                     close.show();
16774                 }
16775
16776                 this.hideTouchView();
16777
16778                 this.fireEvent('select', this, r, rowIndex);
16779
16780                 return;
16781             }
16782
16783             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
16784                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
16785                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
16786                 return;
16787             }
16788
16789             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16790             this.addItem(r.data);
16791             this.tickItems.push(r.data);
16792         }
16793     },
16794     
16795     getAutoCreateNativeIOS : function()
16796     {
16797         var cfg = {
16798             cls: 'form-group' //input-group,
16799         };
16800         
16801         var combobox =  {
16802             tag: 'select',
16803             cls : 'roo-ios-select'
16804         };
16805         
16806         if (this.name) {
16807             combobox.name = this.name;
16808         }
16809         
16810         if (this.disabled) {
16811             combobox.disabled = true;
16812         }
16813         
16814         var settings = this;
16815         
16816         ['xs','sm','md','lg'].map(function(size){
16817             if (settings[size]) {
16818                 cfg.cls += ' col-' + size + '-' + settings[size];
16819             }
16820         });
16821         
16822         cfg.cn = combobox;
16823         
16824         return cfg;
16825         
16826     },
16827     
16828     initIOSView : function()
16829     {
16830         this.store.on('load', this.onIOSViewLoad, this);
16831         
16832         return;
16833     },
16834     
16835     onIOSViewLoad : function()
16836     {
16837         if(this.store.getCount() < 1){
16838             return;
16839         }
16840         
16841         this.clearIOSView();
16842         
16843         if(this.allowBlank) {
16844             
16845             var default_text = '-- SELECT --';
16846             
16847             if(this.placeholder.length){
16848                 default_text = this.placeholder;
16849             }
16850             
16851             if(this.emptyTitle.length){
16852                 default_text += ' - ' + this.emptyTitle + ' -';
16853             }
16854             
16855             var opt = this.inputEl().createChild({
16856                 tag: 'option',
16857                 value : 0,
16858                 html : default_text
16859             });
16860             
16861             var o = {};
16862             o[this.valueField] = 0;
16863             o[this.displayField] = default_text;
16864             
16865             this.ios_options.push({
16866                 data : o,
16867                 el : opt
16868             });
16869             
16870         }
16871         
16872         this.store.data.each(function(d, rowIndex){
16873             
16874             var html = '';
16875             
16876             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
16877                 html = d.data[this.displayField];
16878             }
16879             
16880             var value = '';
16881             
16882             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
16883                 value = d.data[this.valueField];
16884             }
16885             
16886             var option = {
16887                 tag: 'option',
16888                 value : value,
16889                 html : html
16890             };
16891             
16892             if(this.value == d.data[this.valueField]){
16893                 option['selected'] = true;
16894             }
16895             
16896             var opt = this.inputEl().createChild(option);
16897             
16898             this.ios_options.push({
16899                 data : d.data,
16900                 el : opt
16901             });
16902             
16903         }, this);
16904         
16905         this.inputEl().on('change', function(){
16906            this.fireEvent('select', this);
16907         }, this);
16908         
16909     },
16910     
16911     clearIOSView: function()
16912     {
16913         this.inputEl().dom.innerHTML = '';
16914         
16915         this.ios_options = [];
16916     },
16917     
16918     setIOSValue: function(v)
16919     {
16920         this.value = v;
16921         
16922         if(!this.ios_options){
16923             return;
16924         }
16925         
16926         Roo.each(this.ios_options, function(opts){
16927            
16928            opts.el.dom.removeAttribute('selected');
16929            
16930            if(opts.data[this.valueField] != v){
16931                return;
16932            }
16933            
16934            opts.el.dom.setAttribute('selected', true);
16935            
16936         }, this);
16937     }
16938
16939     /** 
16940     * @cfg {Boolean} grow 
16941     * @hide 
16942     */
16943     /** 
16944     * @cfg {Number} growMin 
16945     * @hide 
16946     */
16947     /** 
16948     * @cfg {Number} growMax 
16949     * @hide 
16950     */
16951     /**
16952      * @hide
16953      * @method autoSize
16954      */
16955 });
16956
16957 Roo.apply(Roo.bootstrap.ComboBox,  {
16958     
16959     header : {
16960         tag: 'div',
16961         cls: 'modal-header',
16962         cn: [
16963             {
16964                 tag: 'h4',
16965                 cls: 'modal-title'
16966             }
16967         ]
16968     },
16969     
16970     body : {
16971         tag: 'div',
16972         cls: 'modal-body',
16973         cn: [
16974             {
16975                 tag: 'ul',
16976                 cls: 'list-group'
16977             }
16978         ]
16979     },
16980     
16981     listItemRadio : {
16982         tag: 'li',
16983         cls: 'list-group-item',
16984         cn: [
16985             {
16986                 tag: 'span',
16987                 cls: 'roo-combobox-list-group-item-value'
16988             },
16989             {
16990                 tag: 'div',
16991                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
16992                 cn: [
16993                     {
16994                         tag: 'input',
16995                         type: 'radio'
16996                     },
16997                     {
16998                         tag: 'label'
16999                     }
17000                 ]
17001             }
17002         ]
17003     },
17004     
17005     listItemCheckbox : {
17006         tag: 'li',
17007         cls: 'list-group-item',
17008         cn: [
17009             {
17010                 tag: 'span',
17011                 cls: 'roo-combobox-list-group-item-value'
17012             },
17013             {
17014                 tag: 'div',
17015                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17016                 cn: [
17017                     {
17018                         tag: 'input',
17019                         type: 'checkbox'
17020                     },
17021                     {
17022                         tag: 'label'
17023                     }
17024                 ]
17025             }
17026         ]
17027     },
17028     
17029     emptyResult : {
17030         tag: 'div',
17031         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17032     },
17033     
17034     footer : {
17035         tag: 'div',
17036         cls: 'modal-footer',
17037         cn: [
17038             {
17039                 tag: 'div',
17040                 cls: 'row',
17041                 cn: [
17042                     {
17043                         tag: 'div',
17044                         cls: 'col-xs-6 text-left',
17045                         cn: {
17046                             tag: 'button',
17047                             cls: 'btn btn-danger roo-touch-view-cancel',
17048                             html: 'Cancel'
17049                         }
17050                     },
17051                     {
17052                         tag: 'div',
17053                         cls: 'col-xs-6 text-right',
17054                         cn: {
17055                             tag: 'button',
17056                             cls: 'btn btn-success roo-touch-view-ok',
17057                             html: 'OK'
17058                         }
17059                     }
17060                 ]
17061             }
17062         ]
17063         
17064     }
17065 });
17066
17067 Roo.apply(Roo.bootstrap.ComboBox,  {
17068     
17069     touchViewTemplate : {
17070         tag: 'div',
17071         cls: 'modal fade roo-combobox-touch-view',
17072         cn: [
17073             {
17074                 tag: 'div',
17075                 cls: 'modal-dialog',
17076                 style : 'position:fixed', // we have to fix position....
17077                 cn: [
17078                     {
17079                         tag: 'div',
17080                         cls: 'modal-content',
17081                         cn: [
17082                             Roo.bootstrap.ComboBox.header,
17083                             Roo.bootstrap.ComboBox.body,
17084                             Roo.bootstrap.ComboBox.footer
17085                         ]
17086                     }
17087                 ]
17088             }
17089         ]
17090     }
17091 });/*
17092  * Based on:
17093  * Ext JS Library 1.1.1
17094  * Copyright(c) 2006-2007, Ext JS, LLC.
17095  *
17096  * Originally Released Under LGPL - original licence link has changed is not relivant.
17097  *
17098  * Fork - LGPL
17099  * <script type="text/javascript">
17100  */
17101
17102 /**
17103  * @class Roo.View
17104  * @extends Roo.util.Observable
17105  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
17106  * This class also supports single and multi selection modes. <br>
17107  * Create a data model bound view:
17108  <pre><code>
17109  var store = new Roo.data.Store(...);
17110
17111  var view = new Roo.View({
17112     el : "my-element",
17113     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
17114  
17115     singleSelect: true,
17116     selectedClass: "ydataview-selected",
17117     store: store
17118  });
17119
17120  // listen for node click?
17121  view.on("click", function(vw, index, node, e){
17122  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
17123  });
17124
17125  // load XML data
17126  dataModel.load("foobar.xml");
17127  </code></pre>
17128  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
17129  * <br><br>
17130  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
17131  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
17132  * 
17133  * Note: old style constructor is still suported (container, template, config)
17134  * 
17135  * @constructor
17136  * Create a new View
17137  * @param {Object} config The config object
17138  * 
17139  */
17140 Roo.View = function(config, depreciated_tpl, depreciated_config){
17141     
17142     this.parent = false;
17143     
17144     if (typeof(depreciated_tpl) == 'undefined') {
17145         // new way.. - universal constructor.
17146         Roo.apply(this, config);
17147         this.el  = Roo.get(this.el);
17148     } else {
17149         // old format..
17150         this.el  = Roo.get(config);
17151         this.tpl = depreciated_tpl;
17152         Roo.apply(this, depreciated_config);
17153     }
17154     this.wrapEl  = this.el.wrap().wrap();
17155     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
17156     
17157     
17158     if(typeof(this.tpl) == "string"){
17159         this.tpl = new Roo.Template(this.tpl);
17160     } else {
17161         // support xtype ctors..
17162         this.tpl = new Roo.factory(this.tpl, Roo);
17163     }
17164     
17165     
17166     this.tpl.compile();
17167     
17168     /** @private */
17169     this.addEvents({
17170         /**
17171          * @event beforeclick
17172          * Fires before a click is processed. Returns false to cancel the default action.
17173          * @param {Roo.View} this
17174          * @param {Number} index The index of the target node
17175          * @param {HTMLElement} node The target node
17176          * @param {Roo.EventObject} e The raw event object
17177          */
17178             "beforeclick" : true,
17179         /**
17180          * @event click
17181          * Fires when a template node is clicked.
17182          * @param {Roo.View} this
17183          * @param {Number} index The index of the target node
17184          * @param {HTMLElement} node The target node
17185          * @param {Roo.EventObject} e The raw event object
17186          */
17187             "click" : true,
17188         /**
17189          * @event dblclick
17190          * Fires when a template node is double clicked.
17191          * @param {Roo.View} this
17192          * @param {Number} index The index of the target node
17193          * @param {HTMLElement} node The target node
17194          * @param {Roo.EventObject} e The raw event object
17195          */
17196             "dblclick" : true,
17197         /**
17198          * @event contextmenu
17199          * Fires when a template node is right clicked.
17200          * @param {Roo.View} this
17201          * @param {Number} index The index of the target node
17202          * @param {HTMLElement} node The target node
17203          * @param {Roo.EventObject} e The raw event object
17204          */
17205             "contextmenu" : true,
17206         /**
17207          * @event selectionchange
17208          * Fires when the selected nodes change.
17209          * @param {Roo.View} this
17210          * @param {Array} selections Array of the selected nodes
17211          */
17212             "selectionchange" : true,
17213     
17214         /**
17215          * @event beforeselect
17216          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
17217          * @param {Roo.View} this
17218          * @param {HTMLElement} node The node to be selected
17219          * @param {Array} selections Array of currently selected nodes
17220          */
17221             "beforeselect" : true,
17222         /**
17223          * @event preparedata
17224          * Fires on every row to render, to allow you to change the data.
17225          * @param {Roo.View} this
17226          * @param {Object} data to be rendered (change this)
17227          */
17228           "preparedata" : true
17229           
17230           
17231         });
17232
17233
17234
17235     this.el.on({
17236         "click": this.onClick,
17237         "dblclick": this.onDblClick,
17238         "contextmenu": this.onContextMenu,
17239         scope:this
17240     });
17241
17242     this.selections = [];
17243     this.nodes = [];
17244     this.cmp = new Roo.CompositeElementLite([]);
17245     if(this.store){
17246         this.store = Roo.factory(this.store, Roo.data);
17247         this.setStore(this.store, true);
17248     }
17249     
17250     if ( this.footer && this.footer.xtype) {
17251            
17252          var fctr = this.wrapEl.appendChild(document.createElement("div"));
17253         
17254         this.footer.dataSource = this.store;
17255         this.footer.container = fctr;
17256         this.footer = Roo.factory(this.footer, Roo);
17257         fctr.insertFirst(this.el);
17258         
17259         // this is a bit insane - as the paging toolbar seems to detach the el..
17260 //        dom.parentNode.parentNode.parentNode
17261          // they get detached?
17262     }
17263     
17264     
17265     Roo.View.superclass.constructor.call(this);
17266     
17267     
17268 };
17269
17270 Roo.extend(Roo.View, Roo.util.Observable, {
17271     
17272      /**
17273      * @cfg {Roo.data.Store} store Data store to load data from.
17274      */
17275     store : false,
17276     
17277     /**
17278      * @cfg {String|Roo.Element} el The container element.
17279      */
17280     el : '',
17281     
17282     /**
17283      * @cfg {String|Roo.Template} tpl The template used by this View 
17284      */
17285     tpl : false,
17286     /**
17287      * @cfg {String} dataName the named area of the template to use as the data area
17288      *                          Works with domtemplates roo-name="name"
17289      */
17290     dataName: false,
17291     /**
17292      * @cfg {String} selectedClass The css class to add to selected nodes
17293      */
17294     selectedClass : "x-view-selected",
17295      /**
17296      * @cfg {String} emptyText The empty text to show when nothing is loaded.
17297      */
17298     emptyText : "",
17299     
17300     /**
17301      * @cfg {String} text to display on mask (default Loading)
17302      */
17303     mask : false,
17304     /**
17305      * @cfg {Boolean} multiSelect Allow multiple selection
17306      */
17307     multiSelect : false,
17308     /**
17309      * @cfg {Boolean} singleSelect Allow single selection
17310      */
17311     singleSelect:  false,
17312     
17313     /**
17314      * @cfg {Boolean} toggleSelect - selecting 
17315      */
17316     toggleSelect : false,
17317     
17318     /**
17319      * @cfg {Boolean} tickable - selecting 
17320      */
17321     tickable : false,
17322     
17323     /**
17324      * Returns the element this view is bound to.
17325      * @return {Roo.Element}
17326      */
17327     getEl : function(){
17328         return this.wrapEl;
17329     },
17330     
17331     
17332
17333     /**
17334      * Refreshes the view. - called by datachanged on the store. - do not call directly.
17335      */
17336     refresh : function(){
17337         //Roo.log('refresh');
17338         var t = this.tpl;
17339         
17340         // if we are using something like 'domtemplate', then
17341         // the what gets used is:
17342         // t.applySubtemplate(NAME, data, wrapping data..)
17343         // the outer template then get' applied with
17344         //     the store 'extra data'
17345         // and the body get's added to the
17346         //      roo-name="data" node?
17347         //      <span class='roo-tpl-{name}'></span> ?????
17348         
17349         
17350         
17351         this.clearSelections();
17352         this.el.update("");
17353         var html = [];
17354         var records = this.store.getRange();
17355         if(records.length < 1) {
17356             
17357             // is this valid??  = should it render a template??
17358             
17359             this.el.update(this.emptyText);
17360             return;
17361         }
17362         var el = this.el;
17363         if (this.dataName) {
17364             this.el.update(t.apply(this.store.meta)); //????
17365             el = this.el.child('.roo-tpl-' + this.dataName);
17366         }
17367         
17368         for(var i = 0, len = records.length; i < len; i++){
17369             var data = this.prepareData(records[i].data, i, records[i]);
17370             this.fireEvent("preparedata", this, data, i, records[i]);
17371             
17372             var d = Roo.apply({}, data);
17373             
17374             if(this.tickable){
17375                 Roo.apply(d, {'roo-id' : Roo.id()});
17376                 
17377                 var _this = this;
17378             
17379                 Roo.each(this.parent.item, function(item){
17380                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
17381                         return;
17382                     }
17383                     Roo.apply(d, {'roo-data-checked' : 'checked'});
17384                 });
17385             }
17386             
17387             html[html.length] = Roo.util.Format.trim(
17388                 this.dataName ?
17389                     t.applySubtemplate(this.dataName, d, this.store.meta) :
17390                     t.apply(d)
17391             );
17392         }
17393         
17394         
17395         
17396         el.update(html.join(""));
17397         this.nodes = el.dom.childNodes;
17398         this.updateIndexes(0);
17399     },
17400     
17401
17402     /**
17403      * Function to override to reformat the data that is sent to
17404      * the template for each node.
17405      * DEPRICATED - use the preparedata event handler.
17406      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
17407      * a JSON object for an UpdateManager bound view).
17408      */
17409     prepareData : function(data, index, record)
17410     {
17411         this.fireEvent("preparedata", this, data, index, record);
17412         return data;
17413     },
17414
17415     onUpdate : function(ds, record){
17416         // Roo.log('on update');   
17417         this.clearSelections();
17418         var index = this.store.indexOf(record);
17419         var n = this.nodes[index];
17420         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
17421         n.parentNode.removeChild(n);
17422         this.updateIndexes(index, index);
17423     },
17424
17425     
17426     
17427 // --------- FIXME     
17428     onAdd : function(ds, records, index)
17429     {
17430         //Roo.log(['on Add', ds, records, index] );        
17431         this.clearSelections();
17432         if(this.nodes.length == 0){
17433             this.refresh();
17434             return;
17435         }
17436         var n = this.nodes[index];
17437         for(var i = 0, len = records.length; i < len; i++){
17438             var d = this.prepareData(records[i].data, i, records[i]);
17439             if(n){
17440                 this.tpl.insertBefore(n, d);
17441             }else{
17442                 
17443                 this.tpl.append(this.el, d);
17444             }
17445         }
17446         this.updateIndexes(index);
17447     },
17448
17449     onRemove : function(ds, record, index){
17450        // Roo.log('onRemove');
17451         this.clearSelections();
17452         var el = this.dataName  ?
17453             this.el.child('.roo-tpl-' + this.dataName) :
17454             this.el; 
17455         
17456         el.dom.removeChild(this.nodes[index]);
17457         this.updateIndexes(index);
17458     },
17459
17460     /**
17461      * Refresh an individual node.
17462      * @param {Number} index
17463      */
17464     refreshNode : function(index){
17465         this.onUpdate(this.store, this.store.getAt(index));
17466     },
17467
17468     updateIndexes : function(startIndex, endIndex){
17469         var ns = this.nodes;
17470         startIndex = startIndex || 0;
17471         endIndex = endIndex || ns.length - 1;
17472         for(var i = startIndex; i <= endIndex; i++){
17473             ns[i].nodeIndex = i;
17474         }
17475     },
17476
17477     /**
17478      * Changes the data store this view uses and refresh the view.
17479      * @param {Store} store
17480      */
17481     setStore : function(store, initial){
17482         if(!initial && this.store){
17483             this.store.un("datachanged", this.refresh);
17484             this.store.un("add", this.onAdd);
17485             this.store.un("remove", this.onRemove);
17486             this.store.un("update", this.onUpdate);
17487             this.store.un("clear", this.refresh);
17488             this.store.un("beforeload", this.onBeforeLoad);
17489             this.store.un("load", this.onLoad);
17490             this.store.un("loadexception", this.onLoad);
17491         }
17492         if(store){
17493           
17494             store.on("datachanged", this.refresh, this);
17495             store.on("add", this.onAdd, this);
17496             store.on("remove", this.onRemove, this);
17497             store.on("update", this.onUpdate, this);
17498             store.on("clear", this.refresh, this);
17499             store.on("beforeload", this.onBeforeLoad, this);
17500             store.on("load", this.onLoad, this);
17501             store.on("loadexception", this.onLoad, this);
17502         }
17503         
17504         if(store){
17505             this.refresh();
17506         }
17507     },
17508     /**
17509      * onbeforeLoad - masks the loading area.
17510      *
17511      */
17512     onBeforeLoad : function(store,opts)
17513     {
17514          //Roo.log('onBeforeLoad');   
17515         if (!opts.add) {
17516             this.el.update("");
17517         }
17518         this.el.mask(this.mask ? this.mask : "Loading" ); 
17519     },
17520     onLoad : function ()
17521     {
17522         this.el.unmask();
17523     },
17524     
17525
17526     /**
17527      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
17528      * @param {HTMLElement} node
17529      * @return {HTMLElement} The template node
17530      */
17531     findItemFromChild : function(node){
17532         var el = this.dataName  ?
17533             this.el.child('.roo-tpl-' + this.dataName,true) :
17534             this.el.dom; 
17535         
17536         if(!node || node.parentNode == el){
17537                     return node;
17538             }
17539             var p = node.parentNode;
17540             while(p && p != el){
17541             if(p.parentNode == el){
17542                 return p;
17543             }
17544             p = p.parentNode;
17545         }
17546             return null;
17547     },
17548
17549     /** @ignore */
17550     onClick : function(e){
17551         var item = this.findItemFromChild(e.getTarget());
17552         if(item){
17553             var index = this.indexOf(item);
17554             if(this.onItemClick(item, index, e) !== false){
17555                 this.fireEvent("click", this, index, item, e);
17556             }
17557         }else{
17558             this.clearSelections();
17559         }
17560     },
17561
17562     /** @ignore */
17563     onContextMenu : function(e){
17564         var item = this.findItemFromChild(e.getTarget());
17565         if(item){
17566             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
17567         }
17568     },
17569
17570     /** @ignore */
17571     onDblClick : function(e){
17572         var item = this.findItemFromChild(e.getTarget());
17573         if(item){
17574             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
17575         }
17576     },
17577
17578     onItemClick : function(item, index, e)
17579     {
17580         if(this.fireEvent("beforeclick", this, index, item, e) === false){
17581             return false;
17582         }
17583         if (this.toggleSelect) {
17584             var m = this.isSelected(item) ? 'unselect' : 'select';
17585             //Roo.log(m);
17586             var _t = this;
17587             _t[m](item, true, false);
17588             return true;
17589         }
17590         if(this.multiSelect || this.singleSelect){
17591             if(this.multiSelect && e.shiftKey && this.lastSelection){
17592                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
17593             }else{
17594                 this.select(item, this.multiSelect && e.ctrlKey);
17595                 this.lastSelection = item;
17596             }
17597             
17598             if(!this.tickable){
17599                 e.preventDefault();
17600             }
17601             
17602         }
17603         return true;
17604     },
17605
17606     /**
17607      * Get the number of selected nodes.
17608      * @return {Number}
17609      */
17610     getSelectionCount : function(){
17611         return this.selections.length;
17612     },
17613
17614     /**
17615      * Get the currently selected nodes.
17616      * @return {Array} An array of HTMLElements
17617      */
17618     getSelectedNodes : function(){
17619         return this.selections;
17620     },
17621
17622     /**
17623      * Get the indexes of the selected nodes.
17624      * @return {Array}
17625      */
17626     getSelectedIndexes : function(){
17627         var indexes = [], s = this.selections;
17628         for(var i = 0, len = s.length; i < len; i++){
17629             indexes.push(s[i].nodeIndex);
17630         }
17631         return indexes;
17632     },
17633
17634     /**
17635      * Clear all selections
17636      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
17637      */
17638     clearSelections : function(suppressEvent){
17639         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
17640             this.cmp.elements = this.selections;
17641             this.cmp.removeClass(this.selectedClass);
17642             this.selections = [];
17643             if(!suppressEvent){
17644                 this.fireEvent("selectionchange", this, this.selections);
17645             }
17646         }
17647     },
17648
17649     /**
17650      * Returns true if the passed node is selected
17651      * @param {HTMLElement/Number} node The node or node index
17652      * @return {Boolean}
17653      */
17654     isSelected : function(node){
17655         var s = this.selections;
17656         if(s.length < 1){
17657             return false;
17658         }
17659         node = this.getNode(node);
17660         return s.indexOf(node) !== -1;
17661     },
17662
17663     /**
17664      * Selects nodes.
17665      * @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
17666      * @param {Boolean} keepExisting (optional) true to keep existing selections
17667      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
17668      */
17669     select : function(nodeInfo, keepExisting, suppressEvent){
17670         if(nodeInfo instanceof Array){
17671             if(!keepExisting){
17672                 this.clearSelections(true);
17673             }
17674             for(var i = 0, len = nodeInfo.length; i < len; i++){
17675                 this.select(nodeInfo[i], true, true);
17676             }
17677             return;
17678         } 
17679         var node = this.getNode(nodeInfo);
17680         if(!node || this.isSelected(node)){
17681             return; // already selected.
17682         }
17683         if(!keepExisting){
17684             this.clearSelections(true);
17685         }
17686         
17687         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
17688             Roo.fly(node).addClass(this.selectedClass);
17689             this.selections.push(node);
17690             if(!suppressEvent){
17691                 this.fireEvent("selectionchange", this, this.selections);
17692             }
17693         }
17694         
17695         
17696     },
17697       /**
17698      * Unselects nodes.
17699      * @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
17700      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
17701      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
17702      */
17703     unselect : function(nodeInfo, keepExisting, suppressEvent)
17704     {
17705         if(nodeInfo instanceof Array){
17706             Roo.each(this.selections, function(s) {
17707                 this.unselect(s, nodeInfo);
17708             }, this);
17709             return;
17710         }
17711         var node = this.getNode(nodeInfo);
17712         if(!node || !this.isSelected(node)){
17713             //Roo.log("not selected");
17714             return; // not selected.
17715         }
17716         // fireevent???
17717         var ns = [];
17718         Roo.each(this.selections, function(s) {
17719             if (s == node ) {
17720                 Roo.fly(node).removeClass(this.selectedClass);
17721
17722                 return;
17723             }
17724             ns.push(s);
17725         },this);
17726         
17727         this.selections= ns;
17728         this.fireEvent("selectionchange", this, this.selections);
17729     },
17730
17731     /**
17732      * Gets a template node.
17733      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
17734      * @return {HTMLElement} The node or null if it wasn't found
17735      */
17736     getNode : function(nodeInfo){
17737         if(typeof nodeInfo == "string"){
17738             return document.getElementById(nodeInfo);
17739         }else if(typeof nodeInfo == "number"){
17740             return this.nodes[nodeInfo];
17741         }
17742         return nodeInfo;
17743     },
17744
17745     /**
17746      * Gets a range template nodes.
17747      * @param {Number} startIndex
17748      * @param {Number} endIndex
17749      * @return {Array} An array of nodes
17750      */
17751     getNodes : function(start, end){
17752         var ns = this.nodes;
17753         start = start || 0;
17754         end = typeof end == "undefined" ? ns.length - 1 : end;
17755         var nodes = [];
17756         if(start <= end){
17757             for(var i = start; i <= end; i++){
17758                 nodes.push(ns[i]);
17759             }
17760         } else{
17761             for(var i = start; i >= end; i--){
17762                 nodes.push(ns[i]);
17763             }
17764         }
17765         return nodes;
17766     },
17767
17768     /**
17769      * Finds the index of the passed node
17770      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
17771      * @return {Number} The index of the node or -1
17772      */
17773     indexOf : function(node){
17774         node = this.getNode(node);
17775         if(typeof node.nodeIndex == "number"){
17776             return node.nodeIndex;
17777         }
17778         var ns = this.nodes;
17779         for(var i = 0, len = ns.length; i < len; i++){
17780             if(ns[i] == node){
17781                 return i;
17782             }
17783         }
17784         return -1;
17785     }
17786 });
17787 /*
17788  * - LGPL
17789  *
17790  * based on jquery fullcalendar
17791  * 
17792  */
17793
17794 Roo.bootstrap = Roo.bootstrap || {};
17795 /**
17796  * @class Roo.bootstrap.Calendar
17797  * @extends Roo.bootstrap.Component
17798  * Bootstrap Calendar class
17799  * @cfg {Boolean} loadMask (true|false) default false
17800  * @cfg {Object} header generate the user specific header of the calendar, default false
17801
17802  * @constructor
17803  * Create a new Container
17804  * @param {Object} config The config object
17805  */
17806
17807
17808
17809 Roo.bootstrap.Calendar = function(config){
17810     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
17811      this.addEvents({
17812         /**
17813              * @event select
17814              * Fires when a date is selected
17815              * @param {DatePicker} this
17816              * @param {Date} date The selected date
17817              */
17818         'select': true,
17819         /**
17820              * @event monthchange
17821              * Fires when the displayed month changes 
17822              * @param {DatePicker} this
17823              * @param {Date} date The selected month
17824              */
17825         'monthchange': true,
17826         /**
17827              * @event evententer
17828              * Fires when mouse over an event
17829              * @param {Calendar} this
17830              * @param {event} Event
17831              */
17832         'evententer': true,
17833         /**
17834              * @event eventleave
17835              * Fires when the mouse leaves an
17836              * @param {Calendar} this
17837              * @param {event}
17838              */
17839         'eventleave': true,
17840         /**
17841              * @event eventclick
17842              * Fires when the mouse click an
17843              * @param {Calendar} this
17844              * @param {event}
17845              */
17846         'eventclick': true
17847         
17848     });
17849
17850 };
17851
17852 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
17853     
17854      /**
17855      * @cfg {Number} startDay
17856      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
17857      */
17858     startDay : 0,
17859     
17860     loadMask : false,
17861     
17862     header : false,
17863       
17864     getAutoCreate : function(){
17865         
17866         
17867         var fc_button = function(name, corner, style, content ) {
17868             return Roo.apply({},{
17869                 tag : 'span',
17870                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
17871                          (corner.length ?
17872                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
17873                             ''
17874                         ),
17875                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
17876                 unselectable: 'on'
17877             });
17878         };
17879         
17880         var header = {};
17881         
17882         if(!this.header){
17883             header = {
17884                 tag : 'table',
17885                 cls : 'fc-header',
17886                 style : 'width:100%',
17887                 cn : [
17888                     {
17889                         tag: 'tr',
17890                         cn : [
17891                             {
17892                                 tag : 'td',
17893                                 cls : 'fc-header-left',
17894                                 cn : [
17895                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
17896                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
17897                                     { tag: 'span', cls: 'fc-header-space' },
17898                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
17899
17900
17901                                 ]
17902                             },
17903
17904                             {
17905                                 tag : 'td',
17906                                 cls : 'fc-header-center',
17907                                 cn : [
17908                                     {
17909                                         tag: 'span',
17910                                         cls: 'fc-header-title',
17911                                         cn : {
17912                                             tag: 'H2',
17913                                             html : 'month / year'
17914                                         }
17915                                     }
17916
17917                                 ]
17918                             },
17919                             {
17920                                 tag : 'td',
17921                                 cls : 'fc-header-right',
17922                                 cn : [
17923                               /*      fc_button('month', 'left', '', 'month' ),
17924                                     fc_button('week', '', '', 'week' ),
17925                                     fc_button('day', 'right', '', 'day' )
17926                                 */    
17927
17928                                 ]
17929                             }
17930
17931                         ]
17932                     }
17933                 ]
17934             };
17935         }
17936         
17937         header = this.header;
17938         
17939        
17940         var cal_heads = function() {
17941             var ret = [];
17942             // fixme - handle this.
17943             
17944             for (var i =0; i < Date.dayNames.length; i++) {
17945                 var d = Date.dayNames[i];
17946                 ret.push({
17947                     tag: 'th',
17948                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
17949                     html : d.substring(0,3)
17950                 });
17951                 
17952             }
17953             ret[0].cls += ' fc-first';
17954             ret[6].cls += ' fc-last';
17955             return ret;
17956         };
17957         var cal_cell = function(n) {
17958             return  {
17959                 tag: 'td',
17960                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
17961                 cn : [
17962                     {
17963                         cn : [
17964                             {
17965                                 cls: 'fc-day-number',
17966                                 html: 'D'
17967                             },
17968                             {
17969                                 cls: 'fc-day-content',
17970                              
17971                                 cn : [
17972                                      {
17973                                         style: 'position: relative;' // height: 17px;
17974                                     }
17975                                 ]
17976                             }
17977                             
17978                             
17979                         ]
17980                     }
17981                 ]
17982                 
17983             }
17984         };
17985         var cal_rows = function() {
17986             
17987             var ret = [];
17988             for (var r = 0; r < 6; r++) {
17989                 var row= {
17990                     tag : 'tr',
17991                     cls : 'fc-week',
17992                     cn : []
17993                 };
17994                 
17995                 for (var i =0; i < Date.dayNames.length; i++) {
17996                     var d = Date.dayNames[i];
17997                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
17998
17999                 }
18000                 row.cn[0].cls+=' fc-first';
18001                 row.cn[0].cn[0].style = 'min-height:90px';
18002                 row.cn[6].cls+=' fc-last';
18003                 ret.push(row);
18004                 
18005             }
18006             ret[0].cls += ' fc-first';
18007             ret[4].cls += ' fc-prev-last';
18008             ret[5].cls += ' fc-last';
18009             return ret;
18010             
18011         };
18012         
18013         var cal_table = {
18014             tag: 'table',
18015             cls: 'fc-border-separate',
18016             style : 'width:100%',
18017             cellspacing  : 0,
18018             cn : [
18019                 { 
18020                     tag: 'thead',
18021                     cn : [
18022                         { 
18023                             tag: 'tr',
18024                             cls : 'fc-first fc-last',
18025                             cn : cal_heads()
18026                         }
18027                     ]
18028                 },
18029                 { 
18030                     tag: 'tbody',
18031                     cn : cal_rows()
18032                 }
18033                   
18034             ]
18035         };
18036          
18037          var cfg = {
18038             cls : 'fc fc-ltr',
18039             cn : [
18040                 header,
18041                 {
18042                     cls : 'fc-content',
18043                     style : "position: relative;",
18044                     cn : [
18045                         {
18046                             cls : 'fc-view fc-view-month fc-grid',
18047                             style : 'position: relative',
18048                             unselectable : 'on',
18049                             cn : [
18050                                 {
18051                                     cls : 'fc-event-container',
18052                                     style : 'position:absolute;z-index:8;top:0;left:0;'
18053                                 },
18054                                 cal_table
18055                             ]
18056                         }
18057                     ]
18058     
18059                 }
18060            ] 
18061             
18062         };
18063         
18064          
18065         
18066         return cfg;
18067     },
18068     
18069     
18070     initEvents : function()
18071     {
18072         if(!this.store){
18073             throw "can not find store for calendar";
18074         }
18075         
18076         var mark = {
18077             tag: "div",
18078             cls:"x-dlg-mask",
18079             style: "text-align:center",
18080             cn: [
18081                 {
18082                     tag: "div",
18083                     style: "background-color:white;width:50%;margin:250 auto",
18084                     cn: [
18085                         {
18086                             tag: "img",
18087                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
18088                         },
18089                         {
18090                             tag: "span",
18091                             html: "Loading"
18092                         }
18093                         
18094                     ]
18095                 }
18096             ]
18097         };
18098         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
18099         
18100         var size = this.el.select('.fc-content', true).first().getSize();
18101         this.maskEl.setSize(size.width, size.height);
18102         this.maskEl.enableDisplayMode("block");
18103         if(!this.loadMask){
18104             this.maskEl.hide();
18105         }
18106         
18107         this.store = Roo.factory(this.store, Roo.data);
18108         this.store.on('load', this.onLoad, this);
18109         this.store.on('beforeload', this.onBeforeLoad, this);
18110         
18111         this.resize();
18112         
18113         this.cells = this.el.select('.fc-day',true);
18114         //Roo.log(this.cells);
18115         this.textNodes = this.el.query('.fc-day-number');
18116         this.cells.addClassOnOver('fc-state-hover');
18117         
18118         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
18119         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
18120         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
18121         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
18122         
18123         this.on('monthchange', this.onMonthChange, this);
18124         
18125         this.update(new Date().clearTime());
18126     },
18127     
18128     resize : function() {
18129         var sz  = this.el.getSize();
18130         
18131         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
18132         this.el.select('.fc-day-content div',true).setHeight(34);
18133     },
18134     
18135     
18136     // private
18137     showPrevMonth : function(e){
18138         this.update(this.activeDate.add("mo", -1));
18139     },
18140     showToday : function(e){
18141         this.update(new Date().clearTime());
18142     },
18143     // private
18144     showNextMonth : function(e){
18145         this.update(this.activeDate.add("mo", 1));
18146     },
18147
18148     // private
18149     showPrevYear : function(){
18150         this.update(this.activeDate.add("y", -1));
18151     },
18152
18153     // private
18154     showNextYear : function(){
18155         this.update(this.activeDate.add("y", 1));
18156     },
18157
18158     
18159    // private
18160     update : function(date)
18161     {
18162         var vd = this.activeDate;
18163         this.activeDate = date;
18164 //        if(vd && this.el){
18165 //            var t = date.getTime();
18166 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
18167 //                Roo.log('using add remove');
18168 //                
18169 //                this.fireEvent('monthchange', this, date);
18170 //                
18171 //                this.cells.removeClass("fc-state-highlight");
18172 //                this.cells.each(function(c){
18173 //                   if(c.dateValue == t){
18174 //                       c.addClass("fc-state-highlight");
18175 //                       setTimeout(function(){
18176 //                            try{c.dom.firstChild.focus();}catch(e){}
18177 //                       }, 50);
18178 //                       return false;
18179 //                   }
18180 //                   return true;
18181 //                });
18182 //                return;
18183 //            }
18184 //        }
18185         
18186         var days = date.getDaysInMonth();
18187         
18188         var firstOfMonth = date.getFirstDateOfMonth();
18189         var startingPos = firstOfMonth.getDay()-this.startDay;
18190         
18191         if(startingPos < this.startDay){
18192             startingPos += 7;
18193         }
18194         
18195         var pm = date.add(Date.MONTH, -1);
18196         var prevStart = pm.getDaysInMonth()-startingPos;
18197 //        
18198         this.cells = this.el.select('.fc-day',true);
18199         this.textNodes = this.el.query('.fc-day-number');
18200         this.cells.addClassOnOver('fc-state-hover');
18201         
18202         var cells = this.cells.elements;
18203         var textEls = this.textNodes;
18204         
18205         Roo.each(cells, function(cell){
18206             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
18207         });
18208         
18209         days += startingPos;
18210
18211         // convert everything to numbers so it's fast
18212         var day = 86400000;
18213         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
18214         //Roo.log(d);
18215         //Roo.log(pm);
18216         //Roo.log(prevStart);
18217         
18218         var today = new Date().clearTime().getTime();
18219         var sel = date.clearTime().getTime();
18220         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
18221         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
18222         var ddMatch = this.disabledDatesRE;
18223         var ddText = this.disabledDatesText;
18224         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
18225         var ddaysText = this.disabledDaysText;
18226         var format = this.format;
18227         
18228         var setCellClass = function(cal, cell){
18229             cell.row = 0;
18230             cell.events = [];
18231             cell.more = [];
18232             //Roo.log('set Cell Class');
18233             cell.title = "";
18234             var t = d.getTime();
18235             
18236             //Roo.log(d);
18237             
18238             cell.dateValue = t;
18239             if(t == today){
18240                 cell.className += " fc-today";
18241                 cell.className += " fc-state-highlight";
18242                 cell.title = cal.todayText;
18243             }
18244             if(t == sel){
18245                 // disable highlight in other month..
18246                 //cell.className += " fc-state-highlight";
18247                 
18248             }
18249             // disabling
18250             if(t < min) {
18251                 cell.className = " fc-state-disabled";
18252                 cell.title = cal.minText;
18253                 return;
18254             }
18255             if(t > max) {
18256                 cell.className = " fc-state-disabled";
18257                 cell.title = cal.maxText;
18258                 return;
18259             }
18260             if(ddays){
18261                 if(ddays.indexOf(d.getDay()) != -1){
18262                     cell.title = ddaysText;
18263                     cell.className = " fc-state-disabled";
18264                 }
18265             }
18266             if(ddMatch && format){
18267                 var fvalue = d.dateFormat(format);
18268                 if(ddMatch.test(fvalue)){
18269                     cell.title = ddText.replace("%0", fvalue);
18270                     cell.className = " fc-state-disabled";
18271                 }
18272             }
18273             
18274             if (!cell.initialClassName) {
18275                 cell.initialClassName = cell.dom.className;
18276             }
18277             
18278             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
18279         };
18280
18281         var i = 0;
18282         
18283         for(; i < startingPos; i++) {
18284             textEls[i].innerHTML = (++prevStart);
18285             d.setDate(d.getDate()+1);
18286             
18287             cells[i].className = "fc-past fc-other-month";
18288             setCellClass(this, cells[i]);
18289         }
18290         
18291         var intDay = 0;
18292         
18293         for(; i < days; i++){
18294             intDay = i - startingPos + 1;
18295             textEls[i].innerHTML = (intDay);
18296             d.setDate(d.getDate()+1);
18297             
18298             cells[i].className = ''; // "x-date-active";
18299             setCellClass(this, cells[i]);
18300         }
18301         var extraDays = 0;
18302         
18303         for(; i < 42; i++) {
18304             textEls[i].innerHTML = (++extraDays);
18305             d.setDate(d.getDate()+1);
18306             
18307             cells[i].className = "fc-future fc-other-month";
18308             setCellClass(this, cells[i]);
18309         }
18310         
18311         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
18312         
18313         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
18314         
18315         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
18316         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
18317         
18318         if(totalRows != 6){
18319             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
18320             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
18321         }
18322         
18323         this.fireEvent('monthchange', this, date);
18324         
18325         
18326         /*
18327         if(!this.internalRender){
18328             var main = this.el.dom.firstChild;
18329             var w = main.offsetWidth;
18330             this.el.setWidth(w + this.el.getBorderWidth("lr"));
18331             Roo.fly(main).setWidth(w);
18332             this.internalRender = true;
18333             // opera does not respect the auto grow header center column
18334             // then, after it gets a width opera refuses to recalculate
18335             // without a second pass
18336             if(Roo.isOpera && !this.secondPass){
18337                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
18338                 this.secondPass = true;
18339                 this.update.defer(10, this, [date]);
18340             }
18341         }
18342         */
18343         
18344     },
18345     
18346     findCell : function(dt) {
18347         dt = dt.clearTime().getTime();
18348         var ret = false;
18349         this.cells.each(function(c){
18350             //Roo.log("check " +c.dateValue + '?=' + dt);
18351             if(c.dateValue == dt){
18352                 ret = c;
18353                 return false;
18354             }
18355             return true;
18356         });
18357         
18358         return ret;
18359     },
18360     
18361     findCells : function(ev) {
18362         var s = ev.start.clone().clearTime().getTime();
18363        // Roo.log(s);
18364         var e= ev.end.clone().clearTime().getTime();
18365        // Roo.log(e);
18366         var ret = [];
18367         this.cells.each(function(c){
18368              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
18369             
18370             if(c.dateValue > e){
18371                 return ;
18372             }
18373             if(c.dateValue < s){
18374                 return ;
18375             }
18376             ret.push(c);
18377         });
18378         
18379         return ret;    
18380     },
18381     
18382 //    findBestRow: function(cells)
18383 //    {
18384 //        var ret = 0;
18385 //        
18386 //        for (var i =0 ; i < cells.length;i++) {
18387 //            ret  = Math.max(cells[i].rows || 0,ret);
18388 //        }
18389 //        return ret;
18390 //        
18391 //    },
18392     
18393     
18394     addItem : function(ev)
18395     {
18396         // look for vertical location slot in
18397         var cells = this.findCells(ev);
18398         
18399 //        ev.row = this.findBestRow(cells);
18400         
18401         // work out the location.
18402         
18403         var crow = false;
18404         var rows = [];
18405         for(var i =0; i < cells.length; i++) {
18406             
18407             cells[i].row = cells[0].row;
18408             
18409             if(i == 0){
18410                 cells[i].row = cells[i].row + 1;
18411             }
18412             
18413             if (!crow) {
18414                 crow = {
18415                     start : cells[i],
18416                     end :  cells[i]
18417                 };
18418                 continue;
18419             }
18420             if (crow.start.getY() == cells[i].getY()) {
18421                 // on same row.
18422                 crow.end = cells[i];
18423                 continue;
18424             }
18425             // different row.
18426             rows.push(crow);
18427             crow = {
18428                 start: cells[i],
18429                 end : cells[i]
18430             };
18431             
18432         }
18433         
18434         rows.push(crow);
18435         ev.els = [];
18436         ev.rows = rows;
18437         ev.cells = cells;
18438         
18439         cells[0].events.push(ev);
18440         
18441         this.calevents.push(ev);
18442     },
18443     
18444     clearEvents: function() {
18445         
18446         if(!this.calevents){
18447             return;
18448         }
18449         
18450         Roo.each(this.cells.elements, function(c){
18451             c.row = 0;
18452             c.events = [];
18453             c.more = [];
18454         });
18455         
18456         Roo.each(this.calevents, function(e) {
18457             Roo.each(e.els, function(el) {
18458                 el.un('mouseenter' ,this.onEventEnter, this);
18459                 el.un('mouseleave' ,this.onEventLeave, this);
18460                 el.remove();
18461             },this);
18462         },this);
18463         
18464         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
18465             e.remove();
18466         });
18467         
18468     },
18469     
18470     renderEvents: function()
18471     {   
18472         var _this = this;
18473         
18474         this.cells.each(function(c) {
18475             
18476             if(c.row < 5){
18477                 return;
18478             }
18479             
18480             var ev = c.events;
18481             
18482             var r = 4;
18483             if(c.row != c.events.length){
18484                 r = 4 - (4 - (c.row - c.events.length));
18485             }
18486             
18487             c.events = ev.slice(0, r);
18488             c.more = ev.slice(r);
18489             
18490             if(c.more.length && c.more.length == 1){
18491                 c.events.push(c.more.pop());
18492             }
18493             
18494             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
18495             
18496         });
18497             
18498         this.cells.each(function(c) {
18499             
18500             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
18501             
18502             
18503             for (var e = 0; e < c.events.length; e++){
18504                 var ev = c.events[e];
18505                 var rows = ev.rows;
18506                 
18507                 for(var i = 0; i < rows.length; i++) {
18508                 
18509                     // how many rows should it span..
18510
18511                     var  cfg = {
18512                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
18513                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
18514
18515                         unselectable : "on",
18516                         cn : [
18517                             {
18518                                 cls: 'fc-event-inner',
18519                                 cn : [
18520     //                                {
18521     //                                  tag:'span',
18522     //                                  cls: 'fc-event-time',
18523     //                                  html : cells.length > 1 ? '' : ev.time
18524     //                                },
18525                                     {
18526                                       tag:'span',
18527                                       cls: 'fc-event-title',
18528                                       html : String.format('{0}', ev.title)
18529                                     }
18530
18531
18532                                 ]
18533                             },
18534                             {
18535                                 cls: 'ui-resizable-handle ui-resizable-e',
18536                                 html : '&nbsp;&nbsp;&nbsp'
18537                             }
18538
18539                         ]
18540                     };
18541
18542                     if (i == 0) {
18543                         cfg.cls += ' fc-event-start';
18544                     }
18545                     if ((i+1) == rows.length) {
18546                         cfg.cls += ' fc-event-end';
18547                     }
18548
18549                     var ctr = _this.el.select('.fc-event-container',true).first();
18550                     var cg = ctr.createChild(cfg);
18551
18552                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
18553                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
18554
18555                     var r = (c.more.length) ? 1 : 0;
18556                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
18557                     cg.setWidth(ebox.right - sbox.x -2);
18558
18559                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
18560                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
18561                     cg.on('click', _this.onEventClick, _this, ev);
18562
18563                     ev.els.push(cg);
18564                     
18565                 }
18566                 
18567             }
18568             
18569             
18570             if(c.more.length){
18571                 var  cfg = {
18572                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
18573                     style : 'position: absolute',
18574                     unselectable : "on",
18575                     cn : [
18576                         {
18577                             cls: 'fc-event-inner',
18578                             cn : [
18579                                 {
18580                                   tag:'span',
18581                                   cls: 'fc-event-title',
18582                                   html : 'More'
18583                                 }
18584
18585
18586                             ]
18587                         },
18588                         {
18589                             cls: 'ui-resizable-handle ui-resizable-e',
18590                             html : '&nbsp;&nbsp;&nbsp'
18591                         }
18592
18593                     ]
18594                 };
18595
18596                 var ctr = _this.el.select('.fc-event-container',true).first();
18597                 var cg = ctr.createChild(cfg);
18598
18599                 var sbox = c.select('.fc-day-content',true).first().getBox();
18600                 var ebox = c.select('.fc-day-content',true).first().getBox();
18601                 //Roo.log(cg);
18602                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
18603                 cg.setWidth(ebox.right - sbox.x -2);
18604
18605                 cg.on('click', _this.onMoreEventClick, _this, c.more);
18606                 
18607             }
18608             
18609         });
18610         
18611         
18612         
18613     },
18614     
18615     onEventEnter: function (e, el,event,d) {
18616         this.fireEvent('evententer', this, el, event);
18617     },
18618     
18619     onEventLeave: function (e, el,event,d) {
18620         this.fireEvent('eventleave', this, el, event);
18621     },
18622     
18623     onEventClick: function (e, el,event,d) {
18624         this.fireEvent('eventclick', this, el, event);
18625     },
18626     
18627     onMonthChange: function () {
18628         this.store.load();
18629     },
18630     
18631     onMoreEventClick: function(e, el, more)
18632     {
18633         var _this = this;
18634         
18635         this.calpopover.placement = 'right';
18636         this.calpopover.setTitle('More');
18637         
18638         this.calpopover.setContent('');
18639         
18640         var ctr = this.calpopover.el.select('.popover-content', true).first();
18641         
18642         Roo.each(more, function(m){
18643             var cfg = {
18644                 cls : 'fc-event-hori fc-event-draggable',
18645                 html : m.title
18646             };
18647             var cg = ctr.createChild(cfg);
18648             
18649             cg.on('click', _this.onEventClick, _this, m);
18650         });
18651         
18652         this.calpopover.show(el);
18653         
18654         
18655     },
18656     
18657     onLoad: function () 
18658     {   
18659         this.calevents = [];
18660         var cal = this;
18661         
18662         if(this.store.getCount() > 0){
18663             this.store.data.each(function(d){
18664                cal.addItem({
18665                     id : d.data.id,
18666                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
18667                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
18668                     time : d.data.start_time,
18669                     title : d.data.title,
18670                     description : d.data.description,
18671                     venue : d.data.venue
18672                 });
18673             });
18674         }
18675         
18676         this.renderEvents();
18677         
18678         if(this.calevents.length && this.loadMask){
18679             this.maskEl.hide();
18680         }
18681     },
18682     
18683     onBeforeLoad: function()
18684     {
18685         this.clearEvents();
18686         if(this.loadMask){
18687             this.maskEl.show();
18688         }
18689     }
18690 });
18691
18692  
18693  /*
18694  * - LGPL
18695  *
18696  * element
18697  * 
18698  */
18699
18700 /**
18701  * @class Roo.bootstrap.Popover
18702  * @extends Roo.bootstrap.Component
18703  * Bootstrap Popover class
18704  * @cfg {String} html contents of the popover   (or false to use children..)
18705  * @cfg {String} title of popover (or false to hide)
18706  * @cfg {String} placement how it is placed
18707  * @cfg {String} trigger click || hover (or false to trigger manually)
18708  * @cfg {String} over what (parent or false to trigger manually.)
18709  * @cfg {Number} delay - delay before showing
18710  
18711  * @constructor
18712  * Create a new Popover
18713  * @param {Object} config The config object
18714  */
18715
18716 Roo.bootstrap.Popover = function(config){
18717     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
18718     
18719     this.addEvents({
18720         // raw events
18721          /**
18722          * @event show
18723          * After the popover show
18724          * 
18725          * @param {Roo.bootstrap.Popover} this
18726          */
18727         "show" : true,
18728         /**
18729          * @event hide
18730          * After the popover hide
18731          * 
18732          * @param {Roo.bootstrap.Popover} this
18733          */
18734         "hide" : true
18735     });
18736 };
18737
18738 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
18739     
18740     title: 'Fill in a title',
18741     html: false,
18742     
18743     placement : 'right',
18744     trigger : 'hover', // hover
18745     
18746     delay : 0,
18747     
18748     over: 'parent',
18749     
18750     can_build_overlaid : false,
18751     
18752     getChildContainer : function()
18753     {
18754         return this.el.select('.popover-content',true).first();
18755     },
18756     
18757     getAutoCreate : function(){
18758          
18759         var cfg = {
18760            cls : 'popover roo-dynamic',
18761            style: 'display:block',
18762            cn : [
18763                 {
18764                     cls : 'arrow'
18765                 },
18766                 {
18767                     cls : 'popover-inner',
18768                     cn : [
18769                         {
18770                             tag: 'h3',
18771                             cls: 'popover-title popover-header',
18772                             html : this.title
18773                         },
18774                         {
18775                             cls : 'popover-content popover-body',
18776                             html : this.html
18777                         }
18778                     ]
18779                     
18780                 }
18781            ]
18782         };
18783         
18784         return cfg;
18785     },
18786     setTitle: function(str)
18787     {
18788         this.title = str;
18789         this.el.select('.popover-title',true).first().dom.innerHTML = str;
18790     },
18791     setContent: function(str)
18792     {
18793         this.html = str;
18794         this.el.select('.popover-content',true).first().dom.innerHTML = str;
18795     },
18796     // as it get's added to the bottom of the page.
18797     onRender : function(ct, position)
18798     {
18799         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
18800         if(!this.el){
18801             var cfg = Roo.apply({},  this.getAutoCreate());
18802             cfg.id = Roo.id();
18803             
18804             if (this.cls) {
18805                 cfg.cls += ' ' + this.cls;
18806             }
18807             if (this.style) {
18808                 cfg.style = this.style;
18809             }
18810             //Roo.log("adding to ");
18811             this.el = Roo.get(document.body).createChild(cfg, position);
18812 //            Roo.log(this.el);
18813         }
18814         this.initEvents();
18815     },
18816     
18817     initEvents : function()
18818     {
18819         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
18820         this.el.enableDisplayMode('block');
18821         this.el.hide();
18822         if (this.over === false) {
18823             return; 
18824         }
18825         if (this.triggers === false) {
18826             return;
18827         }
18828         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
18829         var triggers = this.trigger ? this.trigger.split(' ') : [];
18830         Roo.each(triggers, function(trigger) {
18831         
18832             if (trigger == 'click') {
18833                 on_el.on('click', this.toggle, this);
18834             } else if (trigger != 'manual') {
18835                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
18836                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
18837       
18838                 on_el.on(eventIn  ,this.enter, this);
18839                 on_el.on(eventOut, this.leave, this);
18840             }
18841         }, this);
18842         
18843     },
18844     
18845     
18846     // private
18847     timeout : null,
18848     hoverState : null,
18849     
18850     toggle : function () {
18851         this.hoverState == 'in' ? this.leave() : this.enter();
18852     },
18853     
18854     enter : function () {
18855         
18856         clearTimeout(this.timeout);
18857     
18858         this.hoverState = 'in';
18859     
18860         if (!this.delay || !this.delay.show) {
18861             this.show();
18862             return;
18863         }
18864         var _t = this;
18865         this.timeout = setTimeout(function () {
18866             if (_t.hoverState == 'in') {
18867                 _t.show();
18868             }
18869         }, this.delay.show)
18870     },
18871     
18872     leave : function() {
18873         clearTimeout(this.timeout);
18874     
18875         this.hoverState = 'out';
18876     
18877         if (!this.delay || !this.delay.hide) {
18878             this.hide();
18879             return;
18880         }
18881         var _t = this;
18882         this.timeout = setTimeout(function () {
18883             if (_t.hoverState == 'out') {
18884                 _t.hide();
18885             }
18886         }, this.delay.hide)
18887     },
18888     
18889     show : function (on_el)
18890     {
18891         if (!on_el) {
18892             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
18893         }
18894         
18895         // set content.
18896         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
18897         if (this.html !== false) {
18898             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
18899         }
18900         this.el.removeClass([
18901             'fade','top','bottom', 'left', 'right','in',
18902             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
18903         ]);
18904         if (!this.title.length) {
18905             this.el.select('.popover-title',true).hide();
18906         }
18907         
18908         var placement = typeof this.placement == 'function' ?
18909             this.placement.call(this, this.el, on_el) :
18910             this.placement;
18911             
18912         var autoToken = /\s?auto?\s?/i;
18913         var autoPlace = autoToken.test(placement);
18914         if (autoPlace) {
18915             placement = placement.replace(autoToken, '') || 'top';
18916         }
18917         
18918         //this.el.detach()
18919         //this.el.setXY([0,0]);
18920         this.el.show();
18921         this.el.dom.style.display='block';
18922         this.el.addClass(placement);
18923         
18924         //this.el.appendTo(on_el);
18925         
18926         var p = this.getPosition();
18927         var box = this.el.getBox();
18928         
18929         if (autoPlace) {
18930             // fixme..
18931         }
18932         var align = Roo.bootstrap.Popover.alignment[placement];
18933         
18934 //        Roo.log(align);
18935         this.el.alignTo(on_el, align[0],align[1]);
18936         //var arrow = this.el.select('.arrow',true).first();
18937         //arrow.set(align[2], 
18938         
18939         this.el.addClass('in');
18940         
18941         
18942         if (this.el.hasClass('fade')) {
18943             // fade it?
18944         }
18945         
18946         this.hoverState = 'in';
18947         
18948         this.fireEvent('show', this);
18949         
18950     },
18951     hide : function()
18952     {
18953         this.el.setXY([0,0]);
18954         this.el.removeClass('in');
18955         this.el.hide();
18956         this.hoverState = null;
18957         
18958         this.fireEvent('hide', this);
18959     }
18960     
18961 });
18962
18963 Roo.bootstrap.Popover.alignment = {
18964     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
18965     'right' : ['l-r', [10,0], 'left bs-popover-left'],
18966     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
18967     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
18968 };
18969
18970  /*
18971  * - LGPL
18972  *
18973  * Progress
18974  * 
18975  */
18976
18977 /**
18978  * @class Roo.bootstrap.Progress
18979  * @extends Roo.bootstrap.Component
18980  * Bootstrap Progress class
18981  * @cfg {Boolean} striped striped of the progress bar
18982  * @cfg {Boolean} active animated of the progress bar
18983  * 
18984  * 
18985  * @constructor
18986  * Create a new Progress
18987  * @param {Object} config The config object
18988  */
18989
18990 Roo.bootstrap.Progress = function(config){
18991     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
18992 };
18993
18994 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
18995     
18996     striped : false,
18997     active: false,
18998     
18999     getAutoCreate : function(){
19000         var cfg = {
19001             tag: 'div',
19002             cls: 'progress'
19003         };
19004         
19005         
19006         if(this.striped){
19007             cfg.cls += ' progress-striped';
19008         }
19009       
19010         if(this.active){
19011             cfg.cls += ' active';
19012         }
19013         
19014         
19015         return cfg;
19016     }
19017    
19018 });
19019
19020  
19021
19022  /*
19023  * - LGPL
19024  *
19025  * ProgressBar
19026  * 
19027  */
19028
19029 /**
19030  * @class Roo.bootstrap.ProgressBar
19031  * @extends Roo.bootstrap.Component
19032  * Bootstrap ProgressBar class
19033  * @cfg {Number} aria_valuenow aria-value now
19034  * @cfg {Number} aria_valuemin aria-value min
19035  * @cfg {Number} aria_valuemax aria-value max
19036  * @cfg {String} label label for the progress bar
19037  * @cfg {String} panel (success | info | warning | danger )
19038  * @cfg {String} role role of the progress bar
19039  * @cfg {String} sr_only text
19040  * 
19041  * 
19042  * @constructor
19043  * Create a new ProgressBar
19044  * @param {Object} config The config object
19045  */
19046
19047 Roo.bootstrap.ProgressBar = function(config){
19048     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
19049 };
19050
19051 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
19052     
19053     aria_valuenow : 0,
19054     aria_valuemin : 0,
19055     aria_valuemax : 100,
19056     label : false,
19057     panel : false,
19058     role : false,
19059     sr_only: false,
19060     
19061     getAutoCreate : function()
19062     {
19063         
19064         var cfg = {
19065             tag: 'div',
19066             cls: 'progress-bar',
19067             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
19068         };
19069         
19070         if(this.sr_only){
19071             cfg.cn = {
19072                 tag: 'span',
19073                 cls: 'sr-only',
19074                 html: this.sr_only
19075             }
19076         }
19077         
19078         if(this.role){
19079             cfg.role = this.role;
19080         }
19081         
19082         if(this.aria_valuenow){
19083             cfg['aria-valuenow'] = this.aria_valuenow;
19084         }
19085         
19086         if(this.aria_valuemin){
19087             cfg['aria-valuemin'] = this.aria_valuemin;
19088         }
19089         
19090         if(this.aria_valuemax){
19091             cfg['aria-valuemax'] = this.aria_valuemax;
19092         }
19093         
19094         if(this.label && !this.sr_only){
19095             cfg.html = this.label;
19096         }
19097         
19098         if(this.panel){
19099             cfg.cls += ' progress-bar-' + this.panel;
19100         }
19101         
19102         return cfg;
19103     },
19104     
19105     update : function(aria_valuenow)
19106     {
19107         this.aria_valuenow = aria_valuenow;
19108         
19109         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
19110     }
19111    
19112 });
19113
19114  
19115
19116  /*
19117  * - LGPL
19118  *
19119  * column
19120  * 
19121  */
19122
19123 /**
19124  * @class Roo.bootstrap.TabGroup
19125  * @extends Roo.bootstrap.Column
19126  * Bootstrap Column class
19127  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
19128  * @cfg {Boolean} carousel true to make the group behave like a carousel
19129  * @cfg {Boolean} bullets show bullets for the panels
19130  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
19131  * @cfg {Number} timer auto slide timer .. default 0 millisecond
19132  * @cfg {Boolean} showarrow (true|false) show arrow default true
19133  * 
19134  * @constructor
19135  * Create a new TabGroup
19136  * @param {Object} config The config object
19137  */
19138
19139 Roo.bootstrap.TabGroup = function(config){
19140     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
19141     if (!this.navId) {
19142         this.navId = Roo.id();
19143     }
19144     this.tabs = [];
19145     Roo.bootstrap.TabGroup.register(this);
19146     
19147 };
19148
19149 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
19150     
19151     carousel : false,
19152     transition : false,
19153     bullets : 0,
19154     timer : 0,
19155     autoslide : false,
19156     slideFn : false,
19157     slideOnTouch : false,
19158     showarrow : true,
19159     
19160     getAutoCreate : function()
19161     {
19162         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
19163         
19164         cfg.cls += ' tab-content';
19165         
19166         if (this.carousel) {
19167             cfg.cls += ' carousel slide';
19168             
19169             cfg.cn = [{
19170                cls : 'carousel-inner',
19171                cn : []
19172             }];
19173         
19174             if(this.bullets  && !Roo.isTouch){
19175                 
19176                 var bullets = {
19177                     cls : 'carousel-bullets',
19178                     cn : []
19179                 };
19180                
19181                 if(this.bullets_cls){
19182                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
19183                 }
19184                 
19185                 bullets.cn.push({
19186                     cls : 'clear'
19187                 });
19188                 
19189                 cfg.cn[0].cn.push(bullets);
19190             }
19191             
19192             if(this.showarrow){
19193                 cfg.cn[0].cn.push({
19194                     tag : 'div',
19195                     class : 'carousel-arrow',
19196                     cn : [
19197                         {
19198                             tag : 'div',
19199                             class : 'carousel-prev',
19200                             cn : [
19201                                 {
19202                                     tag : 'i',
19203                                     class : 'fa fa-chevron-left'
19204                                 }
19205                             ]
19206                         },
19207                         {
19208                             tag : 'div',
19209                             class : 'carousel-next',
19210                             cn : [
19211                                 {
19212                                     tag : 'i',
19213                                     class : 'fa fa-chevron-right'
19214                                 }
19215                             ]
19216                         }
19217                     ]
19218                 });
19219             }
19220             
19221         }
19222         
19223         return cfg;
19224     },
19225     
19226     initEvents:  function()
19227     {
19228 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
19229 //            this.el.on("touchstart", this.onTouchStart, this);
19230 //        }
19231         
19232         if(this.autoslide){
19233             var _this = this;
19234             
19235             this.slideFn = window.setInterval(function() {
19236                 _this.showPanelNext();
19237             }, this.timer);
19238         }
19239         
19240         if(this.showarrow){
19241             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
19242             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
19243         }
19244         
19245         
19246     },
19247     
19248 //    onTouchStart : function(e, el, o)
19249 //    {
19250 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
19251 //            return;
19252 //        }
19253 //        
19254 //        this.showPanelNext();
19255 //    },
19256     
19257     
19258     getChildContainer : function()
19259     {
19260         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
19261     },
19262     
19263     /**
19264     * register a Navigation item
19265     * @param {Roo.bootstrap.NavItem} the navitem to add
19266     */
19267     register : function(item)
19268     {
19269         this.tabs.push( item);
19270         item.navId = this.navId; // not really needed..
19271         this.addBullet();
19272     
19273     },
19274     
19275     getActivePanel : function()
19276     {
19277         var r = false;
19278         Roo.each(this.tabs, function(t) {
19279             if (t.active) {
19280                 r = t;
19281                 return false;
19282             }
19283             return null;
19284         });
19285         return r;
19286         
19287     },
19288     getPanelByName : function(n)
19289     {
19290         var r = false;
19291         Roo.each(this.tabs, function(t) {
19292             if (t.tabId == n) {
19293                 r = t;
19294                 return false;
19295             }
19296             return null;
19297         });
19298         return r;
19299     },
19300     indexOfPanel : function(p)
19301     {
19302         var r = false;
19303         Roo.each(this.tabs, function(t,i) {
19304             if (t.tabId == p.tabId) {
19305                 r = i;
19306                 return false;
19307             }
19308             return null;
19309         });
19310         return r;
19311     },
19312     /**
19313      * show a specific panel
19314      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
19315      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
19316      */
19317     showPanel : function (pan)
19318     {
19319         if(this.transition || typeof(pan) == 'undefined'){
19320             Roo.log("waiting for the transitionend");
19321             return false;
19322         }
19323         
19324         if (typeof(pan) == 'number') {
19325             pan = this.tabs[pan];
19326         }
19327         
19328         if (typeof(pan) == 'string') {
19329             pan = this.getPanelByName(pan);
19330         }
19331         
19332         var cur = this.getActivePanel();
19333         
19334         if(!pan || !cur){
19335             Roo.log('pan or acitve pan is undefined');
19336             return false;
19337         }
19338         
19339         if (pan.tabId == this.getActivePanel().tabId) {
19340             return true;
19341         }
19342         
19343         if (false === cur.fireEvent('beforedeactivate')) {
19344             return false;
19345         }
19346         
19347         if(this.bullets > 0 && !Roo.isTouch){
19348             this.setActiveBullet(this.indexOfPanel(pan));
19349         }
19350         
19351         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
19352             
19353             //class="carousel-item carousel-item-next carousel-item-left"
19354             
19355             this.transition = true;
19356             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
19357             var lr = dir == 'next' ? 'left' : 'right';
19358             pan.el.addClass(dir); // or prev
19359             pan.el.addClass('carousel-item-' + dir); // or prev
19360             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
19361             cur.el.addClass(lr); // or right
19362             pan.el.addClass(lr);
19363             cur.el.addClass('carousel-item-' +lr); // or right
19364             pan.el.addClass('carousel-item-' +lr);
19365             
19366             
19367             var _this = this;
19368             cur.el.on('transitionend', function() {
19369                 Roo.log("trans end?");
19370                 
19371                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
19372                 pan.setActive(true);
19373                 
19374                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
19375                 cur.setActive(false);
19376                 
19377                 _this.transition = false;
19378                 
19379             }, this, { single:  true } );
19380             
19381             return true;
19382         }
19383         
19384         cur.setActive(false);
19385         pan.setActive(true);
19386         
19387         return true;
19388         
19389     },
19390     showPanelNext : function()
19391     {
19392         var i = this.indexOfPanel(this.getActivePanel());
19393         
19394         if (i >= this.tabs.length - 1 && !this.autoslide) {
19395             return;
19396         }
19397         
19398         if (i >= this.tabs.length - 1 && this.autoslide) {
19399             i = -1;
19400         }
19401         
19402         this.showPanel(this.tabs[i+1]);
19403     },
19404     
19405     showPanelPrev : function()
19406     {
19407         var i = this.indexOfPanel(this.getActivePanel());
19408         
19409         if (i  < 1 && !this.autoslide) {
19410             return;
19411         }
19412         
19413         if (i < 1 && this.autoslide) {
19414             i = this.tabs.length;
19415         }
19416         
19417         this.showPanel(this.tabs[i-1]);
19418     },
19419     
19420     
19421     addBullet: function()
19422     {
19423         if(!this.bullets || Roo.isTouch){
19424             return;
19425         }
19426         var ctr = this.el.select('.carousel-bullets',true).first();
19427         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
19428         var bullet = ctr.createChild({
19429             cls : 'bullet bullet-' + i
19430         },ctr.dom.lastChild);
19431         
19432         
19433         var _this = this;
19434         
19435         bullet.on('click', (function(e, el, o, ii, t){
19436
19437             e.preventDefault();
19438
19439             this.showPanel(ii);
19440
19441             if(this.autoslide && this.slideFn){
19442                 clearInterval(this.slideFn);
19443                 this.slideFn = window.setInterval(function() {
19444                     _this.showPanelNext();
19445                 }, this.timer);
19446             }
19447
19448         }).createDelegate(this, [i, bullet], true));
19449                 
19450         
19451     },
19452      
19453     setActiveBullet : function(i)
19454     {
19455         if(Roo.isTouch){
19456             return;
19457         }
19458         
19459         Roo.each(this.el.select('.bullet', true).elements, function(el){
19460             el.removeClass('selected');
19461         });
19462
19463         var bullet = this.el.select('.bullet-' + i, true).first();
19464         
19465         if(!bullet){
19466             return;
19467         }
19468         
19469         bullet.addClass('selected');
19470     }
19471     
19472     
19473   
19474 });
19475
19476  
19477
19478  
19479  
19480 Roo.apply(Roo.bootstrap.TabGroup, {
19481     
19482     groups: {},
19483      /**
19484     * register a Navigation Group
19485     * @param {Roo.bootstrap.NavGroup} the navgroup to add
19486     */
19487     register : function(navgrp)
19488     {
19489         this.groups[navgrp.navId] = navgrp;
19490         
19491     },
19492     /**
19493     * fetch a Navigation Group based on the navigation ID
19494     * if one does not exist , it will get created.
19495     * @param {string} the navgroup to add
19496     * @returns {Roo.bootstrap.NavGroup} the navgroup 
19497     */
19498     get: function(navId) {
19499         if (typeof(this.groups[navId]) == 'undefined') {
19500             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
19501         }
19502         return this.groups[navId] ;
19503     }
19504     
19505     
19506     
19507 });
19508
19509  /*
19510  * - LGPL
19511  *
19512  * TabPanel
19513  * 
19514  */
19515
19516 /**
19517  * @class Roo.bootstrap.TabPanel
19518  * @extends Roo.bootstrap.Component
19519  * Bootstrap TabPanel class
19520  * @cfg {Boolean} active panel active
19521  * @cfg {String} html panel content
19522  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
19523  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
19524  * @cfg {String} href click to link..
19525  * 
19526  * 
19527  * @constructor
19528  * Create a new TabPanel
19529  * @param {Object} config The config object
19530  */
19531
19532 Roo.bootstrap.TabPanel = function(config){
19533     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
19534     this.addEvents({
19535         /**
19536              * @event changed
19537              * Fires when the active status changes
19538              * @param {Roo.bootstrap.TabPanel} this
19539              * @param {Boolean} state the new state
19540             
19541          */
19542         'changed': true,
19543         /**
19544              * @event beforedeactivate
19545              * Fires before a tab is de-activated - can be used to do validation on a form.
19546              * @param {Roo.bootstrap.TabPanel} this
19547              * @return {Boolean} false if there is an error
19548             
19549          */
19550         'beforedeactivate': true
19551      });
19552     
19553     this.tabId = this.tabId || Roo.id();
19554   
19555 };
19556
19557 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
19558     
19559     active: false,
19560     html: false,
19561     tabId: false,
19562     navId : false,
19563     href : '',
19564     
19565     getAutoCreate : function(){
19566         
19567         
19568         var cfg = {
19569             tag: 'div',
19570             // item is needed for carousel - not sure if it has any effect otherwise
19571             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
19572             html: this.html || ''
19573         };
19574         
19575         if(this.active){
19576             cfg.cls += ' active';
19577         }
19578         
19579         if(this.tabId){
19580             cfg.tabId = this.tabId;
19581         }
19582         
19583         
19584         
19585         return cfg;
19586     },
19587     
19588     initEvents:  function()
19589     {
19590         var p = this.parent();
19591         
19592         this.navId = this.navId || p.navId;
19593         
19594         if (typeof(this.navId) != 'undefined') {
19595             // not really needed.. but just in case.. parent should be a NavGroup.
19596             var tg = Roo.bootstrap.TabGroup.get(this.navId);
19597             
19598             tg.register(this);
19599             
19600             var i = tg.tabs.length - 1;
19601             
19602             if(this.active && tg.bullets > 0 && i < tg.bullets){
19603                 tg.setActiveBullet(i);
19604             }
19605         }
19606         
19607         this.el.on('click', this.onClick, this);
19608         
19609         if(Roo.isTouch){
19610             this.el.on("touchstart", this.onTouchStart, this);
19611             this.el.on("touchmove", this.onTouchMove, this);
19612             this.el.on("touchend", this.onTouchEnd, this);
19613         }
19614         
19615     },
19616     
19617     onRender : function(ct, position)
19618     {
19619         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
19620     },
19621     
19622     setActive : function(state)
19623     {
19624         Roo.log("panel - set active " + this.tabId + "=" + state);
19625         
19626         this.active = state;
19627         if (!state) {
19628             this.el.removeClass('active');
19629             
19630         } else  if (!this.el.hasClass('active')) {
19631             this.el.addClass('active');
19632         }
19633         
19634         this.fireEvent('changed', this, state);
19635     },
19636     
19637     onClick : function(e)
19638     {
19639         e.preventDefault();
19640         
19641         if(!this.href.length){
19642             return;
19643         }
19644         
19645         window.location.href = this.href;
19646     },
19647     
19648     startX : 0,
19649     startY : 0,
19650     endX : 0,
19651     endY : 0,
19652     swiping : false,
19653     
19654     onTouchStart : function(e)
19655     {
19656         this.swiping = false;
19657         
19658         this.startX = e.browserEvent.touches[0].clientX;
19659         this.startY = e.browserEvent.touches[0].clientY;
19660     },
19661     
19662     onTouchMove : function(e)
19663     {
19664         this.swiping = true;
19665         
19666         this.endX = e.browserEvent.touches[0].clientX;
19667         this.endY = e.browserEvent.touches[0].clientY;
19668     },
19669     
19670     onTouchEnd : function(e)
19671     {
19672         if(!this.swiping){
19673             this.onClick(e);
19674             return;
19675         }
19676         
19677         var tabGroup = this.parent();
19678         
19679         if(this.endX > this.startX){ // swiping right
19680             tabGroup.showPanelPrev();
19681             return;
19682         }
19683         
19684         if(this.startX > this.endX){ // swiping left
19685             tabGroup.showPanelNext();
19686             return;
19687         }
19688     }
19689     
19690     
19691 });
19692  
19693
19694  
19695
19696  /*
19697  * - LGPL
19698  *
19699  * DateField
19700  * 
19701  */
19702
19703 /**
19704  * @class Roo.bootstrap.DateField
19705  * @extends Roo.bootstrap.Input
19706  * Bootstrap DateField class
19707  * @cfg {Number} weekStart default 0
19708  * @cfg {String} viewMode default empty, (months|years)
19709  * @cfg {String} minViewMode default empty, (months|years)
19710  * @cfg {Number} startDate default -Infinity
19711  * @cfg {Number} endDate default Infinity
19712  * @cfg {Boolean} todayHighlight default false
19713  * @cfg {Boolean} todayBtn default false
19714  * @cfg {Boolean} calendarWeeks default false
19715  * @cfg {Object} daysOfWeekDisabled default empty
19716  * @cfg {Boolean} singleMode default false (true | false)
19717  * 
19718  * @cfg {Boolean} keyboardNavigation default true
19719  * @cfg {String} language default en
19720  * 
19721  * @constructor
19722  * Create a new DateField
19723  * @param {Object} config The config object
19724  */
19725
19726 Roo.bootstrap.DateField = function(config){
19727     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
19728      this.addEvents({
19729             /**
19730              * @event show
19731              * Fires when this field show.
19732              * @param {Roo.bootstrap.DateField} this
19733              * @param {Mixed} date The date value
19734              */
19735             show : true,
19736             /**
19737              * @event show
19738              * Fires when this field hide.
19739              * @param {Roo.bootstrap.DateField} this
19740              * @param {Mixed} date The date value
19741              */
19742             hide : true,
19743             /**
19744              * @event select
19745              * Fires when select a date.
19746              * @param {Roo.bootstrap.DateField} this
19747              * @param {Mixed} date The date value
19748              */
19749             select : true,
19750             /**
19751              * @event beforeselect
19752              * Fires when before select a date.
19753              * @param {Roo.bootstrap.DateField} this
19754              * @param {Mixed} date The date value
19755              */
19756             beforeselect : true
19757         });
19758 };
19759
19760 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
19761     
19762     /**
19763      * @cfg {String} format
19764      * The default date format string which can be overriden for localization support.  The format must be
19765      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
19766      */
19767     format : "m/d/y",
19768     /**
19769      * @cfg {String} altFormats
19770      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
19771      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
19772      */
19773     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
19774     
19775     weekStart : 0,
19776     
19777     viewMode : '',
19778     
19779     minViewMode : '',
19780     
19781     todayHighlight : false,
19782     
19783     todayBtn: false,
19784     
19785     language: 'en',
19786     
19787     keyboardNavigation: true,
19788     
19789     calendarWeeks: false,
19790     
19791     startDate: -Infinity,
19792     
19793     endDate: Infinity,
19794     
19795     daysOfWeekDisabled: [],
19796     
19797     _events: [],
19798     
19799     singleMode : false,
19800     
19801     UTCDate: function()
19802     {
19803         return new Date(Date.UTC.apply(Date, arguments));
19804     },
19805     
19806     UTCToday: function()
19807     {
19808         var today = new Date();
19809         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
19810     },
19811     
19812     getDate: function() {
19813             var d = this.getUTCDate();
19814             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
19815     },
19816     
19817     getUTCDate: function() {
19818             return this.date;
19819     },
19820     
19821     setDate: function(d) {
19822             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
19823     },
19824     
19825     setUTCDate: function(d) {
19826             this.date = d;
19827             this.setValue(this.formatDate(this.date));
19828     },
19829         
19830     onRender: function(ct, position)
19831     {
19832         
19833         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
19834         
19835         this.language = this.language || 'en';
19836         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
19837         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
19838         
19839         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
19840         this.format = this.format || 'm/d/y';
19841         this.isInline = false;
19842         this.isInput = true;
19843         this.component = this.el.select('.add-on', true).first() || false;
19844         this.component = (this.component && this.component.length === 0) ? false : this.component;
19845         this.hasInput = this.component && this.inputEl().length;
19846         
19847         if (typeof(this.minViewMode === 'string')) {
19848             switch (this.minViewMode) {
19849                 case 'months':
19850                     this.minViewMode = 1;
19851                     break;
19852                 case 'years':
19853                     this.minViewMode = 2;
19854                     break;
19855                 default:
19856                     this.minViewMode = 0;
19857                     break;
19858             }
19859         }
19860         
19861         if (typeof(this.viewMode === 'string')) {
19862             switch (this.viewMode) {
19863                 case 'months':
19864                     this.viewMode = 1;
19865                     break;
19866                 case 'years':
19867                     this.viewMode = 2;
19868                     break;
19869                 default:
19870                     this.viewMode = 0;
19871                     break;
19872             }
19873         }
19874                 
19875         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
19876         
19877 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
19878         
19879         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19880         
19881         this.picker().on('mousedown', this.onMousedown, this);
19882         this.picker().on('click', this.onClick, this);
19883         
19884         this.picker().addClass('datepicker-dropdown');
19885         
19886         this.startViewMode = this.viewMode;
19887         
19888         if(this.singleMode){
19889             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
19890                 v.setVisibilityMode(Roo.Element.DISPLAY);
19891                 v.hide();
19892             });
19893             
19894             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19895                 v.setStyle('width', '189px');
19896             });
19897         }
19898         
19899         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
19900             if(!this.calendarWeeks){
19901                 v.remove();
19902                 return;
19903             }
19904             
19905             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19906             v.attr('colspan', function(i, val){
19907                 return parseInt(val) + 1;
19908             });
19909         });
19910                         
19911         
19912         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
19913         
19914         this.setStartDate(this.startDate);
19915         this.setEndDate(this.endDate);
19916         
19917         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
19918         
19919         this.fillDow();
19920         this.fillMonths();
19921         this.update();
19922         this.showMode();
19923         
19924         if(this.isInline) {
19925             this.showPopup();
19926         }
19927     },
19928     
19929     picker : function()
19930     {
19931         return this.pickerEl;
19932 //        return this.el.select('.datepicker', true).first();
19933     },
19934     
19935     fillDow: function()
19936     {
19937         var dowCnt = this.weekStart;
19938         
19939         var dow = {
19940             tag: 'tr',
19941             cn: [
19942                 
19943             ]
19944         };
19945         
19946         if(this.calendarWeeks){
19947             dow.cn.push({
19948                 tag: 'th',
19949                 cls: 'cw',
19950                 html: '&nbsp;'
19951             })
19952         }
19953         
19954         while (dowCnt < this.weekStart + 7) {
19955             dow.cn.push({
19956                 tag: 'th',
19957                 cls: 'dow',
19958                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
19959             });
19960         }
19961         
19962         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
19963     },
19964     
19965     fillMonths: function()
19966     {    
19967         var i = 0;
19968         var months = this.picker().select('>.datepicker-months td', true).first();
19969         
19970         months.dom.innerHTML = '';
19971         
19972         while (i < 12) {
19973             var month = {
19974                 tag: 'span',
19975                 cls: 'month',
19976                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
19977             };
19978             
19979             months.createChild(month);
19980         }
19981         
19982     },
19983     
19984     update: function()
19985     {
19986         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;
19987         
19988         if (this.date < this.startDate) {
19989             this.viewDate = new Date(this.startDate);
19990         } else if (this.date > this.endDate) {
19991             this.viewDate = new Date(this.endDate);
19992         } else {
19993             this.viewDate = new Date(this.date);
19994         }
19995         
19996         this.fill();
19997     },
19998     
19999     fill: function() 
20000     {
20001         var d = new Date(this.viewDate),
20002                 year = d.getUTCFullYear(),
20003                 month = d.getUTCMonth(),
20004                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
20005                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
20006                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
20007                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
20008                 currentDate = this.date && this.date.valueOf(),
20009                 today = this.UTCToday();
20010         
20011         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
20012         
20013 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20014         
20015 //        this.picker.select('>tfoot th.today').
20016 //                                              .text(dates[this.language].today)
20017 //                                              .toggle(this.todayBtn !== false);
20018     
20019         this.updateNavArrows();
20020         this.fillMonths();
20021                                                 
20022         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
20023         
20024         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
20025          
20026         prevMonth.setUTCDate(day);
20027         
20028         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
20029         
20030         var nextMonth = new Date(prevMonth);
20031         
20032         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
20033         
20034         nextMonth = nextMonth.valueOf();
20035         
20036         var fillMonths = false;
20037         
20038         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
20039         
20040         while(prevMonth.valueOf() <= nextMonth) {
20041             var clsName = '';
20042             
20043             if (prevMonth.getUTCDay() === this.weekStart) {
20044                 if(fillMonths){
20045                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
20046                 }
20047                     
20048                 fillMonths = {
20049                     tag: 'tr',
20050                     cn: []
20051                 };
20052                 
20053                 if(this.calendarWeeks){
20054                     // ISO 8601: First week contains first thursday.
20055                     // ISO also states week starts on Monday, but we can be more abstract here.
20056                     var
20057                     // Start of current week: based on weekstart/current date
20058                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
20059                     // Thursday of this week
20060                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
20061                     // First Thursday of year, year from thursday
20062                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
20063                     // Calendar week: ms between thursdays, div ms per day, div 7 days
20064                     calWeek =  (th - yth) / 864e5 / 7 + 1;
20065                     
20066                     fillMonths.cn.push({
20067                         tag: 'td',
20068                         cls: 'cw',
20069                         html: calWeek
20070                     });
20071                 }
20072             }
20073             
20074             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
20075                 clsName += ' old';
20076             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
20077                 clsName += ' new';
20078             }
20079             if (this.todayHighlight &&
20080                 prevMonth.getUTCFullYear() == today.getFullYear() &&
20081                 prevMonth.getUTCMonth() == today.getMonth() &&
20082                 prevMonth.getUTCDate() == today.getDate()) {
20083                 clsName += ' today';
20084             }
20085             
20086             if (currentDate && prevMonth.valueOf() === currentDate) {
20087                 clsName += ' active';
20088             }
20089             
20090             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
20091                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
20092                     clsName += ' disabled';
20093             }
20094             
20095             fillMonths.cn.push({
20096                 tag: 'td',
20097                 cls: 'day ' + clsName,
20098                 html: prevMonth.getDate()
20099             });
20100             
20101             prevMonth.setDate(prevMonth.getDate()+1);
20102         }
20103           
20104         var currentYear = this.date && this.date.getUTCFullYear();
20105         var currentMonth = this.date && this.date.getUTCMonth();
20106         
20107         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
20108         
20109         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
20110             v.removeClass('active');
20111             
20112             if(currentYear === year && k === currentMonth){
20113                 v.addClass('active');
20114             }
20115             
20116             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
20117                 v.addClass('disabled');
20118             }
20119             
20120         });
20121         
20122         
20123         year = parseInt(year/10, 10) * 10;
20124         
20125         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
20126         
20127         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
20128         
20129         year -= 1;
20130         for (var i = -1; i < 11; i++) {
20131             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
20132                 tag: 'span',
20133                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
20134                 html: year
20135             });
20136             
20137             year += 1;
20138         }
20139     },
20140     
20141     showMode: function(dir) 
20142     {
20143         if (dir) {
20144             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
20145         }
20146         
20147         Roo.each(this.picker().select('>div',true).elements, function(v){
20148             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20149             v.hide();
20150         });
20151         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
20152     },
20153     
20154     place: function()
20155     {
20156         if(this.isInline) {
20157             return;
20158         }
20159         
20160         this.picker().removeClass(['bottom', 'top']);
20161         
20162         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20163             /*
20164              * place to the top of element!
20165              *
20166              */
20167             
20168             this.picker().addClass('top');
20169             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20170             
20171             return;
20172         }
20173         
20174         this.picker().addClass('bottom');
20175         
20176         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20177     },
20178     
20179     parseDate : function(value)
20180     {
20181         if(!value || value instanceof Date){
20182             return value;
20183         }
20184         var v = Date.parseDate(value, this.format);
20185         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
20186             v = Date.parseDate(value, 'Y-m-d');
20187         }
20188         if(!v && this.altFormats){
20189             if(!this.altFormatsArray){
20190                 this.altFormatsArray = this.altFormats.split("|");
20191             }
20192             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
20193                 v = Date.parseDate(value, this.altFormatsArray[i]);
20194             }
20195         }
20196         return v;
20197     },
20198     
20199     formatDate : function(date, fmt)
20200     {   
20201         return (!date || !(date instanceof Date)) ?
20202         date : date.dateFormat(fmt || this.format);
20203     },
20204     
20205     onFocus : function()
20206     {
20207         Roo.bootstrap.DateField.superclass.onFocus.call(this);
20208         this.showPopup();
20209     },
20210     
20211     onBlur : function()
20212     {
20213         Roo.bootstrap.DateField.superclass.onBlur.call(this);
20214         
20215         var d = this.inputEl().getValue();
20216         
20217         this.setValue(d);
20218                 
20219         this.hidePopup();
20220     },
20221     
20222     showPopup : function()
20223     {
20224         this.picker().show();
20225         this.update();
20226         this.place();
20227         
20228         this.fireEvent('showpopup', this, this.date);
20229     },
20230     
20231     hidePopup : function()
20232     {
20233         if(this.isInline) {
20234             return;
20235         }
20236         this.picker().hide();
20237         this.viewMode = this.startViewMode;
20238         this.showMode();
20239         
20240         this.fireEvent('hidepopup', this, this.date);
20241         
20242     },
20243     
20244     onMousedown: function(e)
20245     {
20246         e.stopPropagation();
20247         e.preventDefault();
20248     },
20249     
20250     keyup: function(e)
20251     {
20252         Roo.bootstrap.DateField.superclass.keyup.call(this);
20253         this.update();
20254     },
20255
20256     setValue: function(v)
20257     {
20258         if(this.fireEvent('beforeselect', this, v) !== false){
20259             var d = new Date(this.parseDate(v) ).clearTime();
20260         
20261             if(isNaN(d.getTime())){
20262                 this.date = this.viewDate = '';
20263                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
20264                 return;
20265             }
20266
20267             v = this.formatDate(d);
20268
20269             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
20270
20271             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
20272
20273             this.update();
20274
20275             this.fireEvent('select', this, this.date);
20276         }
20277     },
20278     
20279     getValue: function()
20280     {
20281         return this.formatDate(this.date);
20282     },
20283     
20284     fireKey: function(e)
20285     {
20286         if (!this.picker().isVisible()){
20287             if (e.keyCode == 27) { // allow escape to hide and re-show picker
20288                 this.showPopup();
20289             }
20290             return;
20291         }
20292         
20293         var dateChanged = false,
20294         dir, day, month,
20295         newDate, newViewDate;
20296         
20297         switch(e.keyCode){
20298             case 27: // escape
20299                 this.hidePopup();
20300                 e.preventDefault();
20301                 break;
20302             case 37: // left
20303             case 39: // right
20304                 if (!this.keyboardNavigation) {
20305                     break;
20306                 }
20307                 dir = e.keyCode == 37 ? -1 : 1;
20308                 
20309                 if (e.ctrlKey){
20310                     newDate = this.moveYear(this.date, dir);
20311                     newViewDate = this.moveYear(this.viewDate, dir);
20312                 } else if (e.shiftKey){
20313                     newDate = this.moveMonth(this.date, dir);
20314                     newViewDate = this.moveMonth(this.viewDate, dir);
20315                 } else {
20316                     newDate = new Date(this.date);
20317                     newDate.setUTCDate(this.date.getUTCDate() + dir);
20318                     newViewDate = new Date(this.viewDate);
20319                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
20320                 }
20321                 if (this.dateWithinRange(newDate)){
20322                     this.date = newDate;
20323                     this.viewDate = newViewDate;
20324                     this.setValue(this.formatDate(this.date));
20325 //                    this.update();
20326                     e.preventDefault();
20327                     dateChanged = true;
20328                 }
20329                 break;
20330             case 38: // up
20331             case 40: // down
20332                 if (!this.keyboardNavigation) {
20333                     break;
20334                 }
20335                 dir = e.keyCode == 38 ? -1 : 1;
20336                 if (e.ctrlKey){
20337                     newDate = this.moveYear(this.date, dir);
20338                     newViewDate = this.moveYear(this.viewDate, dir);
20339                 } else if (e.shiftKey){
20340                     newDate = this.moveMonth(this.date, dir);
20341                     newViewDate = this.moveMonth(this.viewDate, dir);
20342                 } else {
20343                     newDate = new Date(this.date);
20344                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
20345                     newViewDate = new Date(this.viewDate);
20346                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
20347                 }
20348                 if (this.dateWithinRange(newDate)){
20349                     this.date = newDate;
20350                     this.viewDate = newViewDate;
20351                     this.setValue(this.formatDate(this.date));
20352 //                    this.update();
20353                     e.preventDefault();
20354                     dateChanged = true;
20355                 }
20356                 break;
20357             case 13: // enter
20358                 this.setValue(this.formatDate(this.date));
20359                 this.hidePopup();
20360                 e.preventDefault();
20361                 break;
20362             case 9: // tab
20363                 this.setValue(this.formatDate(this.date));
20364                 this.hidePopup();
20365                 break;
20366             case 16: // shift
20367             case 17: // ctrl
20368             case 18: // alt
20369                 break;
20370             default :
20371                 this.hidePopup();
20372                 
20373         }
20374     },
20375     
20376     
20377     onClick: function(e) 
20378     {
20379         e.stopPropagation();
20380         e.preventDefault();
20381         
20382         var target = e.getTarget();
20383         
20384         if(target.nodeName.toLowerCase() === 'i'){
20385             target = Roo.get(target).dom.parentNode;
20386         }
20387         
20388         var nodeName = target.nodeName;
20389         var className = target.className;
20390         var html = target.innerHTML;
20391         //Roo.log(nodeName);
20392         
20393         switch(nodeName.toLowerCase()) {
20394             case 'th':
20395                 switch(className) {
20396                     case 'switch':
20397                         this.showMode(1);
20398                         break;
20399                     case 'prev':
20400                     case 'next':
20401                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
20402                         switch(this.viewMode){
20403                                 case 0:
20404                                         this.viewDate = this.moveMonth(this.viewDate, dir);
20405                                         break;
20406                                 case 1:
20407                                 case 2:
20408                                         this.viewDate = this.moveYear(this.viewDate, dir);
20409                                         break;
20410                         }
20411                         this.fill();
20412                         break;
20413                     case 'today':
20414                         var date = new Date();
20415                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
20416 //                        this.fill()
20417                         this.setValue(this.formatDate(this.date));
20418                         
20419                         this.hidePopup();
20420                         break;
20421                 }
20422                 break;
20423             case 'span':
20424                 if (className.indexOf('disabled') < 0) {
20425                     this.viewDate.setUTCDate(1);
20426                     if (className.indexOf('month') > -1) {
20427                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
20428                     } else {
20429                         var year = parseInt(html, 10) || 0;
20430                         this.viewDate.setUTCFullYear(year);
20431                         
20432                     }
20433                     
20434                     if(this.singleMode){
20435                         this.setValue(this.formatDate(this.viewDate));
20436                         this.hidePopup();
20437                         return;
20438                     }
20439                     
20440                     this.showMode(-1);
20441                     this.fill();
20442                 }
20443                 break;
20444                 
20445             case 'td':
20446                 //Roo.log(className);
20447                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
20448                     var day = parseInt(html, 10) || 1;
20449                     var year = this.viewDate.getUTCFullYear(),
20450                         month = this.viewDate.getUTCMonth();
20451
20452                     if (className.indexOf('old') > -1) {
20453                         if(month === 0 ){
20454                             month = 11;
20455                             year -= 1;
20456                         }else{
20457                             month -= 1;
20458                         }
20459                     } else if (className.indexOf('new') > -1) {
20460                         if (month == 11) {
20461                             month = 0;
20462                             year += 1;
20463                         } else {
20464                             month += 1;
20465                         }
20466                     }
20467                     //Roo.log([year,month,day]);
20468                     this.date = this.UTCDate(year, month, day,0,0,0,0);
20469                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
20470 //                    this.fill();
20471                     //Roo.log(this.formatDate(this.date));
20472                     this.setValue(this.formatDate(this.date));
20473                     this.hidePopup();
20474                 }
20475                 break;
20476         }
20477     },
20478     
20479     setStartDate: function(startDate)
20480     {
20481         this.startDate = startDate || -Infinity;
20482         if (this.startDate !== -Infinity) {
20483             this.startDate = this.parseDate(this.startDate);
20484         }
20485         this.update();
20486         this.updateNavArrows();
20487     },
20488
20489     setEndDate: function(endDate)
20490     {
20491         this.endDate = endDate || Infinity;
20492         if (this.endDate !== Infinity) {
20493             this.endDate = this.parseDate(this.endDate);
20494         }
20495         this.update();
20496         this.updateNavArrows();
20497     },
20498     
20499     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
20500     {
20501         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
20502         if (typeof(this.daysOfWeekDisabled) !== 'object') {
20503             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
20504         }
20505         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
20506             return parseInt(d, 10);
20507         });
20508         this.update();
20509         this.updateNavArrows();
20510     },
20511     
20512     updateNavArrows: function() 
20513     {
20514         if(this.singleMode){
20515             return;
20516         }
20517         
20518         var d = new Date(this.viewDate),
20519         year = d.getUTCFullYear(),
20520         month = d.getUTCMonth();
20521         
20522         Roo.each(this.picker().select('.prev', true).elements, function(v){
20523             v.show();
20524             switch (this.viewMode) {
20525                 case 0:
20526
20527                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
20528                         v.hide();
20529                     }
20530                     break;
20531                 case 1:
20532                 case 2:
20533                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
20534                         v.hide();
20535                     }
20536                     break;
20537             }
20538         });
20539         
20540         Roo.each(this.picker().select('.next', true).elements, function(v){
20541             v.show();
20542             switch (this.viewMode) {
20543                 case 0:
20544
20545                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
20546                         v.hide();
20547                     }
20548                     break;
20549                 case 1:
20550                 case 2:
20551                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
20552                         v.hide();
20553                     }
20554                     break;
20555             }
20556         })
20557     },
20558     
20559     moveMonth: function(date, dir)
20560     {
20561         if (!dir) {
20562             return date;
20563         }
20564         var new_date = new Date(date.valueOf()),
20565         day = new_date.getUTCDate(),
20566         month = new_date.getUTCMonth(),
20567         mag = Math.abs(dir),
20568         new_month, test;
20569         dir = dir > 0 ? 1 : -1;
20570         if (mag == 1){
20571             test = dir == -1
20572             // If going back one month, make sure month is not current month
20573             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
20574             ? function(){
20575                 return new_date.getUTCMonth() == month;
20576             }
20577             // If going forward one month, make sure month is as expected
20578             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
20579             : function(){
20580                 return new_date.getUTCMonth() != new_month;
20581             };
20582             new_month = month + dir;
20583             new_date.setUTCMonth(new_month);
20584             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
20585             if (new_month < 0 || new_month > 11) {
20586                 new_month = (new_month + 12) % 12;
20587             }
20588         } else {
20589             // For magnitudes >1, move one month at a time...
20590             for (var i=0; i<mag; i++) {
20591                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
20592                 new_date = this.moveMonth(new_date, dir);
20593             }
20594             // ...then reset the day, keeping it in the new month
20595             new_month = new_date.getUTCMonth();
20596             new_date.setUTCDate(day);
20597             test = function(){
20598                 return new_month != new_date.getUTCMonth();
20599             };
20600         }
20601         // Common date-resetting loop -- if date is beyond end of month, make it
20602         // end of month
20603         while (test()){
20604             new_date.setUTCDate(--day);
20605             new_date.setUTCMonth(new_month);
20606         }
20607         return new_date;
20608     },
20609
20610     moveYear: function(date, dir)
20611     {
20612         return this.moveMonth(date, dir*12);
20613     },
20614
20615     dateWithinRange: function(date)
20616     {
20617         return date >= this.startDate && date <= this.endDate;
20618     },
20619
20620     
20621     remove: function() 
20622     {
20623         this.picker().remove();
20624     },
20625     
20626     validateValue : function(value)
20627     {
20628         if(this.getVisibilityEl().hasClass('hidden')){
20629             return true;
20630         }
20631         
20632         if(value.length < 1)  {
20633             if(this.allowBlank){
20634                 return true;
20635             }
20636             return false;
20637         }
20638         
20639         if(value.length < this.minLength){
20640             return false;
20641         }
20642         if(value.length > this.maxLength){
20643             return false;
20644         }
20645         if(this.vtype){
20646             var vt = Roo.form.VTypes;
20647             if(!vt[this.vtype](value, this)){
20648                 return false;
20649             }
20650         }
20651         if(typeof this.validator == "function"){
20652             var msg = this.validator(value);
20653             if(msg !== true){
20654                 return false;
20655             }
20656         }
20657         
20658         if(this.regex && !this.regex.test(value)){
20659             return false;
20660         }
20661         
20662         if(typeof(this.parseDate(value)) == 'undefined'){
20663             return false;
20664         }
20665         
20666         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
20667             return false;
20668         }      
20669         
20670         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
20671             return false;
20672         } 
20673         
20674         
20675         return true;
20676     },
20677     
20678     reset : function()
20679     {
20680         this.date = this.viewDate = '';
20681         
20682         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
20683     }
20684    
20685 });
20686
20687 Roo.apply(Roo.bootstrap.DateField,  {
20688     
20689     head : {
20690         tag: 'thead',
20691         cn: [
20692         {
20693             tag: 'tr',
20694             cn: [
20695             {
20696                 tag: 'th',
20697                 cls: 'prev',
20698                 html: '<i class="fa fa-arrow-left"/>'
20699             },
20700             {
20701                 tag: 'th',
20702                 cls: 'switch',
20703                 colspan: '5'
20704             },
20705             {
20706                 tag: 'th',
20707                 cls: 'next',
20708                 html: '<i class="fa fa-arrow-right"/>'
20709             }
20710
20711             ]
20712         }
20713         ]
20714     },
20715     
20716     content : {
20717         tag: 'tbody',
20718         cn: [
20719         {
20720             tag: 'tr',
20721             cn: [
20722             {
20723                 tag: 'td',
20724                 colspan: '7'
20725             }
20726             ]
20727         }
20728         ]
20729     },
20730     
20731     footer : {
20732         tag: 'tfoot',
20733         cn: [
20734         {
20735             tag: 'tr',
20736             cn: [
20737             {
20738                 tag: 'th',
20739                 colspan: '7',
20740                 cls: 'today'
20741             }
20742                     
20743             ]
20744         }
20745         ]
20746     },
20747     
20748     dates:{
20749         en: {
20750             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
20751             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
20752             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
20753             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20754             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
20755             today: "Today"
20756         }
20757     },
20758     
20759     modes: [
20760     {
20761         clsName: 'days',
20762         navFnc: 'Month',
20763         navStep: 1
20764     },
20765     {
20766         clsName: 'months',
20767         navFnc: 'FullYear',
20768         navStep: 1
20769     },
20770     {
20771         clsName: 'years',
20772         navFnc: 'FullYear',
20773         navStep: 10
20774     }]
20775 });
20776
20777 Roo.apply(Roo.bootstrap.DateField,  {
20778   
20779     template : {
20780         tag: 'div',
20781         cls: 'datepicker dropdown-menu roo-dynamic',
20782         cn: [
20783         {
20784             tag: 'div',
20785             cls: 'datepicker-days',
20786             cn: [
20787             {
20788                 tag: 'table',
20789                 cls: 'table-condensed',
20790                 cn:[
20791                 Roo.bootstrap.DateField.head,
20792                 {
20793                     tag: 'tbody'
20794                 },
20795                 Roo.bootstrap.DateField.footer
20796                 ]
20797             }
20798             ]
20799         },
20800         {
20801             tag: 'div',
20802             cls: 'datepicker-months',
20803             cn: [
20804             {
20805                 tag: 'table',
20806                 cls: 'table-condensed',
20807                 cn:[
20808                 Roo.bootstrap.DateField.head,
20809                 Roo.bootstrap.DateField.content,
20810                 Roo.bootstrap.DateField.footer
20811                 ]
20812             }
20813             ]
20814         },
20815         {
20816             tag: 'div',
20817             cls: 'datepicker-years',
20818             cn: [
20819             {
20820                 tag: 'table',
20821                 cls: 'table-condensed',
20822                 cn:[
20823                 Roo.bootstrap.DateField.head,
20824                 Roo.bootstrap.DateField.content,
20825                 Roo.bootstrap.DateField.footer
20826                 ]
20827             }
20828             ]
20829         }
20830         ]
20831     }
20832 });
20833
20834  
20835
20836  /*
20837  * - LGPL
20838  *
20839  * TimeField
20840  * 
20841  */
20842
20843 /**
20844  * @class Roo.bootstrap.TimeField
20845  * @extends Roo.bootstrap.Input
20846  * Bootstrap DateField class
20847  * 
20848  * 
20849  * @constructor
20850  * Create a new TimeField
20851  * @param {Object} config The config object
20852  */
20853
20854 Roo.bootstrap.TimeField = function(config){
20855     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
20856     this.addEvents({
20857             /**
20858              * @event show
20859              * Fires when this field show.
20860              * @param {Roo.bootstrap.DateField} thisthis
20861              * @param {Mixed} date The date value
20862              */
20863             show : true,
20864             /**
20865              * @event show
20866              * Fires when this field hide.
20867              * @param {Roo.bootstrap.DateField} this
20868              * @param {Mixed} date The date value
20869              */
20870             hide : true,
20871             /**
20872              * @event select
20873              * Fires when select a date.
20874              * @param {Roo.bootstrap.DateField} this
20875              * @param {Mixed} date The date value
20876              */
20877             select : true
20878         });
20879 };
20880
20881 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
20882     
20883     /**
20884      * @cfg {String} format
20885      * The default time format string which can be overriden for localization support.  The format must be
20886      * valid according to {@link Date#parseDate} (defaults to 'H:i').
20887      */
20888     format : "H:i",
20889        
20890     onRender: function(ct, position)
20891     {
20892         
20893         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
20894                 
20895         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
20896         
20897         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20898         
20899         this.pop = this.picker().select('>.datepicker-time',true).first();
20900         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20901         
20902         this.picker().on('mousedown', this.onMousedown, this);
20903         this.picker().on('click', this.onClick, this);
20904         
20905         this.picker().addClass('datepicker-dropdown');
20906     
20907         this.fillTime();
20908         this.update();
20909             
20910         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
20911         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
20912         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
20913         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
20914         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
20915         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
20916
20917     },
20918     
20919     fireKey: function(e){
20920         if (!this.picker().isVisible()){
20921             if (e.keyCode == 27) { // allow escape to hide and re-show picker
20922                 this.show();
20923             }
20924             return;
20925         }
20926
20927         e.preventDefault();
20928         
20929         switch(e.keyCode){
20930             case 27: // escape
20931                 this.hide();
20932                 break;
20933             case 37: // left
20934             case 39: // right
20935                 this.onTogglePeriod();
20936                 break;
20937             case 38: // up
20938                 this.onIncrementMinutes();
20939                 break;
20940             case 40: // down
20941                 this.onDecrementMinutes();
20942                 break;
20943             case 13: // enter
20944             case 9: // tab
20945                 this.setTime();
20946                 break;
20947         }
20948     },
20949     
20950     onClick: function(e) {
20951         e.stopPropagation();
20952         e.preventDefault();
20953     },
20954     
20955     picker : function()
20956     {
20957         return this.el.select('.datepicker', true).first();
20958     },
20959     
20960     fillTime: function()
20961     {    
20962         var time = this.pop.select('tbody', true).first();
20963         
20964         time.dom.innerHTML = '';
20965         
20966         time.createChild({
20967             tag: 'tr',
20968             cn: [
20969                 {
20970                     tag: 'td',
20971                     cn: [
20972                         {
20973                             tag: 'a',
20974                             href: '#',
20975                             cls: 'btn',
20976                             cn: [
20977                                 {
20978                                     tag: 'span',
20979                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
20980                                 }
20981                             ]
20982                         } 
20983                     ]
20984                 },
20985                 {
20986                     tag: 'td',
20987                     cls: 'separator'
20988                 },
20989                 {
20990                     tag: 'td',
20991                     cn: [
20992                         {
20993                             tag: 'a',
20994                             href: '#',
20995                             cls: 'btn',
20996                             cn: [
20997                                 {
20998                                     tag: 'span',
20999                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
21000                                 }
21001                             ]
21002                         }
21003                     ]
21004                 },
21005                 {
21006                     tag: 'td',
21007                     cls: 'separator'
21008                 }
21009             ]
21010         });
21011         
21012         time.createChild({
21013             tag: 'tr',
21014             cn: [
21015                 {
21016                     tag: 'td',
21017                     cn: [
21018                         {
21019                             tag: 'span',
21020                             cls: 'timepicker-hour',
21021                             html: '00'
21022                         }  
21023                     ]
21024                 },
21025                 {
21026                     tag: 'td',
21027                     cls: 'separator',
21028                     html: ':'
21029                 },
21030                 {
21031                     tag: 'td',
21032                     cn: [
21033                         {
21034                             tag: 'span',
21035                             cls: 'timepicker-minute',
21036                             html: '00'
21037                         }  
21038                     ]
21039                 },
21040                 {
21041                     tag: 'td',
21042                     cls: 'separator'
21043                 },
21044                 {
21045                     tag: 'td',
21046                     cn: [
21047                         {
21048                             tag: 'button',
21049                             type: 'button',
21050                             cls: 'btn btn-primary period',
21051                             html: 'AM'
21052                             
21053                         }
21054                     ]
21055                 }
21056             ]
21057         });
21058         
21059         time.createChild({
21060             tag: 'tr',
21061             cn: [
21062                 {
21063                     tag: 'td',
21064                     cn: [
21065                         {
21066                             tag: 'a',
21067                             href: '#',
21068                             cls: 'btn',
21069                             cn: [
21070                                 {
21071                                     tag: 'span',
21072                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
21073                                 }
21074                             ]
21075                         }
21076                     ]
21077                 },
21078                 {
21079                     tag: 'td',
21080                     cls: 'separator'
21081                 },
21082                 {
21083                     tag: 'td',
21084                     cn: [
21085                         {
21086                             tag: 'a',
21087                             href: '#',
21088                             cls: 'btn',
21089                             cn: [
21090                                 {
21091                                     tag: 'span',
21092                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
21093                                 }
21094                             ]
21095                         }
21096                     ]
21097                 },
21098                 {
21099                     tag: 'td',
21100                     cls: 'separator'
21101                 }
21102             ]
21103         });
21104         
21105     },
21106     
21107     update: function()
21108     {
21109         
21110         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
21111         
21112         this.fill();
21113     },
21114     
21115     fill: function() 
21116     {
21117         var hours = this.time.getHours();
21118         var minutes = this.time.getMinutes();
21119         var period = 'AM';
21120         
21121         if(hours > 11){
21122             period = 'PM';
21123         }
21124         
21125         if(hours == 0){
21126             hours = 12;
21127         }
21128         
21129         
21130         if(hours > 12){
21131             hours = hours - 12;
21132         }
21133         
21134         if(hours < 10){
21135             hours = '0' + hours;
21136         }
21137         
21138         if(minutes < 10){
21139             minutes = '0' + minutes;
21140         }
21141         
21142         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
21143         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
21144         this.pop.select('button', true).first().dom.innerHTML = period;
21145         
21146     },
21147     
21148     place: function()
21149     {   
21150         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
21151         
21152         var cls = ['bottom'];
21153         
21154         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
21155             cls.pop();
21156             cls.push('top');
21157         }
21158         
21159         cls.push('right');
21160         
21161         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
21162             cls.pop();
21163             cls.push('left');
21164         }
21165         
21166         this.picker().addClass(cls.join('-'));
21167         
21168         var _this = this;
21169         
21170         Roo.each(cls, function(c){
21171             if(c == 'bottom'){
21172                 _this.picker().setTop(_this.inputEl().getHeight());
21173                 return;
21174             }
21175             if(c == 'top'){
21176                 _this.picker().setTop(0 - _this.picker().getHeight());
21177                 return;
21178             }
21179             
21180             if(c == 'left'){
21181                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
21182                 return;
21183             }
21184             if(c == 'right'){
21185                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
21186                 return;
21187             }
21188         });
21189         
21190     },
21191   
21192     onFocus : function()
21193     {
21194         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
21195         this.show();
21196     },
21197     
21198     onBlur : function()
21199     {
21200         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
21201         this.hide();
21202     },
21203     
21204     show : function()
21205     {
21206         this.picker().show();
21207         this.pop.show();
21208         this.update();
21209         this.place();
21210         
21211         this.fireEvent('show', this, this.date);
21212     },
21213     
21214     hide : function()
21215     {
21216         this.picker().hide();
21217         this.pop.hide();
21218         
21219         this.fireEvent('hide', this, this.date);
21220     },
21221     
21222     setTime : function()
21223     {
21224         this.hide();
21225         this.setValue(this.time.format(this.format));
21226         
21227         this.fireEvent('select', this, this.date);
21228         
21229         
21230     },
21231     
21232     onMousedown: function(e){
21233         e.stopPropagation();
21234         e.preventDefault();
21235     },
21236     
21237     onIncrementHours: function()
21238     {
21239         Roo.log('onIncrementHours');
21240         this.time = this.time.add(Date.HOUR, 1);
21241         this.update();
21242         
21243     },
21244     
21245     onDecrementHours: function()
21246     {
21247         Roo.log('onDecrementHours');
21248         this.time = this.time.add(Date.HOUR, -1);
21249         this.update();
21250     },
21251     
21252     onIncrementMinutes: function()
21253     {
21254         Roo.log('onIncrementMinutes');
21255         this.time = this.time.add(Date.MINUTE, 1);
21256         this.update();
21257     },
21258     
21259     onDecrementMinutes: function()
21260     {
21261         Roo.log('onDecrementMinutes');
21262         this.time = this.time.add(Date.MINUTE, -1);
21263         this.update();
21264     },
21265     
21266     onTogglePeriod: function()
21267     {
21268         Roo.log('onTogglePeriod');
21269         this.time = this.time.add(Date.HOUR, 12);
21270         this.update();
21271     }
21272     
21273    
21274 });
21275
21276 Roo.apply(Roo.bootstrap.TimeField,  {
21277     
21278     content : {
21279         tag: 'tbody',
21280         cn: [
21281             {
21282                 tag: 'tr',
21283                 cn: [
21284                 {
21285                     tag: 'td',
21286                     colspan: '7'
21287                 }
21288                 ]
21289             }
21290         ]
21291     },
21292     
21293     footer : {
21294         tag: 'tfoot',
21295         cn: [
21296             {
21297                 tag: 'tr',
21298                 cn: [
21299                 {
21300                     tag: 'th',
21301                     colspan: '7',
21302                     cls: '',
21303                     cn: [
21304                         {
21305                             tag: 'button',
21306                             cls: 'btn btn-info ok',
21307                             html: 'OK'
21308                         }
21309                     ]
21310                 }
21311
21312                 ]
21313             }
21314         ]
21315     }
21316 });
21317
21318 Roo.apply(Roo.bootstrap.TimeField,  {
21319   
21320     template : {
21321         tag: 'div',
21322         cls: 'datepicker dropdown-menu',
21323         cn: [
21324             {
21325                 tag: 'div',
21326                 cls: 'datepicker-time',
21327                 cn: [
21328                 {
21329                     tag: 'table',
21330                     cls: 'table-condensed',
21331                     cn:[
21332                     Roo.bootstrap.TimeField.content,
21333                     Roo.bootstrap.TimeField.footer
21334                     ]
21335                 }
21336                 ]
21337             }
21338         ]
21339     }
21340 });
21341
21342  
21343
21344  /*
21345  * - LGPL
21346  *
21347  * MonthField
21348  * 
21349  */
21350
21351 /**
21352  * @class Roo.bootstrap.MonthField
21353  * @extends Roo.bootstrap.Input
21354  * Bootstrap MonthField class
21355  * 
21356  * @cfg {String} language default en
21357  * 
21358  * @constructor
21359  * Create a new MonthField
21360  * @param {Object} config The config object
21361  */
21362
21363 Roo.bootstrap.MonthField = function(config){
21364     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
21365     
21366     this.addEvents({
21367         /**
21368          * @event show
21369          * Fires when this field show.
21370          * @param {Roo.bootstrap.MonthField} this
21371          * @param {Mixed} date The date value
21372          */
21373         show : true,
21374         /**
21375          * @event show
21376          * Fires when this field hide.
21377          * @param {Roo.bootstrap.MonthField} this
21378          * @param {Mixed} date The date value
21379          */
21380         hide : true,
21381         /**
21382          * @event select
21383          * Fires when select a date.
21384          * @param {Roo.bootstrap.MonthField} this
21385          * @param {String} oldvalue The old value
21386          * @param {String} newvalue The new value
21387          */
21388         select : true
21389     });
21390 };
21391
21392 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
21393     
21394     onRender: function(ct, position)
21395     {
21396         
21397         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
21398         
21399         this.language = this.language || 'en';
21400         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
21401         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
21402         
21403         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
21404         this.isInline = false;
21405         this.isInput = true;
21406         this.component = this.el.select('.add-on', true).first() || false;
21407         this.component = (this.component && this.component.length === 0) ? false : this.component;
21408         this.hasInput = this.component && this.inputEL().length;
21409         
21410         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
21411         
21412         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21413         
21414         this.picker().on('mousedown', this.onMousedown, this);
21415         this.picker().on('click', this.onClick, this);
21416         
21417         this.picker().addClass('datepicker-dropdown');
21418         
21419         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21420             v.setStyle('width', '189px');
21421         });
21422         
21423         this.fillMonths();
21424         
21425         this.update();
21426         
21427         if(this.isInline) {
21428             this.show();
21429         }
21430         
21431     },
21432     
21433     setValue: function(v, suppressEvent)
21434     {   
21435         var o = this.getValue();
21436         
21437         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
21438         
21439         this.update();
21440
21441         if(suppressEvent !== true){
21442             this.fireEvent('select', this, o, v);
21443         }
21444         
21445     },
21446     
21447     getValue: function()
21448     {
21449         return this.value;
21450     },
21451     
21452     onClick: function(e) 
21453     {
21454         e.stopPropagation();
21455         e.preventDefault();
21456         
21457         var target = e.getTarget();
21458         
21459         if(target.nodeName.toLowerCase() === 'i'){
21460             target = Roo.get(target).dom.parentNode;
21461         }
21462         
21463         var nodeName = target.nodeName;
21464         var className = target.className;
21465         var html = target.innerHTML;
21466         
21467         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
21468             return;
21469         }
21470         
21471         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
21472         
21473         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21474         
21475         this.hide();
21476                         
21477     },
21478     
21479     picker : function()
21480     {
21481         return this.pickerEl;
21482     },
21483     
21484     fillMonths: function()
21485     {    
21486         var i = 0;
21487         var months = this.picker().select('>.datepicker-months td', true).first();
21488         
21489         months.dom.innerHTML = '';
21490         
21491         while (i < 12) {
21492             var month = {
21493                 tag: 'span',
21494                 cls: 'month',
21495                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
21496             };
21497             
21498             months.createChild(month);
21499         }
21500         
21501     },
21502     
21503     update: function()
21504     {
21505         var _this = this;
21506         
21507         if(typeof(this.vIndex) == 'undefined' && this.value.length){
21508             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
21509         }
21510         
21511         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
21512             e.removeClass('active');
21513             
21514             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
21515                 e.addClass('active');
21516             }
21517         })
21518     },
21519     
21520     place: function()
21521     {
21522         if(this.isInline) {
21523             return;
21524         }
21525         
21526         this.picker().removeClass(['bottom', 'top']);
21527         
21528         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21529             /*
21530              * place to the top of element!
21531              *
21532              */
21533             
21534             this.picker().addClass('top');
21535             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21536             
21537             return;
21538         }
21539         
21540         this.picker().addClass('bottom');
21541         
21542         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21543     },
21544     
21545     onFocus : function()
21546     {
21547         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
21548         this.show();
21549     },
21550     
21551     onBlur : function()
21552     {
21553         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
21554         
21555         var d = this.inputEl().getValue();
21556         
21557         this.setValue(d);
21558                 
21559         this.hide();
21560     },
21561     
21562     show : function()
21563     {
21564         this.picker().show();
21565         this.picker().select('>.datepicker-months', true).first().show();
21566         this.update();
21567         this.place();
21568         
21569         this.fireEvent('show', this, this.date);
21570     },
21571     
21572     hide : function()
21573     {
21574         if(this.isInline) {
21575             return;
21576         }
21577         this.picker().hide();
21578         this.fireEvent('hide', this, this.date);
21579         
21580     },
21581     
21582     onMousedown: function(e)
21583     {
21584         e.stopPropagation();
21585         e.preventDefault();
21586     },
21587     
21588     keyup: function(e)
21589     {
21590         Roo.bootstrap.MonthField.superclass.keyup.call(this);
21591         this.update();
21592     },
21593
21594     fireKey: function(e)
21595     {
21596         if (!this.picker().isVisible()){
21597             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
21598                 this.show();
21599             }
21600             return;
21601         }
21602         
21603         var dir;
21604         
21605         switch(e.keyCode){
21606             case 27: // escape
21607                 this.hide();
21608                 e.preventDefault();
21609                 break;
21610             case 37: // left
21611             case 39: // right
21612                 dir = e.keyCode == 37 ? -1 : 1;
21613                 
21614                 this.vIndex = this.vIndex + dir;
21615                 
21616                 if(this.vIndex < 0){
21617                     this.vIndex = 0;
21618                 }
21619                 
21620                 if(this.vIndex > 11){
21621                     this.vIndex = 11;
21622                 }
21623                 
21624                 if(isNaN(this.vIndex)){
21625                     this.vIndex = 0;
21626                 }
21627                 
21628                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21629                 
21630                 break;
21631             case 38: // up
21632             case 40: // down
21633                 
21634                 dir = e.keyCode == 38 ? -1 : 1;
21635                 
21636                 this.vIndex = this.vIndex + dir * 4;
21637                 
21638                 if(this.vIndex < 0){
21639                     this.vIndex = 0;
21640                 }
21641                 
21642                 if(this.vIndex > 11){
21643                     this.vIndex = 11;
21644                 }
21645                 
21646                 if(isNaN(this.vIndex)){
21647                     this.vIndex = 0;
21648                 }
21649                 
21650                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21651                 break;
21652                 
21653             case 13: // enter
21654                 
21655                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
21656                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21657                 }
21658                 
21659                 this.hide();
21660                 e.preventDefault();
21661                 break;
21662             case 9: // tab
21663                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
21664                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21665                 }
21666                 this.hide();
21667                 break;
21668             case 16: // shift
21669             case 17: // ctrl
21670             case 18: // alt
21671                 break;
21672             default :
21673                 this.hide();
21674                 
21675         }
21676     },
21677     
21678     remove: function() 
21679     {
21680         this.picker().remove();
21681     }
21682    
21683 });
21684
21685 Roo.apply(Roo.bootstrap.MonthField,  {
21686     
21687     content : {
21688         tag: 'tbody',
21689         cn: [
21690         {
21691             tag: 'tr',
21692             cn: [
21693             {
21694                 tag: 'td',
21695                 colspan: '7'
21696             }
21697             ]
21698         }
21699         ]
21700     },
21701     
21702     dates:{
21703         en: {
21704             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21705             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
21706         }
21707     }
21708 });
21709
21710 Roo.apply(Roo.bootstrap.MonthField,  {
21711   
21712     template : {
21713         tag: 'div',
21714         cls: 'datepicker dropdown-menu roo-dynamic',
21715         cn: [
21716             {
21717                 tag: 'div',
21718                 cls: 'datepicker-months',
21719                 cn: [
21720                 {
21721                     tag: 'table',
21722                     cls: 'table-condensed',
21723                     cn:[
21724                         Roo.bootstrap.DateField.content
21725                     ]
21726                 }
21727                 ]
21728             }
21729         ]
21730     }
21731 });
21732
21733  
21734
21735  
21736  /*
21737  * - LGPL
21738  *
21739  * CheckBox
21740  * 
21741  */
21742
21743 /**
21744  * @class Roo.bootstrap.CheckBox
21745  * @extends Roo.bootstrap.Input
21746  * Bootstrap CheckBox class
21747  * 
21748  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
21749  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
21750  * @cfg {String} boxLabel The text that appears beside the checkbox
21751  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
21752  * @cfg {Boolean} checked initnal the element
21753  * @cfg {Boolean} inline inline the element (default false)
21754  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
21755  * @cfg {String} tooltip label tooltip
21756  * 
21757  * @constructor
21758  * Create a new CheckBox
21759  * @param {Object} config The config object
21760  */
21761
21762 Roo.bootstrap.CheckBox = function(config){
21763     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
21764    
21765     this.addEvents({
21766         /**
21767         * @event check
21768         * Fires when the element is checked or unchecked.
21769         * @param {Roo.bootstrap.CheckBox} this This input
21770         * @param {Boolean} checked The new checked value
21771         */
21772        check : true,
21773        /**
21774         * @event click
21775         * Fires when the element is click.
21776         * @param {Roo.bootstrap.CheckBox} this This input
21777         */
21778        click : true
21779     });
21780     
21781 };
21782
21783 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
21784   
21785     inputType: 'checkbox',
21786     inputValue: 1,
21787     valueOff: 0,
21788     boxLabel: false,
21789     checked: false,
21790     weight : false,
21791     inline: false,
21792     tooltip : '',
21793     
21794     // checkbox success does not make any sense really.. 
21795     invalidClass : "",
21796     validClass : "",
21797     
21798     
21799     getAutoCreate : function()
21800     {
21801         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
21802         
21803         var id = Roo.id();
21804         
21805         var cfg = {};
21806         
21807         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
21808         
21809         if(this.inline){
21810             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
21811         }
21812         
21813         var input =  {
21814             tag: 'input',
21815             id : id,
21816             type : this.inputType,
21817             value : this.inputValue,
21818             cls : 'roo-' + this.inputType, //'form-box',
21819             placeholder : this.placeholder || ''
21820             
21821         };
21822         
21823         if(this.inputType != 'radio'){
21824             var hidden =  {
21825                 tag: 'input',
21826                 type : 'hidden',
21827                 cls : 'roo-hidden-value',
21828                 value : this.checked ? this.inputValue : this.valueOff
21829             };
21830         }
21831         
21832             
21833         if (this.weight) { // Validity check?
21834             cfg.cls += " " + this.inputType + "-" + this.weight;
21835         }
21836         
21837         if (this.disabled) {
21838             input.disabled=true;
21839         }
21840         
21841         if(this.checked){
21842             input.checked = this.checked;
21843         }
21844         
21845         if (this.name) {
21846             
21847             input.name = this.name;
21848             
21849             if(this.inputType != 'radio'){
21850                 hidden.name = this.name;
21851                 input.name = '_hidden_' + this.name;
21852             }
21853         }
21854         
21855         if (this.size) {
21856             input.cls += ' input-' + this.size;
21857         }
21858         
21859         var settings=this;
21860         
21861         ['xs','sm','md','lg'].map(function(size){
21862             if (settings[size]) {
21863                 cfg.cls += ' col-' + size + '-' + settings[size];
21864             }
21865         });
21866         
21867         var inputblock = input;
21868          
21869         if (this.before || this.after) {
21870             
21871             inputblock = {
21872                 cls : 'input-group',
21873                 cn :  [] 
21874             };
21875             
21876             if (this.before) {
21877                 inputblock.cn.push({
21878                     tag :'span',
21879                     cls : 'input-group-addon',
21880                     html : this.before
21881                 });
21882             }
21883             
21884             inputblock.cn.push(input);
21885             
21886             if(this.inputType != 'radio'){
21887                 inputblock.cn.push(hidden);
21888             }
21889             
21890             if (this.after) {
21891                 inputblock.cn.push({
21892                     tag :'span',
21893                     cls : 'input-group-addon',
21894                     html : this.after
21895                 });
21896             }
21897             
21898         }
21899         var boxLabelCfg = false;
21900         
21901         if(this.boxLabel){
21902            
21903             boxLabelCfg = {
21904                 tag: 'label',
21905                 //'for': id, // box label is handled by onclick - so no for...
21906                 cls: 'box-label',
21907                 html: this.boxLabel
21908             };
21909             if(this.tooltip){
21910                 boxLabelCfg.tooltip = this.tooltip;
21911             }
21912              
21913         }
21914         
21915         
21916         if (align ==='left' && this.fieldLabel.length) {
21917 //                Roo.log("left and has label");
21918             cfg.cn = [
21919                 {
21920                     tag: 'label',
21921                     'for' :  id,
21922                     cls : 'control-label',
21923                     html : this.fieldLabel
21924                 },
21925                 {
21926                     cls : "", 
21927                     cn: [
21928                         inputblock
21929                     ]
21930                 }
21931             ];
21932             
21933             if (boxLabelCfg) {
21934                 cfg.cn[1].cn.push(boxLabelCfg);
21935             }
21936             
21937             if(this.labelWidth > 12){
21938                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
21939             }
21940             
21941             if(this.labelWidth < 13 && this.labelmd == 0){
21942                 this.labelmd = this.labelWidth;
21943             }
21944             
21945             if(this.labellg > 0){
21946                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
21947                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
21948             }
21949             
21950             if(this.labelmd > 0){
21951                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
21952                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
21953             }
21954             
21955             if(this.labelsm > 0){
21956                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
21957                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
21958             }
21959             
21960             if(this.labelxs > 0){
21961                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
21962                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
21963             }
21964             
21965         } else if ( this.fieldLabel.length) {
21966 //                Roo.log(" label");
21967                 cfg.cn = [
21968                    
21969                     {
21970                         tag: this.boxLabel ? 'span' : 'label',
21971                         'for': id,
21972                         cls: 'control-label box-input-label',
21973                         //cls : 'input-group-addon',
21974                         html : this.fieldLabel
21975                     },
21976                     
21977                     inputblock
21978                     
21979                 ];
21980                 if (boxLabelCfg) {
21981                     cfg.cn.push(boxLabelCfg);
21982                 }
21983
21984         } else {
21985             
21986 //                Roo.log(" no label && no align");
21987                 cfg.cn = [  inputblock ] ;
21988                 if (boxLabelCfg) {
21989                     cfg.cn.push(boxLabelCfg);
21990                 }
21991
21992                 
21993         }
21994         
21995        
21996         
21997         if(this.inputType != 'radio'){
21998             cfg.cn.push(hidden);
21999         }
22000         
22001         return cfg;
22002         
22003     },
22004     
22005     /**
22006      * return the real input element.
22007      */
22008     inputEl: function ()
22009     {
22010         return this.el.select('input.roo-' + this.inputType,true).first();
22011     },
22012     hiddenEl: function ()
22013     {
22014         return this.el.select('input.roo-hidden-value',true).first();
22015     },
22016     
22017     labelEl: function()
22018     {
22019         return this.el.select('label.control-label',true).first();
22020     },
22021     /* depricated... */
22022     
22023     label: function()
22024     {
22025         return this.labelEl();
22026     },
22027     
22028     boxLabelEl: function()
22029     {
22030         return this.el.select('label.box-label',true).first();
22031     },
22032     
22033     initEvents : function()
22034     {
22035 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
22036         
22037         this.inputEl().on('click', this.onClick,  this);
22038         
22039         if (this.boxLabel) { 
22040             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
22041         }
22042         
22043         this.startValue = this.getValue();
22044         
22045         if(this.groupId){
22046             Roo.bootstrap.CheckBox.register(this);
22047         }
22048     },
22049     
22050     onClick : function(e)
22051     {   
22052         if(this.fireEvent('click', this, e) !== false){
22053             this.setChecked(!this.checked);
22054         }
22055         
22056     },
22057     
22058     setChecked : function(state,suppressEvent)
22059     {
22060         this.startValue = this.getValue();
22061
22062         if(this.inputType == 'radio'){
22063             
22064             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22065                 e.dom.checked = false;
22066             });
22067             
22068             this.inputEl().dom.checked = true;
22069             
22070             this.inputEl().dom.value = this.inputValue;
22071             
22072             if(suppressEvent !== true){
22073                 this.fireEvent('check', this, true);
22074             }
22075             
22076             this.validate();
22077             
22078             return;
22079         }
22080         
22081         this.checked = state;
22082         
22083         this.inputEl().dom.checked = state;
22084         
22085         
22086         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
22087         
22088         if(suppressEvent !== true){
22089             this.fireEvent('check', this, state);
22090         }
22091         
22092         this.validate();
22093     },
22094     
22095     getValue : function()
22096     {
22097         if(this.inputType == 'radio'){
22098             return this.getGroupValue();
22099         }
22100         
22101         return this.hiddenEl().dom.value;
22102         
22103     },
22104     
22105     getGroupValue : function()
22106     {
22107         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
22108             return '';
22109         }
22110         
22111         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
22112     },
22113     
22114     setValue : function(v,suppressEvent)
22115     {
22116         if(this.inputType == 'radio'){
22117             this.setGroupValue(v, suppressEvent);
22118             return;
22119         }
22120         
22121         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
22122         
22123         this.validate();
22124     },
22125     
22126     setGroupValue : function(v, suppressEvent)
22127     {
22128         this.startValue = this.getValue();
22129         
22130         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22131             e.dom.checked = false;
22132             
22133             if(e.dom.value == v){
22134                 e.dom.checked = true;
22135             }
22136         });
22137         
22138         if(suppressEvent !== true){
22139             this.fireEvent('check', this, true);
22140         }
22141
22142         this.validate();
22143         
22144         return;
22145     },
22146     
22147     validate : function()
22148     {
22149         if(this.getVisibilityEl().hasClass('hidden')){
22150             return true;
22151         }
22152         
22153         if(
22154                 this.disabled || 
22155                 (this.inputType == 'radio' && this.validateRadio()) ||
22156                 (this.inputType == 'checkbox' && this.validateCheckbox())
22157         ){
22158             this.markValid();
22159             return true;
22160         }
22161         
22162         this.markInvalid();
22163         return false;
22164     },
22165     
22166     validateRadio : function()
22167     {
22168         if(this.getVisibilityEl().hasClass('hidden')){
22169             return true;
22170         }
22171         
22172         if(this.allowBlank){
22173             return true;
22174         }
22175         
22176         var valid = false;
22177         
22178         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22179             if(!e.dom.checked){
22180                 return;
22181             }
22182             
22183             valid = true;
22184             
22185             return false;
22186         });
22187         
22188         return valid;
22189     },
22190     
22191     validateCheckbox : function()
22192     {
22193         if(!this.groupId){
22194             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
22195             //return (this.getValue() == this.inputValue) ? true : false;
22196         }
22197         
22198         var group = Roo.bootstrap.CheckBox.get(this.groupId);
22199         
22200         if(!group){
22201             return false;
22202         }
22203         
22204         var r = false;
22205         
22206         for(var i in group){
22207             if(group[i].el.isVisible(true)){
22208                 r = false;
22209                 break;
22210             }
22211             
22212             r = true;
22213         }
22214         
22215         for(var i in group){
22216             if(r){
22217                 break;
22218             }
22219             
22220             r = (group[i].getValue() == group[i].inputValue) ? true : false;
22221         }
22222         
22223         return r;
22224     },
22225     
22226     /**
22227      * Mark this field as valid
22228      */
22229     markValid : function()
22230     {
22231         var _this = this;
22232         
22233         this.fireEvent('valid', this);
22234         
22235         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
22236         
22237         if(this.groupId){
22238             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
22239         }
22240         
22241         if(label){
22242             label.markValid();
22243         }
22244
22245         if(this.inputType == 'radio'){
22246             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22247                 var fg = e.findParent('.form-group', false, true);
22248                 if (Roo.bootstrap.version == 3) {
22249                     fg.removeClass([_this.invalidClass, _this.validClass]);
22250                     fg.addClass(_this.validClass);
22251                 } else {
22252                     fg.removeClass(['is-valid', 'is-invalid']);
22253                     fg.addClass('is-valid');
22254                 }
22255             });
22256             
22257             return;
22258         }
22259
22260         if(!this.groupId){
22261             var fg = this.el.findParent('.form-group', false, true);
22262             if (Roo.bootstrap.version == 3) {
22263                 fg.removeClass([this.invalidClass, this.validClass]);
22264                 fg.addClass(this.validClass);
22265             } else {
22266                 fg.removeClass(['is-valid', 'is-invalid']);
22267                 fg.addClass('is-valid');
22268             }
22269             return;
22270         }
22271         
22272         var group = Roo.bootstrap.CheckBox.get(this.groupId);
22273         
22274         if(!group){
22275             return;
22276         }
22277         
22278         for(var i in group){
22279             var fg = group[i].el.findParent('.form-group', false, true);
22280             if (Roo.bootstrap.version == 3) {
22281                 fg.removeClass([this.invalidClass, this.validClass]);
22282                 fg.addClass(this.validClass);
22283             } else {
22284                 fg.removeClass(['is-valid', 'is-invalid']);
22285                 fg.addClass('is-valid');
22286             }
22287         }
22288     },
22289     
22290      /**
22291      * Mark this field as invalid
22292      * @param {String} msg The validation message
22293      */
22294     markInvalid : function(msg)
22295     {
22296         if(this.allowBlank){
22297             return;
22298         }
22299         
22300         var _this = this;
22301         
22302         this.fireEvent('invalid', this, msg);
22303         
22304         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
22305         
22306         if(this.groupId){
22307             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
22308         }
22309         
22310         if(label){
22311             label.markInvalid();
22312         }
22313             
22314         if(this.inputType == 'radio'){
22315             
22316             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22317                 var fg = e.findParent('.form-group', false, true);
22318                 if (Roo.bootstrap.version == 3) {
22319                     fg.removeClass([_this.invalidClass, _this.validClass]);
22320                     fg.addClass(_this.invalidClass);
22321                 } else {
22322                     fg.removeClass(['is-invalid', 'is-valid']);
22323                     fg.addClass('is-invalid');
22324                 }
22325             });
22326             
22327             return;
22328         }
22329         
22330         if(!this.groupId){
22331             var fg = this.el.findParent('.form-group', false, true);
22332             if (Roo.bootstrap.version == 3) {
22333                 fg.removeClass([_this.invalidClass, _this.validClass]);
22334                 fg.addClass(_this.invalidClass);
22335             } else {
22336                 fg.removeClass(['is-invalid', 'is-valid']);
22337                 fg.addClass('is-invalid');
22338             }
22339             return;
22340         }
22341         
22342         var group = Roo.bootstrap.CheckBox.get(this.groupId);
22343         
22344         if(!group){
22345             return;
22346         }
22347         
22348         for(var i in group){
22349             var fg = group[i].el.findParent('.form-group', false, true);
22350             if (Roo.bootstrap.version == 3) {
22351                 fg.removeClass([_this.invalidClass, _this.validClass]);
22352                 fg.addClass(_this.invalidClass);
22353             } else {
22354                 fg.removeClass(['is-invalid', 'is-valid']);
22355                 fg.addClass('is-invalid');
22356             }
22357         }
22358         
22359     },
22360     
22361     clearInvalid : function()
22362     {
22363         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
22364         
22365         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
22366         
22367         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
22368         
22369         if (label && label.iconEl) {
22370             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
22371             label.iconEl.removeClass(['is-invalid', 'is-valid']);
22372         }
22373     },
22374     
22375     disable : function()
22376     {
22377         if(this.inputType != 'radio'){
22378             Roo.bootstrap.CheckBox.superclass.disable.call(this);
22379             return;
22380         }
22381         
22382         var _this = this;
22383         
22384         if(this.rendered){
22385             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22386                 _this.getActionEl().addClass(this.disabledClass);
22387                 e.dom.disabled = true;
22388             });
22389         }
22390         
22391         this.disabled = true;
22392         this.fireEvent("disable", this);
22393         return this;
22394     },
22395
22396     enable : function()
22397     {
22398         if(this.inputType != 'radio'){
22399             Roo.bootstrap.CheckBox.superclass.enable.call(this);
22400             return;
22401         }
22402         
22403         var _this = this;
22404         
22405         if(this.rendered){
22406             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22407                 _this.getActionEl().removeClass(this.disabledClass);
22408                 e.dom.disabled = false;
22409             });
22410         }
22411         
22412         this.disabled = false;
22413         this.fireEvent("enable", this);
22414         return this;
22415     },
22416     
22417     setBoxLabel : function(v)
22418     {
22419         this.boxLabel = v;
22420         
22421         if(this.rendered){
22422             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
22423         }
22424     }
22425
22426 });
22427
22428 Roo.apply(Roo.bootstrap.CheckBox, {
22429     
22430     groups: {},
22431     
22432      /**
22433     * register a CheckBox Group
22434     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
22435     */
22436     register : function(checkbox)
22437     {
22438         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
22439             this.groups[checkbox.groupId] = {};
22440         }
22441         
22442         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
22443             return;
22444         }
22445         
22446         this.groups[checkbox.groupId][checkbox.name] = checkbox;
22447         
22448     },
22449     /**
22450     * fetch a CheckBox Group based on the group ID
22451     * @param {string} the group ID
22452     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
22453     */
22454     get: function(groupId) {
22455         if (typeof(this.groups[groupId]) == 'undefined') {
22456             return false;
22457         }
22458         
22459         return this.groups[groupId] ;
22460     }
22461     
22462     
22463 });
22464 /*
22465  * - LGPL
22466  *
22467  * RadioItem
22468  * 
22469  */
22470
22471 /**
22472  * @class Roo.bootstrap.Radio
22473  * @extends Roo.bootstrap.Component
22474  * Bootstrap Radio class
22475  * @cfg {String} boxLabel - the label associated
22476  * @cfg {String} value - the value of radio
22477  * 
22478  * @constructor
22479  * Create a new Radio
22480  * @param {Object} config The config object
22481  */
22482 Roo.bootstrap.Radio = function(config){
22483     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
22484     
22485 };
22486
22487 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
22488     
22489     boxLabel : '',
22490     
22491     value : '',
22492     
22493     getAutoCreate : function()
22494     {
22495         var cfg = {
22496             tag : 'div',
22497             cls : 'form-group radio',
22498             cn : [
22499                 {
22500                     tag : 'label',
22501                     cls : 'box-label',
22502                     html : this.boxLabel
22503                 }
22504             ]
22505         };
22506         
22507         return cfg;
22508     },
22509     
22510     initEvents : function() 
22511     {
22512         this.parent().register(this);
22513         
22514         this.el.on('click', this.onClick, this);
22515         
22516     },
22517     
22518     onClick : function(e)
22519     {
22520         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
22521             this.setChecked(true);
22522         }
22523     },
22524     
22525     setChecked : function(state, suppressEvent)
22526     {
22527         this.parent().setValue(this.value, suppressEvent);
22528         
22529     },
22530     
22531     setBoxLabel : function(v)
22532     {
22533         this.boxLabel = v;
22534         
22535         if(this.rendered){
22536             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
22537         }
22538     }
22539     
22540 });
22541  
22542
22543  /*
22544  * - LGPL
22545  *
22546  * Input
22547  * 
22548  */
22549
22550 /**
22551  * @class Roo.bootstrap.SecurePass
22552  * @extends Roo.bootstrap.Input
22553  * Bootstrap SecurePass class
22554  *
22555  * 
22556  * @constructor
22557  * Create a new SecurePass
22558  * @param {Object} config The config object
22559  */
22560  
22561 Roo.bootstrap.SecurePass = function (config) {
22562     // these go here, so the translation tool can replace them..
22563     this.errors = {
22564         PwdEmpty: "Please type a password, and then retype it to confirm.",
22565         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
22566         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
22567         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
22568         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
22569         FNInPwd: "Your password can't contain your first name. Please type a different password.",
22570         LNInPwd: "Your password can't contain your last name. Please type a different password.",
22571         TooWeak: "Your password is Too Weak."
22572     },
22573     this.meterLabel = "Password strength:";
22574     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
22575     this.meterClass = [
22576         "roo-password-meter-tooweak", 
22577         "roo-password-meter-weak", 
22578         "roo-password-meter-medium", 
22579         "roo-password-meter-strong", 
22580         "roo-password-meter-grey"
22581     ];
22582     
22583     this.errors = {};
22584     
22585     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
22586 }
22587
22588 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
22589     /**
22590      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
22591      * {
22592      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
22593      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
22594      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
22595      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
22596      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
22597      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
22598      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
22599      * })
22600      */
22601     // private
22602     
22603     meterWidth: 300,
22604     errorMsg :'',    
22605     errors: false,
22606     imageRoot: '/',
22607     /**
22608      * @cfg {String/Object} Label for the strength meter (defaults to
22609      * 'Password strength:')
22610      */
22611     // private
22612     meterLabel: '',
22613     /**
22614      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
22615      * ['Weak', 'Medium', 'Strong'])
22616      */
22617     // private    
22618     pwdStrengths: false,    
22619     // private
22620     strength: 0,
22621     // private
22622     _lastPwd: null,
22623     // private
22624     kCapitalLetter: 0,
22625     kSmallLetter: 1,
22626     kDigit: 2,
22627     kPunctuation: 3,
22628     
22629     insecure: false,
22630     // private
22631     initEvents: function ()
22632     {
22633         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
22634
22635         if (this.el.is('input[type=password]') && Roo.isSafari) {
22636             this.el.on('keydown', this.SafariOnKeyDown, this);
22637         }
22638
22639         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
22640     },
22641     // private
22642     onRender: function (ct, position)
22643     {
22644         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
22645         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
22646         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
22647
22648         this.trigger.createChild({
22649                    cn: [
22650                     {
22651                     //id: 'PwdMeter',
22652                     tag: 'div',
22653                     cls: 'roo-password-meter-grey col-xs-12',
22654                     style: {
22655                         //width: 0,
22656                         //width: this.meterWidth + 'px'                                                
22657                         }
22658                     },
22659                     {                            
22660                          cls: 'roo-password-meter-text'                          
22661                     }
22662                 ]            
22663         });
22664
22665          
22666         if (this.hideTrigger) {
22667             this.trigger.setDisplayed(false);
22668         }
22669         this.setSize(this.width || '', this.height || '');
22670     },
22671     // private
22672     onDestroy: function ()
22673     {
22674         if (this.trigger) {
22675             this.trigger.removeAllListeners();
22676             this.trigger.remove();
22677         }
22678         if (this.wrap) {
22679             this.wrap.remove();
22680         }
22681         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
22682     },
22683     // private
22684     checkStrength: function ()
22685     {
22686         var pwd = this.inputEl().getValue();
22687         if (pwd == this._lastPwd) {
22688             return;
22689         }
22690
22691         var strength;
22692         if (this.ClientSideStrongPassword(pwd)) {
22693             strength = 3;
22694         } else if (this.ClientSideMediumPassword(pwd)) {
22695             strength = 2;
22696         } else if (this.ClientSideWeakPassword(pwd)) {
22697             strength = 1;
22698         } else {
22699             strength = 0;
22700         }
22701         
22702         Roo.log('strength1: ' + strength);
22703         
22704         //var pm = this.trigger.child('div/div/div').dom;
22705         var pm = this.trigger.child('div/div');
22706         pm.removeClass(this.meterClass);
22707         pm.addClass(this.meterClass[strength]);
22708                 
22709         
22710         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
22711                 
22712         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
22713         
22714         this._lastPwd = pwd;
22715     },
22716     reset: function ()
22717     {
22718         Roo.bootstrap.SecurePass.superclass.reset.call(this);
22719         
22720         this._lastPwd = '';
22721         
22722         var pm = this.trigger.child('div/div');
22723         pm.removeClass(this.meterClass);
22724         pm.addClass('roo-password-meter-grey');        
22725         
22726         
22727         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
22728         
22729         pt.innerHTML = '';
22730         this.inputEl().dom.type='password';
22731     },
22732     // private
22733     validateValue: function (value)
22734     {
22735         
22736         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
22737             return false;
22738         }
22739         if (value.length == 0) {
22740             if (this.allowBlank) {
22741                 this.clearInvalid();
22742                 return true;
22743             }
22744
22745             this.markInvalid(this.errors.PwdEmpty);
22746             this.errorMsg = this.errors.PwdEmpty;
22747             return false;
22748         }
22749         
22750         if(this.insecure){
22751             return true;
22752         }
22753         
22754         if ('[\x21-\x7e]*'.match(value)) {
22755             this.markInvalid(this.errors.PwdBadChar);
22756             this.errorMsg = this.errors.PwdBadChar;
22757             return false;
22758         }
22759         if (value.length < 6) {
22760             this.markInvalid(this.errors.PwdShort);
22761             this.errorMsg = this.errors.PwdShort;
22762             return false;
22763         }
22764         if (value.length > 16) {
22765             this.markInvalid(this.errors.PwdLong);
22766             this.errorMsg = this.errors.PwdLong;
22767             return false;
22768         }
22769         var strength;
22770         if (this.ClientSideStrongPassword(value)) {
22771             strength = 3;
22772         } else if (this.ClientSideMediumPassword(value)) {
22773             strength = 2;
22774         } else if (this.ClientSideWeakPassword(value)) {
22775             strength = 1;
22776         } else {
22777             strength = 0;
22778         }
22779
22780         
22781         if (strength < 2) {
22782             //this.markInvalid(this.errors.TooWeak);
22783             this.errorMsg = this.errors.TooWeak;
22784             //return false;
22785         }
22786         
22787         
22788         console.log('strength2: ' + strength);
22789         
22790         //var pm = this.trigger.child('div/div/div').dom;
22791         
22792         var pm = this.trigger.child('div/div');
22793         pm.removeClass(this.meterClass);
22794         pm.addClass(this.meterClass[strength]);
22795                 
22796         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
22797                 
22798         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
22799         
22800         this.errorMsg = ''; 
22801         return true;
22802     },
22803     // private
22804     CharacterSetChecks: function (type)
22805     {
22806         this.type = type;
22807         this.fResult = false;
22808     },
22809     // private
22810     isctype: function (character, type)
22811     {
22812         switch (type) {  
22813             case this.kCapitalLetter:
22814                 if (character >= 'A' && character <= 'Z') {
22815                     return true;
22816                 }
22817                 break;
22818             
22819             case this.kSmallLetter:
22820                 if (character >= 'a' && character <= 'z') {
22821                     return true;
22822                 }
22823                 break;
22824             
22825             case this.kDigit:
22826                 if (character >= '0' && character <= '9') {
22827                     return true;
22828                 }
22829                 break;
22830             
22831             case this.kPunctuation:
22832                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
22833                     return true;
22834                 }
22835                 break;
22836             
22837             default:
22838                 return false;
22839         }
22840
22841     },
22842     // private
22843     IsLongEnough: function (pwd, size)
22844     {
22845         return !(pwd == null || isNaN(size) || pwd.length < size);
22846     },
22847     // private
22848     SpansEnoughCharacterSets: function (word, nb)
22849     {
22850         if (!this.IsLongEnough(word, nb))
22851         {
22852             return false;
22853         }
22854
22855         var characterSetChecks = new Array(
22856             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
22857             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
22858         );
22859         
22860         for (var index = 0; index < word.length; ++index) {
22861             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
22862                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
22863                     characterSetChecks[nCharSet].fResult = true;
22864                     break;
22865                 }
22866             }
22867         }
22868
22869         var nCharSets = 0;
22870         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
22871             if (characterSetChecks[nCharSet].fResult) {
22872                 ++nCharSets;
22873             }
22874         }
22875
22876         if (nCharSets < nb) {
22877             return false;
22878         }
22879         return true;
22880     },
22881     // private
22882     ClientSideStrongPassword: function (pwd)
22883     {
22884         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
22885     },
22886     // private
22887     ClientSideMediumPassword: function (pwd)
22888     {
22889         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
22890     },
22891     // private
22892     ClientSideWeakPassword: function (pwd)
22893     {
22894         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
22895     }
22896           
22897 })//<script type="text/javascript">
22898
22899 /*
22900  * Based  Ext JS Library 1.1.1
22901  * Copyright(c) 2006-2007, Ext JS, LLC.
22902  * LGPL
22903  *
22904  */
22905  
22906 /**
22907  * @class Roo.HtmlEditorCore
22908  * @extends Roo.Component
22909  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
22910  *
22911  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22912  */
22913
22914 Roo.HtmlEditorCore = function(config){
22915     
22916     
22917     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
22918     
22919     
22920     this.addEvents({
22921         /**
22922          * @event initialize
22923          * Fires when the editor is fully initialized (including the iframe)
22924          * @param {Roo.HtmlEditorCore} this
22925          */
22926         initialize: true,
22927         /**
22928          * @event activate
22929          * Fires when the editor is first receives the focus. Any insertion must wait
22930          * until after this event.
22931          * @param {Roo.HtmlEditorCore} this
22932          */
22933         activate: true,
22934          /**
22935          * @event beforesync
22936          * Fires before the textarea is updated with content from the editor iframe. Return false
22937          * to cancel the sync.
22938          * @param {Roo.HtmlEditorCore} this
22939          * @param {String} html
22940          */
22941         beforesync: true,
22942          /**
22943          * @event beforepush
22944          * Fires before the iframe editor is updated with content from the textarea. Return false
22945          * to cancel the push.
22946          * @param {Roo.HtmlEditorCore} this
22947          * @param {String} html
22948          */
22949         beforepush: true,
22950          /**
22951          * @event sync
22952          * Fires when the textarea is updated with content from the editor iframe.
22953          * @param {Roo.HtmlEditorCore} this
22954          * @param {String} html
22955          */
22956         sync: true,
22957          /**
22958          * @event push
22959          * Fires when the iframe editor is updated with content from the textarea.
22960          * @param {Roo.HtmlEditorCore} this
22961          * @param {String} html
22962          */
22963         push: true,
22964         
22965         /**
22966          * @event editorevent
22967          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22968          * @param {Roo.HtmlEditorCore} this
22969          */
22970         editorevent: true
22971         
22972     });
22973     
22974     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
22975     
22976     // defaults : white / black...
22977     this.applyBlacklists();
22978     
22979     
22980     
22981 };
22982
22983
22984 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
22985
22986
22987      /**
22988      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
22989      */
22990     
22991     owner : false,
22992     
22993      /**
22994      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22995      *                        Roo.resizable.
22996      */
22997     resizable : false,
22998      /**
22999      * @cfg {Number} height (in pixels)
23000      */   
23001     height: 300,
23002    /**
23003      * @cfg {Number} width (in pixels)
23004      */   
23005     width: 500,
23006     
23007     /**
23008      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23009      * 
23010      */
23011     stylesheets: false,
23012     
23013     // id of frame..
23014     frameId: false,
23015     
23016     // private properties
23017     validationEvent : false,
23018     deferHeight: true,
23019     initialized : false,
23020     activated : false,
23021     sourceEditMode : false,
23022     onFocus : Roo.emptyFn,
23023     iframePad:3,
23024     hideMode:'offsets',
23025     
23026     clearUp: true,
23027     
23028     // blacklist + whitelisted elements..
23029     black: false,
23030     white: false,
23031      
23032     bodyCls : '',
23033
23034     /**
23035      * Protected method that will not generally be called directly. It
23036      * is called when the editor initializes the iframe with HTML contents. Override this method if you
23037      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
23038      */
23039     getDocMarkup : function(){
23040         // body styles..
23041         var st = '';
23042         
23043         // inherit styels from page...?? 
23044         if (this.stylesheets === false) {
23045             
23046             Roo.get(document.head).select('style').each(function(node) {
23047                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23048             });
23049             
23050             Roo.get(document.head).select('link').each(function(node) { 
23051                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23052             });
23053             
23054         } else if (!this.stylesheets.length) {
23055                 // simple..
23056                 st = '<style type="text/css">' +
23057                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23058                    '</style>';
23059         } else { 
23060             st = '<style type="text/css">' +
23061                     this.stylesheets +
23062                 '</style>';
23063         }
23064         
23065         st +=  '<style type="text/css">' +
23066             'IMG { cursor: pointer } ' +
23067         '</style>';
23068
23069         var cls = 'roo-htmleditor-body';
23070         
23071         if(this.bodyCls.length){
23072             cls += ' ' + this.bodyCls;
23073         }
23074         
23075         return '<html><head>' + st  +
23076             //<style type="text/css">' +
23077             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23078             //'</style>' +
23079             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
23080     },
23081
23082     // private
23083     onRender : function(ct, position)
23084     {
23085         var _t = this;
23086         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
23087         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
23088         
23089         
23090         this.el.dom.style.border = '0 none';
23091         this.el.dom.setAttribute('tabIndex', -1);
23092         this.el.addClass('x-hidden hide');
23093         
23094         
23095         
23096         if(Roo.isIE){ // fix IE 1px bogus margin
23097             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
23098         }
23099        
23100         
23101         this.frameId = Roo.id();
23102         
23103          
23104         
23105         var iframe = this.owner.wrap.createChild({
23106             tag: 'iframe',
23107             cls: 'form-control', // bootstrap..
23108             id: this.frameId,
23109             name: this.frameId,
23110             frameBorder : 'no',
23111             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
23112         }, this.el
23113         );
23114         
23115         
23116         this.iframe = iframe.dom;
23117
23118          this.assignDocWin();
23119         
23120         this.doc.designMode = 'on';
23121        
23122         this.doc.open();
23123         this.doc.write(this.getDocMarkup());
23124         this.doc.close();
23125
23126         
23127         var task = { // must defer to wait for browser to be ready
23128             run : function(){
23129                 //console.log("run task?" + this.doc.readyState);
23130                 this.assignDocWin();
23131                 if(this.doc.body || this.doc.readyState == 'complete'){
23132                     try {
23133                         this.doc.designMode="on";
23134                     } catch (e) {
23135                         return;
23136                     }
23137                     Roo.TaskMgr.stop(task);
23138                     this.initEditor.defer(10, this);
23139                 }
23140             },
23141             interval : 10,
23142             duration: 10000,
23143             scope: this
23144         };
23145         Roo.TaskMgr.start(task);
23146
23147     },
23148
23149     // private
23150     onResize : function(w, h)
23151     {
23152          Roo.log('resize: ' +w + ',' + h );
23153         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
23154         if(!this.iframe){
23155             return;
23156         }
23157         if(typeof w == 'number'){
23158             
23159             this.iframe.style.width = w + 'px';
23160         }
23161         if(typeof h == 'number'){
23162             
23163             this.iframe.style.height = h + 'px';
23164             if(this.doc){
23165                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
23166             }
23167         }
23168         
23169     },
23170
23171     /**
23172      * Toggles the editor between standard and source edit mode.
23173      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23174      */
23175     toggleSourceEdit : function(sourceEditMode){
23176         
23177         this.sourceEditMode = sourceEditMode === true;
23178         
23179         if(this.sourceEditMode){
23180  
23181             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
23182             
23183         }else{
23184             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
23185             //this.iframe.className = '';
23186             this.deferFocus();
23187         }
23188         //this.setSize(this.owner.wrap.getSize());
23189         //this.fireEvent('editmodechange', this, this.sourceEditMode);
23190     },
23191
23192     
23193   
23194
23195     /**
23196      * Protected method that will not generally be called directly. If you need/want
23197      * custom HTML cleanup, this is the method you should override.
23198      * @param {String} html The HTML to be cleaned
23199      * return {String} The cleaned HTML
23200      */
23201     cleanHtml : function(html){
23202         html = String(html);
23203         if(html.length > 5){
23204             if(Roo.isSafari){ // strip safari nonsense
23205                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
23206             }
23207         }
23208         if(html == '&nbsp;'){
23209             html = '';
23210         }
23211         return html;
23212     },
23213
23214     /**
23215      * HTML Editor -> Textarea
23216      * Protected method that will not generally be called directly. Syncs the contents
23217      * of the editor iframe with the textarea.
23218      */
23219     syncValue : function(){
23220         if(this.initialized){
23221             var bd = (this.doc.body || this.doc.documentElement);
23222             //this.cleanUpPaste(); -- this is done else where and causes havoc..
23223             var html = bd.innerHTML;
23224             if(Roo.isSafari){
23225                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
23226                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
23227                 if(m && m[1]){
23228                     html = '<div style="'+m[0]+'">' + html + '</div>';
23229                 }
23230             }
23231             html = this.cleanHtml(html);
23232             // fix up the special chars.. normaly like back quotes in word...
23233             // however we do not want to do this with chinese..
23234             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
23235                 
23236                 var cc = match.charCodeAt();
23237
23238                 // Get the character value, handling surrogate pairs
23239                 if (match.length == 2) {
23240                     // It's a surrogate pair, calculate the Unicode code point
23241                     var high = match.charCodeAt(0) - 0xD800;
23242                     var low  = match.charCodeAt(1) - 0xDC00;
23243                     cc = (high * 0x400) + low + 0x10000;
23244                 }  else if (
23245                     (cc >= 0x4E00 && cc < 0xA000 ) ||
23246                     (cc >= 0x3400 && cc < 0x4E00 ) ||
23247                     (cc >= 0xf900 && cc < 0xfb00 )
23248                 ) {
23249                         return match;
23250                 }  
23251          
23252                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
23253                 return "&#" + cc + ";";
23254                 
23255                 
23256             });
23257             
23258             
23259              
23260             if(this.owner.fireEvent('beforesync', this, html) !== false){
23261                 this.el.dom.value = html;
23262                 this.owner.fireEvent('sync', this, html);
23263             }
23264         }
23265     },
23266
23267     /**
23268      * Protected method that will not generally be called directly. Pushes the value of the textarea
23269      * into the iframe editor.
23270      */
23271     pushValue : function(){
23272         if(this.initialized){
23273             var v = this.el.dom.value.trim();
23274             
23275 //            if(v.length < 1){
23276 //                v = '&#160;';
23277 //            }
23278             
23279             if(this.owner.fireEvent('beforepush', this, v) !== false){
23280                 var d = (this.doc.body || this.doc.documentElement);
23281                 d.innerHTML = v;
23282                 this.cleanUpPaste();
23283                 this.el.dom.value = d.innerHTML;
23284                 this.owner.fireEvent('push', this, v);
23285             }
23286         }
23287     },
23288
23289     // private
23290     deferFocus : function(){
23291         this.focus.defer(10, this);
23292     },
23293
23294     // doc'ed in Field
23295     focus : function(){
23296         if(this.win && !this.sourceEditMode){
23297             this.win.focus();
23298         }else{
23299             this.el.focus();
23300         }
23301     },
23302     
23303     assignDocWin: function()
23304     {
23305         var iframe = this.iframe;
23306         
23307          if(Roo.isIE){
23308             this.doc = iframe.contentWindow.document;
23309             this.win = iframe.contentWindow;
23310         } else {
23311 //            if (!Roo.get(this.frameId)) {
23312 //                return;
23313 //            }
23314 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
23315 //            this.win = Roo.get(this.frameId).dom.contentWindow;
23316             
23317             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
23318                 return;
23319             }
23320             
23321             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
23322             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
23323         }
23324     },
23325     
23326     // private
23327     initEditor : function(){
23328         //console.log("INIT EDITOR");
23329         this.assignDocWin();
23330         
23331         
23332         
23333         this.doc.designMode="on";
23334         this.doc.open();
23335         this.doc.write(this.getDocMarkup());
23336         this.doc.close();
23337         
23338         var dbody = (this.doc.body || this.doc.documentElement);
23339         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
23340         // this copies styles from the containing element into thsi one..
23341         // not sure why we need all of this..
23342         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
23343         
23344         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
23345         //ss['background-attachment'] = 'fixed'; // w3c
23346         dbody.bgProperties = 'fixed'; // ie
23347         //Roo.DomHelper.applyStyles(dbody, ss);
23348         Roo.EventManager.on(this.doc, {
23349             //'mousedown': this.onEditorEvent,
23350             'mouseup': this.onEditorEvent,
23351             'dblclick': this.onEditorEvent,
23352             'click': this.onEditorEvent,
23353             'keyup': this.onEditorEvent,
23354             buffer:100,
23355             scope: this
23356         });
23357         if(Roo.isGecko){
23358             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
23359         }
23360         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
23361             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
23362         }
23363         this.initialized = true;
23364
23365         this.owner.fireEvent('initialize', this);
23366         this.pushValue();
23367     },
23368
23369     // private
23370     onDestroy : function(){
23371         
23372         
23373         
23374         if(this.rendered){
23375             
23376             //for (var i =0; i < this.toolbars.length;i++) {
23377             //    // fixme - ask toolbars for heights?
23378             //    this.toolbars[i].onDestroy();
23379            // }
23380             
23381             //this.wrap.dom.innerHTML = '';
23382             //this.wrap.remove();
23383         }
23384     },
23385
23386     // private
23387     onFirstFocus : function(){
23388         
23389         this.assignDocWin();
23390         
23391         
23392         this.activated = true;
23393          
23394     
23395         if(Roo.isGecko){ // prevent silly gecko errors
23396             this.win.focus();
23397             var s = this.win.getSelection();
23398             if(!s.focusNode || s.focusNode.nodeType != 3){
23399                 var r = s.getRangeAt(0);
23400                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
23401                 r.collapse(true);
23402                 this.deferFocus();
23403             }
23404             try{
23405                 this.execCmd('useCSS', true);
23406                 this.execCmd('styleWithCSS', false);
23407             }catch(e){}
23408         }
23409         this.owner.fireEvent('activate', this);
23410     },
23411
23412     // private
23413     adjustFont: function(btn){
23414         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
23415         //if(Roo.isSafari){ // safari
23416         //    adjust *= 2;
23417        // }
23418         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
23419         if(Roo.isSafari){ // safari
23420             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
23421             v =  (v < 10) ? 10 : v;
23422             v =  (v > 48) ? 48 : v;
23423             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
23424             
23425         }
23426         
23427         
23428         v = Math.max(1, v+adjust);
23429         
23430         this.execCmd('FontSize', v  );
23431     },
23432
23433     onEditorEvent : function(e)
23434     {
23435         this.owner.fireEvent('editorevent', this, e);
23436       //  this.updateToolbar();
23437         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
23438     },
23439
23440     insertTag : function(tg)
23441     {
23442         // could be a bit smarter... -> wrap the current selected tRoo..
23443         if (tg.toLowerCase() == 'span' ||
23444             tg.toLowerCase() == 'code' ||
23445             tg.toLowerCase() == 'sup' ||
23446             tg.toLowerCase() == 'sub' 
23447             ) {
23448             
23449             range = this.createRange(this.getSelection());
23450             var wrappingNode = this.doc.createElement(tg.toLowerCase());
23451             wrappingNode.appendChild(range.extractContents());
23452             range.insertNode(wrappingNode);
23453
23454             return;
23455             
23456             
23457             
23458         }
23459         this.execCmd("formatblock",   tg);
23460         
23461     },
23462     
23463     insertText : function(txt)
23464     {
23465         
23466         
23467         var range = this.createRange();
23468         range.deleteContents();
23469                //alert(Sender.getAttribute('label'));
23470                
23471         range.insertNode(this.doc.createTextNode(txt));
23472     } ,
23473     
23474      
23475
23476     /**
23477      * Executes a Midas editor command on the editor document and performs necessary focus and
23478      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
23479      * @param {String} cmd The Midas command
23480      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
23481      */
23482     relayCmd : function(cmd, value){
23483         this.win.focus();
23484         this.execCmd(cmd, value);
23485         this.owner.fireEvent('editorevent', this);
23486         //this.updateToolbar();
23487         this.owner.deferFocus();
23488     },
23489
23490     /**
23491      * Executes a Midas editor command directly on the editor document.
23492      * For visual commands, you should use {@link #relayCmd} instead.
23493      * <b>This should only be called after the editor is initialized.</b>
23494      * @param {String} cmd The Midas command
23495      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
23496      */
23497     execCmd : function(cmd, value){
23498         this.doc.execCommand(cmd, false, value === undefined ? null : value);
23499         this.syncValue();
23500     },
23501  
23502  
23503    
23504     /**
23505      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
23506      * to insert tRoo.
23507      * @param {String} text | dom node.. 
23508      */
23509     insertAtCursor : function(text)
23510     {
23511         
23512         if(!this.activated){
23513             return;
23514         }
23515         /*
23516         if(Roo.isIE){
23517             this.win.focus();
23518             var r = this.doc.selection.createRange();
23519             if(r){
23520                 r.collapse(true);
23521                 r.pasteHTML(text);
23522                 this.syncValue();
23523                 this.deferFocus();
23524             
23525             }
23526             return;
23527         }
23528         */
23529         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
23530             this.win.focus();
23531             
23532             
23533             // from jquery ui (MIT licenced)
23534             var range, node;
23535             var win = this.win;
23536             
23537             if (win.getSelection && win.getSelection().getRangeAt) {
23538                 range = win.getSelection().getRangeAt(0);
23539                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
23540                 range.insertNode(node);
23541             } else if (win.document.selection && win.document.selection.createRange) {
23542                 // no firefox support
23543                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
23544                 win.document.selection.createRange().pasteHTML(txt);
23545             } else {
23546                 // no firefox support
23547                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
23548                 this.execCmd('InsertHTML', txt);
23549             } 
23550             
23551             this.syncValue();
23552             
23553             this.deferFocus();
23554         }
23555     },
23556  // private
23557     mozKeyPress : function(e){
23558         if(e.ctrlKey){
23559             var c = e.getCharCode(), cmd;
23560           
23561             if(c > 0){
23562                 c = String.fromCharCode(c).toLowerCase();
23563                 switch(c){
23564                     case 'b':
23565                         cmd = 'bold';
23566                         break;
23567                     case 'i':
23568                         cmd = 'italic';
23569                         break;
23570                     
23571                     case 'u':
23572                         cmd = 'underline';
23573                         break;
23574                     
23575                     case 'v':
23576                         this.cleanUpPaste.defer(100, this);
23577                         return;
23578                         
23579                 }
23580                 if(cmd){
23581                     this.win.focus();
23582                     this.execCmd(cmd);
23583                     this.deferFocus();
23584                     e.preventDefault();
23585                 }
23586                 
23587             }
23588         }
23589     },
23590
23591     // private
23592     fixKeys : function(){ // load time branching for fastest keydown performance
23593         if(Roo.isIE){
23594             return function(e){
23595                 var k = e.getKey(), r;
23596                 if(k == e.TAB){
23597                     e.stopEvent();
23598                     r = this.doc.selection.createRange();
23599                     if(r){
23600                         r.collapse(true);
23601                         r.pasteHTML('&#160;&#160;&#160;&#160;');
23602                         this.deferFocus();
23603                     }
23604                     return;
23605                 }
23606                 
23607                 if(k == e.ENTER){
23608                     r = this.doc.selection.createRange();
23609                     if(r){
23610                         var target = r.parentElement();
23611                         if(!target || target.tagName.toLowerCase() != 'li'){
23612                             e.stopEvent();
23613                             r.pasteHTML('<br />');
23614                             r.collapse(false);
23615                             r.select();
23616                         }
23617                     }
23618                 }
23619                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23620                     this.cleanUpPaste.defer(100, this);
23621                     return;
23622                 }
23623                 
23624                 
23625             };
23626         }else if(Roo.isOpera){
23627             return function(e){
23628                 var k = e.getKey();
23629                 if(k == e.TAB){
23630                     e.stopEvent();
23631                     this.win.focus();
23632                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
23633                     this.deferFocus();
23634                 }
23635                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23636                     this.cleanUpPaste.defer(100, this);
23637                     return;
23638                 }
23639                 
23640             };
23641         }else if(Roo.isSafari){
23642             return function(e){
23643                 var k = e.getKey();
23644                 
23645                 if(k == e.TAB){
23646                     e.stopEvent();
23647                     this.execCmd('InsertText','\t');
23648                     this.deferFocus();
23649                     return;
23650                 }
23651                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23652                     this.cleanUpPaste.defer(100, this);
23653                     return;
23654                 }
23655                 
23656              };
23657         }
23658     }(),
23659     
23660     getAllAncestors: function()
23661     {
23662         var p = this.getSelectedNode();
23663         var a = [];
23664         if (!p) {
23665             a.push(p); // push blank onto stack..
23666             p = this.getParentElement();
23667         }
23668         
23669         
23670         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
23671             a.push(p);
23672             p = p.parentNode;
23673         }
23674         a.push(this.doc.body);
23675         return a;
23676     },
23677     lastSel : false,
23678     lastSelNode : false,
23679     
23680     
23681     getSelection : function() 
23682     {
23683         this.assignDocWin();
23684         return Roo.isIE ? this.doc.selection : this.win.getSelection();
23685     },
23686     
23687     getSelectedNode: function() 
23688     {
23689         // this may only work on Gecko!!!
23690         
23691         // should we cache this!!!!
23692         
23693         
23694         
23695          
23696         var range = this.createRange(this.getSelection()).cloneRange();
23697         
23698         if (Roo.isIE) {
23699             var parent = range.parentElement();
23700             while (true) {
23701                 var testRange = range.duplicate();
23702                 testRange.moveToElementText(parent);
23703                 if (testRange.inRange(range)) {
23704                     break;
23705                 }
23706                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
23707                     break;
23708                 }
23709                 parent = parent.parentElement;
23710             }
23711             return parent;
23712         }
23713         
23714         // is ancestor a text element.
23715         var ac =  range.commonAncestorContainer;
23716         if (ac.nodeType == 3) {
23717             ac = ac.parentNode;
23718         }
23719         
23720         var ar = ac.childNodes;
23721          
23722         var nodes = [];
23723         var other_nodes = [];
23724         var has_other_nodes = false;
23725         for (var i=0;i<ar.length;i++) {
23726             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
23727                 continue;
23728             }
23729             // fullly contained node.
23730             
23731             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
23732                 nodes.push(ar[i]);
23733                 continue;
23734             }
23735             
23736             // probably selected..
23737             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
23738                 other_nodes.push(ar[i]);
23739                 continue;
23740             }
23741             // outer..
23742             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
23743                 continue;
23744             }
23745             
23746             
23747             has_other_nodes = true;
23748         }
23749         if (!nodes.length && other_nodes.length) {
23750             nodes= other_nodes;
23751         }
23752         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
23753             return false;
23754         }
23755         
23756         return nodes[0];
23757     },
23758     createRange: function(sel)
23759     {
23760         // this has strange effects when using with 
23761         // top toolbar - not sure if it's a great idea.
23762         //this.editor.contentWindow.focus();
23763         if (typeof sel != "undefined") {
23764             try {
23765                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
23766             } catch(e) {
23767                 return this.doc.createRange();
23768             }
23769         } else {
23770             return this.doc.createRange();
23771         }
23772     },
23773     getParentElement: function()
23774     {
23775         
23776         this.assignDocWin();
23777         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
23778         
23779         var range = this.createRange(sel);
23780          
23781         try {
23782             var p = range.commonAncestorContainer;
23783             while (p.nodeType == 3) { // text node
23784                 p = p.parentNode;
23785             }
23786             return p;
23787         } catch (e) {
23788             return null;
23789         }
23790     
23791     },
23792     /***
23793      *
23794      * Range intersection.. the hard stuff...
23795      *  '-1' = before
23796      *  '0' = hits..
23797      *  '1' = after.
23798      *         [ -- selected range --- ]
23799      *   [fail]                        [fail]
23800      *
23801      *    basically..
23802      *      if end is before start or  hits it. fail.
23803      *      if start is after end or hits it fail.
23804      *
23805      *   if either hits (but other is outside. - then it's not 
23806      *   
23807      *    
23808      **/
23809     
23810     
23811     // @see http://www.thismuchiknow.co.uk/?p=64.
23812     rangeIntersectsNode : function(range, node)
23813     {
23814         var nodeRange = node.ownerDocument.createRange();
23815         try {
23816             nodeRange.selectNode(node);
23817         } catch (e) {
23818             nodeRange.selectNodeContents(node);
23819         }
23820     
23821         var rangeStartRange = range.cloneRange();
23822         rangeStartRange.collapse(true);
23823     
23824         var rangeEndRange = range.cloneRange();
23825         rangeEndRange.collapse(false);
23826     
23827         var nodeStartRange = nodeRange.cloneRange();
23828         nodeStartRange.collapse(true);
23829     
23830         var nodeEndRange = nodeRange.cloneRange();
23831         nodeEndRange.collapse(false);
23832     
23833         return rangeStartRange.compareBoundaryPoints(
23834                  Range.START_TO_START, nodeEndRange) == -1 &&
23835                rangeEndRange.compareBoundaryPoints(
23836                  Range.START_TO_START, nodeStartRange) == 1;
23837         
23838          
23839     },
23840     rangeCompareNode : function(range, node)
23841     {
23842         var nodeRange = node.ownerDocument.createRange();
23843         try {
23844             nodeRange.selectNode(node);
23845         } catch (e) {
23846             nodeRange.selectNodeContents(node);
23847         }
23848         
23849         
23850         range.collapse(true);
23851     
23852         nodeRange.collapse(true);
23853      
23854         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
23855         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
23856          
23857         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
23858         
23859         var nodeIsBefore   =  ss == 1;
23860         var nodeIsAfter    = ee == -1;
23861         
23862         if (nodeIsBefore && nodeIsAfter) {
23863             return 0; // outer
23864         }
23865         if (!nodeIsBefore && nodeIsAfter) {
23866             return 1; //right trailed.
23867         }
23868         
23869         if (nodeIsBefore && !nodeIsAfter) {
23870             return 2;  // left trailed.
23871         }
23872         // fully contined.
23873         return 3;
23874     },
23875
23876     // private? - in a new class?
23877     cleanUpPaste :  function()
23878     {
23879         // cleans up the whole document..
23880         Roo.log('cleanuppaste');
23881         
23882         this.cleanUpChildren(this.doc.body);
23883         var clean = this.cleanWordChars(this.doc.body.innerHTML);
23884         if (clean != this.doc.body.innerHTML) {
23885             this.doc.body.innerHTML = clean;
23886         }
23887         
23888     },
23889     
23890     cleanWordChars : function(input) {// change the chars to hex code
23891         var he = Roo.HtmlEditorCore;
23892         
23893         var output = input;
23894         Roo.each(he.swapCodes, function(sw) { 
23895             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
23896             
23897             output = output.replace(swapper, sw[1]);
23898         });
23899         
23900         return output;
23901     },
23902     
23903     
23904     cleanUpChildren : function (n)
23905     {
23906         if (!n.childNodes.length) {
23907             return;
23908         }
23909         for (var i = n.childNodes.length-1; i > -1 ; i--) {
23910            this.cleanUpChild(n.childNodes[i]);
23911         }
23912     },
23913     
23914     
23915         
23916     
23917     cleanUpChild : function (node)
23918     {
23919         var ed = this;
23920         //console.log(node);
23921         if (node.nodeName == "#text") {
23922             // clean up silly Windows -- stuff?
23923             return; 
23924         }
23925         if (node.nodeName == "#comment") {
23926             node.parentNode.removeChild(node);
23927             // clean up silly Windows -- stuff?
23928             return; 
23929         }
23930         var lcname = node.tagName.toLowerCase();
23931         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
23932         // whitelist of tags..
23933         
23934         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
23935             // remove node.
23936             node.parentNode.removeChild(node);
23937             return;
23938             
23939         }
23940         
23941         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
23942         
23943         // spans with no attributes - just remove them..
23944         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
23945             remove_keep_children = true;
23946         }
23947         
23948         // remove <a name=....> as rendering on yahoo mailer is borked with this.
23949         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
23950         
23951         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
23952         //    remove_keep_children = true;
23953         //}
23954         
23955         if (remove_keep_children) {
23956             this.cleanUpChildren(node);
23957             // inserts everything just before this node...
23958             while (node.childNodes.length) {
23959                 var cn = node.childNodes[0];
23960                 node.removeChild(cn);
23961                 node.parentNode.insertBefore(cn, node);
23962             }
23963             node.parentNode.removeChild(node);
23964             return;
23965         }
23966         
23967         if (!node.attributes || !node.attributes.length) {
23968             
23969           
23970             
23971             
23972             this.cleanUpChildren(node);
23973             return;
23974         }
23975         
23976         function cleanAttr(n,v)
23977         {
23978             
23979             if (v.match(/^\./) || v.match(/^\//)) {
23980                 return;
23981             }
23982             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
23983                 return;
23984             }
23985             if (v.match(/^#/)) {
23986                 return;
23987             }
23988 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
23989             node.removeAttribute(n);
23990             
23991         }
23992         
23993         var cwhite = this.cwhite;
23994         var cblack = this.cblack;
23995             
23996         function cleanStyle(n,v)
23997         {
23998             if (v.match(/expression/)) { //XSS?? should we even bother..
23999                 node.removeAttribute(n);
24000                 return;
24001             }
24002             
24003             var parts = v.split(/;/);
24004             var clean = [];
24005             
24006             Roo.each(parts, function(p) {
24007                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
24008                 if (!p.length) {
24009                     return true;
24010                 }
24011                 var l = p.split(':').shift().replace(/\s+/g,'');
24012                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
24013                 
24014                 if ( cwhite.length && cblack.indexOf(l) > -1) {
24015 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24016                     //node.removeAttribute(n);
24017                     return true;
24018                 }
24019                 //Roo.log()
24020                 // only allow 'c whitelisted system attributes'
24021                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
24022 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24023                     //node.removeAttribute(n);
24024                     return true;
24025                 }
24026                 
24027                 
24028                  
24029                 
24030                 clean.push(p);
24031                 return true;
24032             });
24033             if (clean.length) { 
24034                 node.setAttribute(n, clean.join(';'));
24035             } else {
24036                 node.removeAttribute(n);
24037             }
24038             
24039         }
24040         
24041         
24042         for (var i = node.attributes.length-1; i > -1 ; i--) {
24043             var a = node.attributes[i];
24044             //console.log(a);
24045             
24046             if (a.name.toLowerCase().substr(0,2)=='on')  {
24047                 node.removeAttribute(a.name);
24048                 continue;
24049             }
24050             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
24051                 node.removeAttribute(a.name);
24052                 continue;
24053             }
24054             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
24055                 cleanAttr(a.name,a.value); // fixme..
24056                 continue;
24057             }
24058             if (a.name == 'style') {
24059                 cleanStyle(a.name,a.value);
24060                 continue;
24061             }
24062             /// clean up MS crap..
24063             // tecnically this should be a list of valid class'es..
24064             
24065             
24066             if (a.name == 'class') {
24067                 if (a.value.match(/^Mso/)) {
24068                     node.removeAttribute('class');
24069                 }
24070                 
24071                 if (a.value.match(/^body$/)) {
24072                     node.removeAttribute('class');
24073                 }
24074                 continue;
24075             }
24076             
24077             // style cleanup!?
24078             // class cleanup?
24079             
24080         }
24081         
24082         
24083         this.cleanUpChildren(node);
24084         
24085         
24086     },
24087     
24088     /**
24089      * Clean up MS wordisms...
24090      */
24091     cleanWord : function(node)
24092     {
24093         if (!node) {
24094             this.cleanWord(this.doc.body);
24095             return;
24096         }
24097         
24098         if(
24099                 node.nodeName == 'SPAN' &&
24100                 !node.hasAttributes() &&
24101                 node.childNodes.length == 1 &&
24102                 node.firstChild.nodeName == "#text"  
24103         ) {
24104             var textNode = node.firstChild;
24105             node.removeChild(textNode);
24106             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
24107                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
24108             }
24109             node.parentNode.insertBefore(textNode, node);
24110             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
24111                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
24112             }
24113             node.parentNode.removeChild(node);
24114         }
24115         
24116         if (node.nodeName == "#text") {
24117             // clean up silly Windows -- stuff?
24118             return; 
24119         }
24120         if (node.nodeName == "#comment") {
24121             node.parentNode.removeChild(node);
24122             // clean up silly Windows -- stuff?
24123             return; 
24124         }
24125         
24126         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
24127             node.parentNode.removeChild(node);
24128             return;
24129         }
24130         //Roo.log(node.tagName);
24131         // remove - but keep children..
24132         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
24133             //Roo.log('-- removed');
24134             while (node.childNodes.length) {
24135                 var cn = node.childNodes[0];
24136                 node.removeChild(cn);
24137                 node.parentNode.insertBefore(cn, node);
24138                 // move node to parent - and clean it..
24139                 this.cleanWord(cn);
24140             }
24141             node.parentNode.removeChild(node);
24142             /// no need to iterate chidlren = it's got none..
24143             //this.iterateChildren(node, this.cleanWord);
24144             return;
24145         }
24146         // clean styles
24147         if (node.className.length) {
24148             
24149             var cn = node.className.split(/\W+/);
24150             var cna = [];
24151             Roo.each(cn, function(cls) {
24152                 if (cls.match(/Mso[a-zA-Z]+/)) {
24153                     return;
24154                 }
24155                 cna.push(cls);
24156             });
24157             node.className = cna.length ? cna.join(' ') : '';
24158             if (!cna.length) {
24159                 node.removeAttribute("class");
24160             }
24161         }
24162         
24163         if (node.hasAttribute("lang")) {
24164             node.removeAttribute("lang");
24165         }
24166         
24167         if (node.hasAttribute("style")) {
24168             
24169             var styles = node.getAttribute("style").split(";");
24170             var nstyle = [];
24171             Roo.each(styles, function(s) {
24172                 if (!s.match(/:/)) {
24173                     return;
24174                 }
24175                 var kv = s.split(":");
24176                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
24177                     return;
24178                 }
24179                 // what ever is left... we allow.
24180                 nstyle.push(s);
24181             });
24182             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
24183             if (!nstyle.length) {
24184                 node.removeAttribute('style');
24185             }
24186         }
24187         this.iterateChildren(node, this.cleanWord);
24188         
24189         
24190         
24191     },
24192     /**
24193      * iterateChildren of a Node, calling fn each time, using this as the scole..
24194      * @param {DomNode} node node to iterate children of.
24195      * @param {Function} fn method of this class to call on each item.
24196      */
24197     iterateChildren : function(node, fn)
24198     {
24199         if (!node.childNodes.length) {
24200                 return;
24201         }
24202         for (var i = node.childNodes.length-1; i > -1 ; i--) {
24203            fn.call(this, node.childNodes[i])
24204         }
24205     },
24206     
24207     
24208     /**
24209      * cleanTableWidths.
24210      *
24211      * Quite often pasting from word etc.. results in tables with column and widths.
24212      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
24213      *
24214      */
24215     cleanTableWidths : function(node)
24216     {
24217          
24218          
24219         if (!node) {
24220             this.cleanTableWidths(this.doc.body);
24221             return;
24222         }
24223         
24224         // ignore list...
24225         if (node.nodeName == "#text" || node.nodeName == "#comment") {
24226             return; 
24227         }
24228         Roo.log(node.tagName);
24229         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
24230             this.iterateChildren(node, this.cleanTableWidths);
24231             return;
24232         }
24233         if (node.hasAttribute('width')) {
24234             node.removeAttribute('width');
24235         }
24236         
24237          
24238         if (node.hasAttribute("style")) {
24239             // pretty basic...
24240             
24241             var styles = node.getAttribute("style").split(";");
24242             var nstyle = [];
24243             Roo.each(styles, function(s) {
24244                 if (!s.match(/:/)) {
24245                     return;
24246                 }
24247                 var kv = s.split(":");
24248                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
24249                     return;
24250                 }
24251                 // what ever is left... we allow.
24252                 nstyle.push(s);
24253             });
24254             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
24255             if (!nstyle.length) {
24256                 node.removeAttribute('style');
24257             }
24258         }
24259         
24260         this.iterateChildren(node, this.cleanTableWidths);
24261         
24262         
24263     },
24264     
24265     
24266     
24267     
24268     domToHTML : function(currentElement, depth, nopadtext) {
24269         
24270         depth = depth || 0;
24271         nopadtext = nopadtext || false;
24272     
24273         if (!currentElement) {
24274             return this.domToHTML(this.doc.body);
24275         }
24276         
24277         //Roo.log(currentElement);
24278         var j;
24279         var allText = false;
24280         var nodeName = currentElement.nodeName;
24281         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
24282         
24283         if  (nodeName == '#text') {
24284             
24285             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
24286         }
24287         
24288         
24289         var ret = '';
24290         if (nodeName != 'BODY') {
24291              
24292             var i = 0;
24293             // Prints the node tagName, such as <A>, <IMG>, etc
24294             if (tagName) {
24295                 var attr = [];
24296                 for(i = 0; i < currentElement.attributes.length;i++) {
24297                     // quoting?
24298                     var aname = currentElement.attributes.item(i).name;
24299                     if (!currentElement.attributes.item(i).value.length) {
24300                         continue;
24301                     }
24302                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
24303                 }
24304                 
24305                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
24306             } 
24307             else {
24308                 
24309                 // eack
24310             }
24311         } else {
24312             tagName = false;
24313         }
24314         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
24315             return ret;
24316         }
24317         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
24318             nopadtext = true;
24319         }
24320         
24321         
24322         // Traverse the tree
24323         i = 0;
24324         var currentElementChild = currentElement.childNodes.item(i);
24325         var allText = true;
24326         var innerHTML  = '';
24327         lastnode = '';
24328         while (currentElementChild) {
24329             // Formatting code (indent the tree so it looks nice on the screen)
24330             var nopad = nopadtext;
24331             if (lastnode == 'SPAN') {
24332                 nopad  = true;
24333             }
24334             // text
24335             if  (currentElementChild.nodeName == '#text') {
24336                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
24337                 toadd = nopadtext ? toadd : toadd.trim();
24338                 if (!nopad && toadd.length > 80) {
24339                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
24340                 }
24341                 innerHTML  += toadd;
24342                 
24343                 i++;
24344                 currentElementChild = currentElement.childNodes.item(i);
24345                 lastNode = '';
24346                 continue;
24347             }
24348             allText = false;
24349             
24350             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
24351                 
24352             // Recursively traverse the tree structure of the child node
24353             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
24354             lastnode = currentElementChild.nodeName;
24355             i++;
24356             currentElementChild=currentElement.childNodes.item(i);
24357         }
24358         
24359         ret += innerHTML;
24360         
24361         if (!allText) {
24362                 // The remaining code is mostly for formatting the tree
24363             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
24364         }
24365         
24366         
24367         if (tagName) {
24368             ret+= "</"+tagName+">";
24369         }
24370         return ret;
24371         
24372     },
24373         
24374     applyBlacklists : function()
24375     {
24376         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
24377         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
24378         
24379         this.white = [];
24380         this.black = [];
24381         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
24382             if (b.indexOf(tag) > -1) {
24383                 return;
24384             }
24385             this.white.push(tag);
24386             
24387         }, this);
24388         
24389         Roo.each(w, function(tag) {
24390             if (b.indexOf(tag) > -1) {
24391                 return;
24392             }
24393             if (this.white.indexOf(tag) > -1) {
24394                 return;
24395             }
24396             this.white.push(tag);
24397             
24398         }, this);
24399         
24400         
24401         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
24402             if (w.indexOf(tag) > -1) {
24403                 return;
24404             }
24405             this.black.push(tag);
24406             
24407         }, this);
24408         
24409         Roo.each(b, function(tag) {
24410             if (w.indexOf(tag) > -1) {
24411                 return;
24412             }
24413             if (this.black.indexOf(tag) > -1) {
24414                 return;
24415             }
24416             this.black.push(tag);
24417             
24418         }, this);
24419         
24420         
24421         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
24422         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
24423         
24424         this.cwhite = [];
24425         this.cblack = [];
24426         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
24427             if (b.indexOf(tag) > -1) {
24428                 return;
24429             }
24430             this.cwhite.push(tag);
24431             
24432         }, this);
24433         
24434         Roo.each(w, function(tag) {
24435             if (b.indexOf(tag) > -1) {
24436                 return;
24437             }
24438             if (this.cwhite.indexOf(tag) > -1) {
24439                 return;
24440             }
24441             this.cwhite.push(tag);
24442             
24443         }, this);
24444         
24445         
24446         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
24447             if (w.indexOf(tag) > -1) {
24448                 return;
24449             }
24450             this.cblack.push(tag);
24451             
24452         }, this);
24453         
24454         Roo.each(b, function(tag) {
24455             if (w.indexOf(tag) > -1) {
24456                 return;
24457             }
24458             if (this.cblack.indexOf(tag) > -1) {
24459                 return;
24460             }
24461             this.cblack.push(tag);
24462             
24463         }, this);
24464     },
24465     
24466     setStylesheets : function(stylesheets)
24467     {
24468         if(typeof(stylesheets) == 'string'){
24469             Roo.get(this.iframe.contentDocument.head).createChild({
24470                 tag : 'link',
24471                 rel : 'stylesheet',
24472                 type : 'text/css',
24473                 href : stylesheets
24474             });
24475             
24476             return;
24477         }
24478         var _this = this;
24479      
24480         Roo.each(stylesheets, function(s) {
24481             if(!s.length){
24482                 return;
24483             }
24484             
24485             Roo.get(_this.iframe.contentDocument.head).createChild({
24486                 tag : 'link',
24487                 rel : 'stylesheet',
24488                 type : 'text/css',
24489                 href : s
24490             });
24491         });
24492
24493         
24494     },
24495     
24496     removeStylesheets : function()
24497     {
24498         var _this = this;
24499         
24500         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
24501             s.remove();
24502         });
24503     },
24504     
24505     setStyle : function(style)
24506     {
24507         Roo.get(this.iframe.contentDocument.head).createChild({
24508             tag : 'style',
24509             type : 'text/css',
24510             html : style
24511         });
24512
24513         return;
24514     }
24515     
24516     // hide stuff that is not compatible
24517     /**
24518      * @event blur
24519      * @hide
24520      */
24521     /**
24522      * @event change
24523      * @hide
24524      */
24525     /**
24526      * @event focus
24527      * @hide
24528      */
24529     /**
24530      * @event specialkey
24531      * @hide
24532      */
24533     /**
24534      * @cfg {String} fieldClass @hide
24535      */
24536     /**
24537      * @cfg {String} focusClass @hide
24538      */
24539     /**
24540      * @cfg {String} autoCreate @hide
24541      */
24542     /**
24543      * @cfg {String} inputType @hide
24544      */
24545     /**
24546      * @cfg {String} invalidClass @hide
24547      */
24548     /**
24549      * @cfg {String} invalidText @hide
24550      */
24551     /**
24552      * @cfg {String} msgFx @hide
24553      */
24554     /**
24555      * @cfg {String} validateOnBlur @hide
24556      */
24557 });
24558
24559 Roo.HtmlEditorCore.white = [
24560         'area', 'br', 'img', 'input', 'hr', 'wbr',
24561         
24562        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
24563        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
24564        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
24565        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
24566        'table',   'ul',         'xmp', 
24567        
24568        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
24569       'thead',   'tr', 
24570      
24571       'dir', 'menu', 'ol', 'ul', 'dl',
24572        
24573       'embed',  'object'
24574 ];
24575
24576
24577 Roo.HtmlEditorCore.black = [
24578     //    'embed',  'object', // enable - backend responsiblity to clean thiese
24579         'applet', // 
24580         'base',   'basefont', 'bgsound', 'blink',  'body', 
24581         'frame',  'frameset', 'head',    'html',   'ilayer', 
24582         'iframe', 'layer',  'link',     'meta',    'object',   
24583         'script', 'style' ,'title',  'xml' // clean later..
24584 ];
24585 Roo.HtmlEditorCore.clean = [
24586     'script', 'style', 'title', 'xml'
24587 ];
24588 Roo.HtmlEditorCore.remove = [
24589     'font'
24590 ];
24591 // attributes..
24592
24593 Roo.HtmlEditorCore.ablack = [
24594     'on'
24595 ];
24596     
24597 Roo.HtmlEditorCore.aclean = [ 
24598     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
24599 ];
24600
24601 // protocols..
24602 Roo.HtmlEditorCore.pwhite= [
24603         'http',  'https',  'mailto'
24604 ];
24605
24606 // white listed style attributes.
24607 Roo.HtmlEditorCore.cwhite= [
24608       //  'text-align', /// default is to allow most things..
24609       
24610          
24611 //        'font-size'//??
24612 ];
24613
24614 // black listed style attributes.
24615 Roo.HtmlEditorCore.cblack= [
24616       //  'font-size' -- this can be set by the project 
24617 ];
24618
24619
24620 Roo.HtmlEditorCore.swapCodes   =[ 
24621     [    8211, "--" ], 
24622     [    8212, "--" ], 
24623     [    8216,  "'" ],  
24624     [    8217, "'" ],  
24625     [    8220, '"' ],  
24626     [    8221, '"' ],  
24627     [    8226, "*" ],  
24628     [    8230, "..." ]
24629 ]; 
24630
24631     /*
24632  * - LGPL
24633  *
24634  * HtmlEditor
24635  * 
24636  */
24637
24638 /**
24639  * @class Roo.bootstrap.HtmlEditor
24640  * @extends Roo.bootstrap.TextArea
24641  * Bootstrap HtmlEditor class
24642
24643  * @constructor
24644  * Create a new HtmlEditor
24645  * @param {Object} config The config object
24646  */
24647
24648 Roo.bootstrap.HtmlEditor = function(config){
24649     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
24650     if (!this.toolbars) {
24651         this.toolbars = [];
24652     }
24653     
24654     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
24655     this.addEvents({
24656             /**
24657              * @event initialize
24658              * Fires when the editor is fully initialized (including the iframe)
24659              * @param {HtmlEditor} this
24660              */
24661             initialize: true,
24662             /**
24663              * @event activate
24664              * Fires when the editor is first receives the focus. Any insertion must wait
24665              * until after this event.
24666              * @param {HtmlEditor} this
24667              */
24668             activate: true,
24669              /**
24670              * @event beforesync
24671              * Fires before the textarea is updated with content from the editor iframe. Return false
24672              * to cancel the sync.
24673              * @param {HtmlEditor} this
24674              * @param {String} html
24675              */
24676             beforesync: true,
24677              /**
24678              * @event beforepush
24679              * Fires before the iframe editor is updated with content from the textarea. Return false
24680              * to cancel the push.
24681              * @param {HtmlEditor} this
24682              * @param {String} html
24683              */
24684             beforepush: true,
24685              /**
24686              * @event sync
24687              * Fires when the textarea is updated with content from the editor iframe.
24688              * @param {HtmlEditor} this
24689              * @param {String} html
24690              */
24691             sync: true,
24692              /**
24693              * @event push
24694              * Fires when the iframe editor is updated with content from the textarea.
24695              * @param {HtmlEditor} this
24696              * @param {String} html
24697              */
24698             push: true,
24699              /**
24700              * @event editmodechange
24701              * Fires when the editor switches edit modes
24702              * @param {HtmlEditor} this
24703              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
24704              */
24705             editmodechange: true,
24706             /**
24707              * @event editorevent
24708              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24709              * @param {HtmlEditor} this
24710              */
24711             editorevent: true,
24712             /**
24713              * @event firstfocus
24714              * Fires when on first focus - needed by toolbars..
24715              * @param {HtmlEditor} this
24716              */
24717             firstfocus: true,
24718             /**
24719              * @event autosave
24720              * Auto save the htmlEditor value as a file into Events
24721              * @param {HtmlEditor} this
24722              */
24723             autosave: true,
24724             /**
24725              * @event savedpreview
24726              * preview the saved version of htmlEditor
24727              * @param {HtmlEditor} this
24728              */
24729             savedpreview: true
24730         });
24731 };
24732
24733
24734 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
24735     
24736     
24737       /**
24738      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
24739      */
24740     toolbars : false,
24741     
24742      /**
24743     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
24744     */
24745     btns : [],
24746    
24747      /**
24748      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24749      *                        Roo.resizable.
24750      */
24751     resizable : false,
24752      /**
24753      * @cfg {Number} height (in pixels)
24754      */   
24755     height: 300,
24756    /**
24757      * @cfg {Number} width (in pixels)
24758      */   
24759     width: false,
24760     
24761     /**
24762      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24763      * 
24764      */
24765     stylesheets: false,
24766     
24767     // id of frame..
24768     frameId: false,
24769     
24770     // private properties
24771     validationEvent : false,
24772     deferHeight: true,
24773     initialized : false,
24774     activated : false,
24775     
24776     onFocus : Roo.emptyFn,
24777     iframePad:3,
24778     hideMode:'offsets',
24779     
24780     tbContainer : false,
24781     
24782     bodyCls : '',
24783     
24784     toolbarContainer :function() {
24785         return this.wrap.select('.x-html-editor-tb',true).first();
24786     },
24787
24788     /**
24789      * Protected method that will not generally be called directly. It
24790      * is called when the editor creates its toolbar. Override this method if you need to
24791      * add custom toolbar buttons.
24792      * @param {HtmlEditor} editor
24793      */
24794     createToolbar : function(){
24795         Roo.log('renewing');
24796         Roo.log("create toolbars");
24797         
24798         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
24799         this.toolbars[0].render(this.toolbarContainer());
24800         
24801         return;
24802         
24803 //        if (!editor.toolbars || !editor.toolbars.length) {
24804 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
24805 //        }
24806 //        
24807 //        for (var i =0 ; i < editor.toolbars.length;i++) {
24808 //            editor.toolbars[i] = Roo.factory(
24809 //                    typeof(editor.toolbars[i]) == 'string' ?
24810 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
24811 //                Roo.bootstrap.HtmlEditor);
24812 //            editor.toolbars[i].init(editor);
24813 //        }
24814     },
24815
24816      
24817     // private
24818     onRender : function(ct, position)
24819     {
24820        // Roo.log("Call onRender: " + this.xtype);
24821         var _t = this;
24822         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
24823       
24824         this.wrap = this.inputEl().wrap({
24825             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
24826         });
24827         
24828         this.editorcore.onRender(ct, position);
24829          
24830         if (this.resizable) {
24831             this.resizeEl = new Roo.Resizable(this.wrap, {
24832                 pinned : true,
24833                 wrap: true,
24834                 dynamic : true,
24835                 minHeight : this.height,
24836                 height: this.height,
24837                 handles : this.resizable,
24838                 width: this.width,
24839                 listeners : {
24840                     resize : function(r, w, h) {
24841                         _t.onResize(w,h); // -something
24842                     }
24843                 }
24844             });
24845             
24846         }
24847         this.createToolbar(this);
24848        
24849         
24850         if(!this.width && this.resizable){
24851             this.setSize(this.wrap.getSize());
24852         }
24853         if (this.resizeEl) {
24854             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
24855             // should trigger onReize..
24856         }
24857         
24858     },
24859
24860     // private
24861     onResize : function(w, h)
24862     {
24863         Roo.log('resize: ' +w + ',' + h );
24864         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
24865         var ew = false;
24866         var eh = false;
24867         
24868         if(this.inputEl() ){
24869             if(typeof w == 'number'){
24870                 var aw = w - this.wrap.getFrameWidth('lr');
24871                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
24872                 ew = aw;
24873             }
24874             if(typeof h == 'number'){
24875                  var tbh = -11;  // fixme it needs to tool bar size!
24876                 for (var i =0; i < this.toolbars.length;i++) {
24877                     // fixme - ask toolbars for heights?
24878                     tbh += this.toolbars[i].el.getHeight();
24879                     //if (this.toolbars[i].footer) {
24880                     //    tbh += this.toolbars[i].footer.el.getHeight();
24881                     //}
24882                 }
24883               
24884                 
24885                 
24886                 
24887                 
24888                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
24889                 ah -= 5; // knock a few pixes off for look..
24890                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
24891                 var eh = ah;
24892             }
24893         }
24894         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
24895         this.editorcore.onResize(ew,eh);
24896         
24897     },
24898
24899     /**
24900      * Toggles the editor between standard and source edit mode.
24901      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24902      */
24903     toggleSourceEdit : function(sourceEditMode)
24904     {
24905         this.editorcore.toggleSourceEdit(sourceEditMode);
24906         
24907         if(this.editorcore.sourceEditMode){
24908             Roo.log('editor - showing textarea');
24909             
24910 //            Roo.log('in');
24911 //            Roo.log(this.syncValue());
24912             this.syncValue();
24913             this.inputEl().removeClass(['hide', 'x-hidden']);
24914             this.inputEl().dom.removeAttribute('tabIndex');
24915             this.inputEl().focus();
24916         }else{
24917             Roo.log('editor - hiding textarea');
24918 //            Roo.log('out')
24919 //            Roo.log(this.pushValue()); 
24920             this.pushValue();
24921             
24922             this.inputEl().addClass(['hide', 'x-hidden']);
24923             this.inputEl().dom.setAttribute('tabIndex', -1);
24924             //this.deferFocus();
24925         }
24926          
24927         if(this.resizable){
24928             this.setSize(this.wrap.getSize());
24929         }
24930         
24931         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
24932     },
24933  
24934     // private (for BoxComponent)
24935     adjustSize : Roo.BoxComponent.prototype.adjustSize,
24936
24937     // private (for BoxComponent)
24938     getResizeEl : function(){
24939         return this.wrap;
24940     },
24941
24942     // private (for BoxComponent)
24943     getPositionEl : function(){
24944         return this.wrap;
24945     },
24946
24947     // private
24948     initEvents : function(){
24949         this.originalValue = this.getValue();
24950     },
24951
24952 //    /**
24953 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24954 //     * @method
24955 //     */
24956 //    markInvalid : Roo.emptyFn,
24957 //    /**
24958 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24959 //     * @method
24960 //     */
24961 //    clearInvalid : Roo.emptyFn,
24962
24963     setValue : function(v){
24964         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
24965         this.editorcore.pushValue();
24966     },
24967
24968      
24969     // private
24970     deferFocus : function(){
24971         this.focus.defer(10, this);
24972     },
24973
24974     // doc'ed in Field
24975     focus : function(){
24976         this.editorcore.focus();
24977         
24978     },
24979       
24980
24981     // private
24982     onDestroy : function(){
24983         
24984         
24985         
24986         if(this.rendered){
24987             
24988             for (var i =0; i < this.toolbars.length;i++) {
24989                 // fixme - ask toolbars for heights?
24990                 this.toolbars[i].onDestroy();
24991             }
24992             
24993             this.wrap.dom.innerHTML = '';
24994             this.wrap.remove();
24995         }
24996     },
24997
24998     // private
24999     onFirstFocus : function(){
25000         //Roo.log("onFirstFocus");
25001         this.editorcore.onFirstFocus();
25002          for (var i =0; i < this.toolbars.length;i++) {
25003             this.toolbars[i].onFirstFocus();
25004         }
25005         
25006     },
25007     
25008     // private
25009     syncValue : function()
25010     {   
25011         this.editorcore.syncValue();
25012     },
25013     
25014     pushValue : function()
25015     {   
25016         this.editorcore.pushValue();
25017     }
25018      
25019     
25020     // hide stuff that is not compatible
25021     /**
25022      * @event blur
25023      * @hide
25024      */
25025     /**
25026      * @event change
25027      * @hide
25028      */
25029     /**
25030      * @event focus
25031      * @hide
25032      */
25033     /**
25034      * @event specialkey
25035      * @hide
25036      */
25037     /**
25038      * @cfg {String} fieldClass @hide
25039      */
25040     /**
25041      * @cfg {String} focusClass @hide
25042      */
25043     /**
25044      * @cfg {String} autoCreate @hide
25045      */
25046     /**
25047      * @cfg {String} inputType @hide
25048      */
25049      
25050     /**
25051      * @cfg {String} invalidText @hide
25052      */
25053     /**
25054      * @cfg {String} msgFx @hide
25055      */
25056     /**
25057      * @cfg {String} validateOnBlur @hide
25058      */
25059 });
25060  
25061     
25062    
25063    
25064    
25065       
25066 Roo.namespace('Roo.bootstrap.htmleditor');
25067 /**
25068  * @class Roo.bootstrap.HtmlEditorToolbar1
25069  * Basic Toolbar
25070  * 
25071  * @example
25072  * Usage:
25073  *
25074  new Roo.bootstrap.HtmlEditor({
25075     ....
25076     toolbars : [
25077         new Roo.bootstrap.HtmlEditorToolbar1({
25078             disable : { fonts: 1 , format: 1, ..., ... , ...],
25079             btns : [ .... ]
25080         })
25081     }
25082      
25083  * 
25084  * @cfg {Object} disable List of elements to disable..
25085  * @cfg {Array} btns List of additional buttons.
25086  * 
25087  * 
25088  * NEEDS Extra CSS? 
25089  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
25090  */
25091  
25092 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
25093 {
25094     
25095     Roo.apply(this, config);
25096     
25097     // default disabled, based on 'good practice'..
25098     this.disable = this.disable || {};
25099     Roo.applyIf(this.disable, {
25100         fontSize : true,
25101         colors : true,
25102         specialElements : true
25103     });
25104     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
25105     
25106     this.editor = config.editor;
25107     this.editorcore = config.editor.editorcore;
25108     
25109     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
25110     
25111     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25112     // dont call parent... till later.
25113 }
25114 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
25115      
25116     bar : true,
25117     
25118     editor : false,
25119     editorcore : false,
25120     
25121     
25122     formats : [
25123         "p" ,  
25124         "h1","h2","h3","h4","h5","h6", 
25125         "pre", "code", 
25126         "abbr", "acronym", "address", "cite", "samp", "var",
25127         'div','span'
25128     ],
25129     
25130     onRender : function(ct, position)
25131     {
25132        // Roo.log("Call onRender: " + this.xtype);
25133         
25134        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
25135        Roo.log(this.el);
25136        this.el.dom.style.marginBottom = '0';
25137        var _this = this;
25138        var editorcore = this.editorcore;
25139        var editor= this.editor;
25140        
25141        var children = [];
25142        var btn = function(id,cmd , toggle, handler, html){
25143        
25144             var  event = toggle ? 'toggle' : 'click';
25145        
25146             var a = {
25147                 size : 'sm',
25148                 xtype: 'Button',
25149                 xns: Roo.bootstrap,
25150                 //glyphicon : id,
25151                 fa: id,
25152                 cmd : id || cmd,
25153                 enableToggle:toggle !== false,
25154                 html : html || '',
25155                 pressed : toggle ? false : null,
25156                 listeners : {}
25157             };
25158             a.listeners[toggle ? 'toggle' : 'click'] = function() {
25159                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
25160             };
25161             children.push(a);
25162             return a;
25163        }
25164        
25165     //    var cb_box = function...
25166         
25167         var style = {
25168                 xtype: 'Button',
25169                 size : 'sm',
25170                 xns: Roo.bootstrap,
25171                 fa : 'font',
25172                 //html : 'submit'
25173                 menu : {
25174                     xtype: 'Menu',
25175                     xns: Roo.bootstrap,
25176                     items:  []
25177                 }
25178         };
25179         Roo.each(this.formats, function(f) {
25180             style.menu.items.push({
25181                 xtype :'MenuItem',
25182                 xns: Roo.bootstrap,
25183                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
25184                 tagname : f,
25185                 listeners : {
25186                     click : function()
25187                     {
25188                         editorcore.insertTag(this.tagname);
25189                         editor.focus();
25190                     }
25191                 }
25192                 
25193             });
25194         });
25195         children.push(style);   
25196         
25197         btn('bold',false,true);
25198         btn('italic',false,true);
25199         btn('align-left', 'justifyleft',true);
25200         btn('align-center', 'justifycenter',true);
25201         btn('align-right' , 'justifyright',true);
25202         btn('link', false, false, function(btn) {
25203             //Roo.log("create link?");
25204             var url = prompt(this.createLinkText, this.defaultLinkValue);
25205             if(url && url != 'http:/'+'/'){
25206                 this.editorcore.relayCmd('createlink', url);
25207             }
25208         }),
25209         btn('list','insertunorderedlist',true);
25210         btn('pencil', false,true, function(btn){
25211                 Roo.log(this);
25212                 this.toggleSourceEdit(btn.pressed);
25213         });
25214         
25215         if (this.editor.btns.length > 0) {
25216             for (var i = 0; i<this.editor.btns.length; i++) {
25217                 children.push(this.editor.btns[i]);
25218             }
25219         }
25220         
25221         /*
25222         var cog = {
25223                 xtype: 'Button',
25224                 size : 'sm',
25225                 xns: Roo.bootstrap,
25226                 glyphicon : 'cog',
25227                 //html : 'submit'
25228                 menu : {
25229                     xtype: 'Menu',
25230                     xns: Roo.bootstrap,
25231                     items:  []
25232                 }
25233         };
25234         
25235         cog.menu.items.push({
25236             xtype :'MenuItem',
25237             xns: Roo.bootstrap,
25238             html : Clean styles,
25239             tagname : f,
25240             listeners : {
25241                 click : function()
25242                 {
25243                     editorcore.insertTag(this.tagname);
25244                     editor.focus();
25245                 }
25246             }
25247             
25248         });
25249        */
25250         
25251          
25252        this.xtype = 'NavSimplebar';
25253         
25254         for(var i=0;i< children.length;i++) {
25255             
25256             this.buttons.add(this.addxtypeChild(children[i]));
25257             
25258         }
25259         
25260         editor.on('editorevent', this.updateToolbar, this);
25261     },
25262     onBtnClick : function(id)
25263     {
25264        this.editorcore.relayCmd(id);
25265        this.editorcore.focus();
25266     },
25267     
25268     /**
25269      * Protected method that will not generally be called directly. It triggers
25270      * a toolbar update by reading the markup state of the current selection in the editor.
25271      */
25272     updateToolbar: function(){
25273
25274         if(!this.editorcore.activated){
25275             this.editor.onFirstFocus(); // is this neeed?
25276             return;
25277         }
25278
25279         var btns = this.buttons; 
25280         var doc = this.editorcore.doc;
25281         btns.get('bold').setActive(doc.queryCommandState('bold'));
25282         btns.get('italic').setActive(doc.queryCommandState('italic'));
25283         //btns.get('underline').setActive(doc.queryCommandState('underline'));
25284         
25285         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
25286         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
25287         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
25288         
25289         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
25290         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
25291          /*
25292         
25293         var ans = this.editorcore.getAllAncestors();
25294         if (this.formatCombo) {
25295             
25296             
25297             var store = this.formatCombo.store;
25298             this.formatCombo.setValue("");
25299             for (var i =0; i < ans.length;i++) {
25300                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
25301                     // select it..
25302                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
25303                     break;
25304                 }
25305             }
25306         }
25307         
25308         
25309         
25310         // hides menus... - so this cant be on a menu...
25311         Roo.bootstrap.MenuMgr.hideAll();
25312         */
25313         Roo.bootstrap.MenuMgr.hideAll();
25314         //this.editorsyncValue();
25315     },
25316     onFirstFocus: function() {
25317         this.buttons.each(function(item){
25318            item.enable();
25319         });
25320     },
25321     toggleSourceEdit : function(sourceEditMode){
25322         
25323           
25324         if(sourceEditMode){
25325             Roo.log("disabling buttons");
25326            this.buttons.each( function(item){
25327                 if(item.cmd != 'pencil'){
25328                     item.disable();
25329                 }
25330             });
25331           
25332         }else{
25333             Roo.log("enabling buttons");
25334             if(this.editorcore.initialized){
25335                 this.buttons.each( function(item){
25336                     item.enable();
25337                 });
25338             }
25339             
25340         }
25341         Roo.log("calling toggole on editor");
25342         // tell the editor that it's been pressed..
25343         this.editor.toggleSourceEdit(sourceEditMode);
25344        
25345     }
25346 });
25347
25348
25349
25350
25351
25352 /**
25353  * @class Roo.bootstrap.Table.AbstractSelectionModel
25354  * @extends Roo.util.Observable
25355  * Abstract base class for grid SelectionModels.  It provides the interface that should be
25356  * implemented by descendant classes.  This class should not be directly instantiated.
25357  * @constructor
25358  */
25359 Roo.bootstrap.Table.AbstractSelectionModel = function(){
25360     this.locked = false;
25361     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
25362 };
25363
25364
25365 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
25366     /** @ignore Called by the grid automatically. Do not call directly. */
25367     init : function(grid){
25368         this.grid = grid;
25369         this.initEvents();
25370     },
25371
25372     /**
25373      * Locks the selections.
25374      */
25375     lock : function(){
25376         this.locked = true;
25377     },
25378
25379     /**
25380      * Unlocks the selections.
25381      */
25382     unlock : function(){
25383         this.locked = false;
25384     },
25385
25386     /**
25387      * Returns true if the selections are locked.
25388      * @return {Boolean}
25389      */
25390     isLocked : function(){
25391         return this.locked;
25392     },
25393     
25394     
25395     initEvents : function ()
25396     {
25397         
25398     }
25399 });
25400 /**
25401  * @extends Roo.bootstrap.Table.AbstractSelectionModel
25402  * @class Roo.bootstrap.Table.RowSelectionModel
25403  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
25404  * It supports multiple selections and keyboard selection/navigation. 
25405  * @constructor
25406  * @param {Object} config
25407  */
25408
25409 Roo.bootstrap.Table.RowSelectionModel = function(config){
25410     Roo.apply(this, config);
25411     this.selections = new Roo.util.MixedCollection(false, function(o){
25412         return o.id;
25413     });
25414
25415     this.last = false;
25416     this.lastActive = false;
25417
25418     this.addEvents({
25419         /**
25420              * @event selectionchange
25421              * Fires when the selection changes
25422              * @param {SelectionModel} this
25423              */
25424             "selectionchange" : true,
25425         /**
25426              * @event afterselectionchange
25427              * Fires after the selection changes (eg. by key press or clicking)
25428              * @param {SelectionModel} this
25429              */
25430             "afterselectionchange" : true,
25431         /**
25432              * @event beforerowselect
25433              * Fires when a row is selected being selected, return false to cancel.
25434              * @param {SelectionModel} this
25435              * @param {Number} rowIndex The selected index
25436              * @param {Boolean} keepExisting False if other selections will be cleared
25437              */
25438             "beforerowselect" : true,
25439         /**
25440              * @event rowselect
25441              * Fires when a row is selected.
25442              * @param {SelectionModel} this
25443              * @param {Number} rowIndex The selected index
25444              * @param {Roo.data.Record} r The record
25445              */
25446             "rowselect" : true,
25447         /**
25448              * @event rowdeselect
25449              * Fires when a row is deselected.
25450              * @param {SelectionModel} this
25451              * @param {Number} rowIndex The selected index
25452              */
25453         "rowdeselect" : true
25454     });
25455     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
25456     this.locked = false;
25457  };
25458
25459 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
25460     /**
25461      * @cfg {Boolean} singleSelect
25462      * True to allow selection of only one row at a time (defaults to false)
25463      */
25464     singleSelect : false,
25465
25466     // private
25467     initEvents : function()
25468     {
25469
25470         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
25471         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
25472         //}else{ // allow click to work like normal
25473          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
25474         //}
25475         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
25476         this.grid.on("rowclick", this.handleMouseDown, this);
25477         
25478         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
25479             "up" : function(e){
25480                 if(!e.shiftKey){
25481                     this.selectPrevious(e.shiftKey);
25482                 }else if(this.last !== false && this.lastActive !== false){
25483                     var last = this.last;
25484                     this.selectRange(this.last,  this.lastActive-1);
25485                     this.grid.getView().focusRow(this.lastActive);
25486                     if(last !== false){
25487                         this.last = last;
25488                     }
25489                 }else{
25490                     this.selectFirstRow();
25491                 }
25492                 this.fireEvent("afterselectionchange", this);
25493             },
25494             "down" : function(e){
25495                 if(!e.shiftKey){
25496                     this.selectNext(e.shiftKey);
25497                 }else if(this.last !== false && this.lastActive !== false){
25498                     var last = this.last;
25499                     this.selectRange(this.last,  this.lastActive+1);
25500                     this.grid.getView().focusRow(this.lastActive);
25501                     if(last !== false){
25502                         this.last = last;
25503                     }
25504                 }else{
25505                     this.selectFirstRow();
25506                 }
25507                 this.fireEvent("afterselectionchange", this);
25508             },
25509             scope: this
25510         });
25511         this.grid.store.on('load', function(){
25512             this.selections.clear();
25513         },this);
25514         /*
25515         var view = this.grid.view;
25516         view.on("refresh", this.onRefresh, this);
25517         view.on("rowupdated", this.onRowUpdated, this);
25518         view.on("rowremoved", this.onRemove, this);
25519         */
25520     },
25521
25522     // private
25523     onRefresh : function()
25524     {
25525         var ds = this.grid.store, i, v = this.grid.view;
25526         var s = this.selections;
25527         s.each(function(r){
25528             if((i = ds.indexOfId(r.id)) != -1){
25529                 v.onRowSelect(i);
25530             }else{
25531                 s.remove(r);
25532             }
25533         });
25534     },
25535
25536     // private
25537     onRemove : function(v, index, r){
25538         this.selections.remove(r);
25539     },
25540
25541     // private
25542     onRowUpdated : function(v, index, r){
25543         if(this.isSelected(r)){
25544             v.onRowSelect(index);
25545         }
25546     },
25547
25548     /**
25549      * Select records.
25550      * @param {Array} records The records to select
25551      * @param {Boolean} keepExisting (optional) True to keep existing selections
25552      */
25553     selectRecords : function(records, keepExisting)
25554     {
25555         if(!keepExisting){
25556             this.clearSelections();
25557         }
25558             var ds = this.grid.store;
25559         for(var i = 0, len = records.length; i < len; i++){
25560             this.selectRow(ds.indexOf(records[i]), true);
25561         }
25562     },
25563
25564     /**
25565      * Gets the number of selected rows.
25566      * @return {Number}
25567      */
25568     getCount : function(){
25569         return this.selections.length;
25570     },
25571
25572     /**
25573      * Selects the first row in the grid.
25574      */
25575     selectFirstRow : function(){
25576         this.selectRow(0);
25577     },
25578
25579     /**
25580      * Select the last row.
25581      * @param {Boolean} keepExisting (optional) True to keep existing selections
25582      */
25583     selectLastRow : function(keepExisting){
25584         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
25585         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
25586     },
25587
25588     /**
25589      * Selects the row immediately following the last selected row.
25590      * @param {Boolean} keepExisting (optional) True to keep existing selections
25591      */
25592     selectNext : function(keepExisting)
25593     {
25594             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
25595             this.selectRow(this.last+1, keepExisting);
25596             this.grid.getView().focusRow(this.last);
25597         }
25598     },
25599
25600     /**
25601      * Selects the row that precedes the last selected row.
25602      * @param {Boolean} keepExisting (optional) True to keep existing selections
25603      */
25604     selectPrevious : function(keepExisting){
25605         if(this.last){
25606             this.selectRow(this.last-1, keepExisting);
25607             this.grid.getView().focusRow(this.last);
25608         }
25609     },
25610
25611     /**
25612      * Returns the selected records
25613      * @return {Array} Array of selected records
25614      */
25615     getSelections : function(){
25616         return [].concat(this.selections.items);
25617     },
25618
25619     /**
25620      * Returns the first selected record.
25621      * @return {Record}
25622      */
25623     getSelected : function(){
25624         return this.selections.itemAt(0);
25625     },
25626
25627
25628     /**
25629      * Clears all selections.
25630      */
25631     clearSelections : function(fast)
25632     {
25633         if(this.locked) {
25634             return;
25635         }
25636         if(fast !== true){
25637                 var ds = this.grid.store;
25638             var s = this.selections;
25639             s.each(function(r){
25640                 this.deselectRow(ds.indexOfId(r.id));
25641             }, this);
25642             s.clear();
25643         }else{
25644             this.selections.clear();
25645         }
25646         this.last = false;
25647     },
25648
25649
25650     /**
25651      * Selects all rows.
25652      */
25653     selectAll : function(){
25654         if(this.locked) {
25655             return;
25656         }
25657         this.selections.clear();
25658         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
25659             this.selectRow(i, true);
25660         }
25661     },
25662
25663     /**
25664      * Returns True if there is a selection.
25665      * @return {Boolean}
25666      */
25667     hasSelection : function(){
25668         return this.selections.length > 0;
25669     },
25670
25671     /**
25672      * Returns True if the specified row is selected.
25673      * @param {Number/Record} record The record or index of the record to check
25674      * @return {Boolean}
25675      */
25676     isSelected : function(index){
25677             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
25678         return (r && this.selections.key(r.id) ? true : false);
25679     },
25680
25681     /**
25682      * Returns True if the specified record id is selected.
25683      * @param {String} id The id of record to check
25684      * @return {Boolean}
25685      */
25686     isIdSelected : function(id){
25687         return (this.selections.key(id) ? true : false);
25688     },
25689
25690
25691     // private
25692     handleMouseDBClick : function(e, t){
25693         
25694     },
25695     // private
25696     handleMouseDown : function(e, t)
25697     {
25698             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
25699         if(this.isLocked() || rowIndex < 0 ){
25700             return;
25701         };
25702         if(e.shiftKey && this.last !== false){
25703             var last = this.last;
25704             this.selectRange(last, rowIndex, e.ctrlKey);
25705             this.last = last; // reset the last
25706             t.focus();
25707     
25708         }else{
25709             var isSelected = this.isSelected(rowIndex);
25710             //Roo.log("select row:" + rowIndex);
25711             if(isSelected){
25712                 this.deselectRow(rowIndex);
25713             } else {
25714                         this.selectRow(rowIndex, true);
25715             }
25716     
25717             /*
25718                 if(e.button !== 0 && isSelected){
25719                 alert('rowIndex 2: ' + rowIndex);
25720                     view.focusRow(rowIndex);
25721                 }else if(e.ctrlKey && isSelected){
25722                     this.deselectRow(rowIndex);
25723                 }else if(!isSelected){
25724                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
25725                     view.focusRow(rowIndex);
25726                 }
25727             */
25728         }
25729         this.fireEvent("afterselectionchange", this);
25730     },
25731     // private
25732     handleDragableRowClick :  function(grid, rowIndex, e) 
25733     {
25734         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
25735             this.selectRow(rowIndex, false);
25736             grid.view.focusRow(rowIndex);
25737              this.fireEvent("afterselectionchange", this);
25738         }
25739     },
25740     
25741     /**
25742      * Selects multiple rows.
25743      * @param {Array} rows Array of the indexes of the row to select
25744      * @param {Boolean} keepExisting (optional) True to keep existing selections
25745      */
25746     selectRows : function(rows, keepExisting){
25747         if(!keepExisting){
25748             this.clearSelections();
25749         }
25750         for(var i = 0, len = rows.length; i < len; i++){
25751             this.selectRow(rows[i], true);
25752         }
25753     },
25754
25755     /**
25756      * Selects a range of rows. All rows in between startRow and endRow are also selected.
25757      * @param {Number} startRow The index of the first row in the range
25758      * @param {Number} endRow The index of the last row in the range
25759      * @param {Boolean} keepExisting (optional) True to retain existing selections
25760      */
25761     selectRange : function(startRow, endRow, keepExisting){
25762         if(this.locked) {
25763             return;
25764         }
25765         if(!keepExisting){
25766             this.clearSelections();
25767         }
25768         if(startRow <= endRow){
25769             for(var i = startRow; i <= endRow; i++){
25770                 this.selectRow(i, true);
25771             }
25772         }else{
25773             for(var i = startRow; i >= endRow; i--){
25774                 this.selectRow(i, true);
25775             }
25776         }
25777     },
25778
25779     /**
25780      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
25781      * @param {Number} startRow The index of the first row in the range
25782      * @param {Number} endRow The index of the last row in the range
25783      */
25784     deselectRange : function(startRow, endRow, preventViewNotify){
25785         if(this.locked) {
25786             return;
25787         }
25788         for(var i = startRow; i <= endRow; i++){
25789             this.deselectRow(i, preventViewNotify);
25790         }
25791     },
25792
25793     /**
25794      * Selects a row.
25795      * @param {Number} row The index of the row to select
25796      * @param {Boolean} keepExisting (optional) True to keep existing selections
25797      */
25798     selectRow : function(index, keepExisting, preventViewNotify)
25799     {
25800             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
25801             return;
25802         }
25803         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
25804             if(!keepExisting || this.singleSelect){
25805                 this.clearSelections();
25806             }
25807             
25808             var r = this.grid.store.getAt(index);
25809             //console.log('selectRow - record id :' + r.id);
25810             
25811             this.selections.add(r);
25812             this.last = this.lastActive = index;
25813             if(!preventViewNotify){
25814                 var proxy = new Roo.Element(
25815                                 this.grid.getRowDom(index)
25816                 );
25817                 proxy.addClass('bg-info info');
25818             }
25819             this.fireEvent("rowselect", this, index, r);
25820             this.fireEvent("selectionchange", this);
25821         }
25822     },
25823
25824     /**
25825      * Deselects a row.
25826      * @param {Number} row The index of the row to deselect
25827      */
25828     deselectRow : function(index, preventViewNotify)
25829     {
25830         if(this.locked) {
25831             return;
25832         }
25833         if(this.last == index){
25834             this.last = false;
25835         }
25836         if(this.lastActive == index){
25837             this.lastActive = false;
25838         }
25839         
25840         var r = this.grid.store.getAt(index);
25841         if (!r) {
25842             return;
25843         }
25844         
25845         this.selections.remove(r);
25846         //.console.log('deselectRow - record id :' + r.id);
25847         if(!preventViewNotify){
25848         
25849             var proxy = new Roo.Element(
25850                 this.grid.getRowDom(index)
25851             );
25852             proxy.removeClass('bg-info info');
25853         }
25854         this.fireEvent("rowdeselect", this, index);
25855         this.fireEvent("selectionchange", this);
25856     },
25857
25858     // private
25859     restoreLast : function(){
25860         if(this._last){
25861             this.last = this._last;
25862         }
25863     },
25864
25865     // private
25866     acceptsNav : function(row, col, cm){
25867         return !cm.isHidden(col) && cm.isCellEditable(col, row);
25868     },
25869
25870     // private
25871     onEditorKey : function(field, e){
25872         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
25873         if(k == e.TAB){
25874             e.stopEvent();
25875             ed.completeEdit();
25876             if(e.shiftKey){
25877                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
25878             }else{
25879                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
25880             }
25881         }else if(k == e.ENTER && !e.ctrlKey){
25882             e.stopEvent();
25883             ed.completeEdit();
25884             if(e.shiftKey){
25885                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
25886             }else{
25887                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
25888             }
25889         }else if(k == e.ESC){
25890             ed.cancelEdit();
25891         }
25892         if(newCell){
25893             g.startEditing(newCell[0], newCell[1]);
25894         }
25895     }
25896 });
25897 /*
25898  * Based on:
25899  * Ext JS Library 1.1.1
25900  * Copyright(c) 2006-2007, Ext JS, LLC.
25901  *
25902  * Originally Released Under LGPL - original licence link has changed is not relivant.
25903  *
25904  * Fork - LGPL
25905  * <script type="text/javascript">
25906  */
25907  
25908 /**
25909  * @class Roo.bootstrap.PagingToolbar
25910  * @extends Roo.bootstrap.NavSimplebar
25911  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
25912  * @constructor
25913  * Create a new PagingToolbar
25914  * @param {Object} config The config object
25915  * @param {Roo.data.Store} store
25916  */
25917 Roo.bootstrap.PagingToolbar = function(config)
25918 {
25919     // old args format still supported... - xtype is prefered..
25920         // created from xtype...
25921     
25922     this.ds = config.dataSource;
25923     
25924     if (config.store && !this.ds) {
25925         this.store= Roo.factory(config.store, Roo.data);
25926         this.ds = this.store;
25927         this.ds.xmodule = this.xmodule || false;
25928     }
25929     
25930     this.toolbarItems = [];
25931     if (config.items) {
25932         this.toolbarItems = config.items;
25933     }
25934     
25935     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
25936     
25937     this.cursor = 0;
25938     
25939     if (this.ds) { 
25940         this.bind(this.ds);
25941     }
25942     
25943     if (Roo.bootstrap.version == 4) {
25944         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
25945     } else {
25946         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
25947     }
25948     
25949 };
25950
25951 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
25952     /**
25953      * @cfg {Roo.data.Store} dataSource
25954      * The underlying data store providing the paged data
25955      */
25956     /**
25957      * @cfg {String/HTMLElement/Element} container
25958      * container The id or element that will contain the toolbar
25959      */
25960     /**
25961      * @cfg {Boolean} displayInfo
25962      * True to display the displayMsg (defaults to false)
25963      */
25964     /**
25965      * @cfg {Number} pageSize
25966      * The number of records to display per page (defaults to 20)
25967      */
25968     pageSize: 20,
25969     /**
25970      * @cfg {String} displayMsg
25971      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
25972      */
25973     displayMsg : 'Displaying {0} - {1} of {2}',
25974     /**
25975      * @cfg {String} emptyMsg
25976      * The message to display when no records are found (defaults to "No data to display")
25977      */
25978     emptyMsg : 'No data to display',
25979     /**
25980      * Customizable piece of the default paging text (defaults to "Page")
25981      * @type String
25982      */
25983     beforePageText : "Page",
25984     /**
25985      * Customizable piece of the default paging text (defaults to "of %0")
25986      * @type String
25987      */
25988     afterPageText : "of {0}",
25989     /**
25990      * Customizable piece of the default paging text (defaults to "First Page")
25991      * @type String
25992      */
25993     firstText : "First Page",
25994     /**
25995      * Customizable piece of the default paging text (defaults to "Previous Page")
25996      * @type String
25997      */
25998     prevText : "Previous Page",
25999     /**
26000      * Customizable piece of the default paging text (defaults to "Next Page")
26001      * @type String
26002      */
26003     nextText : "Next Page",
26004     /**
26005      * Customizable piece of the default paging text (defaults to "Last Page")
26006      * @type String
26007      */
26008     lastText : "Last Page",
26009     /**
26010      * Customizable piece of the default paging text (defaults to "Refresh")
26011      * @type String
26012      */
26013     refreshText : "Refresh",
26014
26015     buttons : false,
26016     // private
26017     onRender : function(ct, position) 
26018     {
26019         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
26020         this.navgroup.parentId = this.id;
26021         this.navgroup.onRender(this.el, null);
26022         // add the buttons to the navgroup
26023         
26024         if(this.displayInfo){
26025             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
26026             this.displayEl = this.el.select('.x-paging-info', true).first();
26027 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
26028 //            this.displayEl = navel.el.select('span',true).first();
26029         }
26030         
26031         var _this = this;
26032         
26033         if(this.buttons){
26034             Roo.each(_this.buttons, function(e){ // this might need to use render????
26035                Roo.factory(e).render(_this.el);
26036             });
26037         }
26038             
26039         Roo.each(_this.toolbarItems, function(e) {
26040             _this.navgroup.addItem(e);
26041         });
26042         
26043         
26044         this.first = this.navgroup.addItem({
26045             tooltip: this.firstText,
26046             cls: "prev btn-outline-secondary",
26047             html : ' <i class="fa fa-step-backward"></i>',
26048             disabled: true,
26049             preventDefault: true,
26050             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
26051         });
26052         
26053         this.prev =  this.navgroup.addItem({
26054             tooltip: this.prevText,
26055             cls: "prev btn-outline-secondary",
26056             html : ' <i class="fa fa-backward"></i>',
26057             disabled: true,
26058             preventDefault: true,
26059             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
26060         });
26061     //this.addSeparator();
26062         
26063         
26064         var field = this.navgroup.addItem( {
26065             tagtype : 'span',
26066             cls : 'x-paging-position  btn-outline-secondary',
26067              disabled: true,
26068             html : this.beforePageText  +
26069                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
26070                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
26071          } ); //?? escaped?
26072         
26073         this.field = field.el.select('input', true).first();
26074         this.field.on("keydown", this.onPagingKeydown, this);
26075         this.field.on("focus", function(){this.dom.select();});
26076     
26077     
26078         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
26079         //this.field.setHeight(18);
26080         //this.addSeparator();
26081         this.next = this.navgroup.addItem({
26082             tooltip: this.nextText,
26083             cls: "next btn-outline-secondary",
26084             html : ' <i class="fa fa-forward"></i>',
26085             disabled: true,
26086             preventDefault: true,
26087             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
26088         });
26089         this.last = this.navgroup.addItem({
26090             tooltip: this.lastText,
26091             html : ' <i class="fa fa-step-forward"></i>',
26092             cls: "next btn-outline-secondary",
26093             disabled: true,
26094             preventDefault: true,
26095             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
26096         });
26097     //this.addSeparator();
26098         this.loading = this.navgroup.addItem({
26099             tooltip: this.refreshText,
26100             cls: "btn-outline-secondary",
26101             html : ' <i class="fa fa-refresh"></i>',
26102             preventDefault: true,
26103             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
26104         });
26105         
26106     },
26107
26108     // private
26109     updateInfo : function(){
26110         if(this.displayEl){
26111             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
26112             var msg = count == 0 ?
26113                 this.emptyMsg :
26114                 String.format(
26115                     this.displayMsg,
26116                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
26117                 );
26118             this.displayEl.update(msg);
26119         }
26120     },
26121
26122     // private
26123     onLoad : function(ds, r, o)
26124     {
26125         this.cursor = o.params.start ? o.params.start : 0;
26126         
26127         var d = this.getPageData(),
26128             ap = d.activePage,
26129             ps = d.pages;
26130         
26131         
26132         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
26133         this.field.dom.value = ap;
26134         this.first.setDisabled(ap == 1);
26135         this.prev.setDisabled(ap == 1);
26136         this.next.setDisabled(ap == ps);
26137         this.last.setDisabled(ap == ps);
26138         this.loading.enable();
26139         this.updateInfo();
26140     },
26141
26142     // private
26143     getPageData : function(){
26144         var total = this.ds.getTotalCount();
26145         return {
26146             total : total,
26147             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
26148             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
26149         };
26150     },
26151
26152     // private
26153     onLoadError : function(){
26154         this.loading.enable();
26155     },
26156
26157     // private
26158     onPagingKeydown : function(e){
26159         var k = e.getKey();
26160         var d = this.getPageData();
26161         if(k == e.RETURN){
26162             var v = this.field.dom.value, pageNum;
26163             if(!v || isNaN(pageNum = parseInt(v, 10))){
26164                 this.field.dom.value = d.activePage;
26165                 return;
26166             }
26167             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
26168             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
26169             e.stopEvent();
26170         }
26171         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))
26172         {
26173           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
26174           this.field.dom.value = pageNum;
26175           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
26176           e.stopEvent();
26177         }
26178         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
26179         {
26180           var v = this.field.dom.value, pageNum; 
26181           var increment = (e.shiftKey) ? 10 : 1;
26182           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
26183                 increment *= -1;
26184           }
26185           if(!v || isNaN(pageNum = parseInt(v, 10))) {
26186             this.field.dom.value = d.activePage;
26187             return;
26188           }
26189           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
26190           {
26191             this.field.dom.value = parseInt(v, 10) + increment;
26192             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
26193             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
26194           }
26195           e.stopEvent();
26196         }
26197     },
26198
26199     // private
26200     beforeLoad : function(){
26201         if(this.loading){
26202             this.loading.disable();
26203         }
26204     },
26205
26206     // private
26207     onClick : function(which){
26208         
26209         var ds = this.ds;
26210         if (!ds) {
26211             return;
26212         }
26213         
26214         switch(which){
26215             case "first":
26216                 ds.load({params:{start: 0, limit: this.pageSize}});
26217             break;
26218             case "prev":
26219                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
26220             break;
26221             case "next":
26222                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
26223             break;
26224             case "last":
26225                 var total = ds.getTotalCount();
26226                 var extra = total % this.pageSize;
26227                 var lastStart = extra ? (total - extra) : total-this.pageSize;
26228                 ds.load({params:{start: lastStart, limit: this.pageSize}});
26229             break;
26230             case "refresh":
26231                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
26232             break;
26233         }
26234     },
26235
26236     /**
26237      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
26238      * @param {Roo.data.Store} store The data store to unbind
26239      */
26240     unbind : function(ds){
26241         ds.un("beforeload", this.beforeLoad, this);
26242         ds.un("load", this.onLoad, this);
26243         ds.un("loadexception", this.onLoadError, this);
26244         ds.un("remove", this.updateInfo, this);
26245         ds.un("add", this.updateInfo, this);
26246         this.ds = undefined;
26247     },
26248
26249     /**
26250      * Binds the paging toolbar to the specified {@link Roo.data.Store}
26251      * @param {Roo.data.Store} store The data store to bind
26252      */
26253     bind : function(ds){
26254         ds.on("beforeload", this.beforeLoad, this);
26255         ds.on("load", this.onLoad, this);
26256         ds.on("loadexception", this.onLoadError, this);
26257         ds.on("remove", this.updateInfo, this);
26258         ds.on("add", this.updateInfo, this);
26259         this.ds = ds;
26260     }
26261 });/*
26262  * - LGPL
26263  *
26264  * element
26265  * 
26266  */
26267
26268 /**
26269  * @class Roo.bootstrap.MessageBar
26270  * @extends Roo.bootstrap.Component
26271  * Bootstrap MessageBar class
26272  * @cfg {String} html contents of the MessageBar
26273  * @cfg {String} weight (info | success | warning | danger) default info
26274  * @cfg {String} beforeClass insert the bar before the given class
26275  * @cfg {Boolean} closable (true | false) default false
26276  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
26277  * 
26278  * @constructor
26279  * Create a new Element
26280  * @param {Object} config The config object
26281  */
26282
26283 Roo.bootstrap.MessageBar = function(config){
26284     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
26285 };
26286
26287 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
26288     
26289     html: '',
26290     weight: 'info',
26291     closable: false,
26292     fixed: false,
26293     beforeClass: 'bootstrap-sticky-wrap',
26294     
26295     getAutoCreate : function(){
26296         
26297         var cfg = {
26298             tag: 'div',
26299             cls: 'alert alert-dismissable alert-' + this.weight,
26300             cn: [
26301                 {
26302                     tag: 'span',
26303                     cls: 'message',
26304                     html: this.html || ''
26305                 }
26306             ]
26307         };
26308         
26309         if(this.fixed){
26310             cfg.cls += ' alert-messages-fixed';
26311         }
26312         
26313         if(this.closable){
26314             cfg.cn.push({
26315                 tag: 'button',
26316                 cls: 'close',
26317                 html: 'x'
26318             });
26319         }
26320         
26321         return cfg;
26322     },
26323     
26324     onRender : function(ct, position)
26325     {
26326         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
26327         
26328         if(!this.el){
26329             var cfg = Roo.apply({},  this.getAutoCreate());
26330             cfg.id = Roo.id();
26331             
26332             if (this.cls) {
26333                 cfg.cls += ' ' + this.cls;
26334             }
26335             if (this.style) {
26336                 cfg.style = this.style;
26337             }
26338             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
26339             
26340             this.el.setVisibilityMode(Roo.Element.DISPLAY);
26341         }
26342         
26343         this.el.select('>button.close').on('click', this.hide, this);
26344         
26345     },
26346     
26347     show : function()
26348     {
26349         if (!this.rendered) {
26350             this.render();
26351         }
26352         
26353         this.el.show();
26354         
26355         this.fireEvent('show', this);
26356         
26357     },
26358     
26359     hide : function()
26360     {
26361         if (!this.rendered) {
26362             this.render();
26363         }
26364         
26365         this.el.hide();
26366         
26367         this.fireEvent('hide', this);
26368     },
26369     
26370     update : function()
26371     {
26372 //        var e = this.el.dom.firstChild;
26373 //        
26374 //        if(this.closable){
26375 //            e = e.nextSibling;
26376 //        }
26377 //        
26378 //        e.data = this.html || '';
26379
26380         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
26381     }
26382    
26383 });
26384
26385  
26386
26387      /*
26388  * - LGPL
26389  *
26390  * Graph
26391  * 
26392  */
26393
26394
26395 /**
26396  * @class Roo.bootstrap.Graph
26397  * @extends Roo.bootstrap.Component
26398  * Bootstrap Graph class
26399 > Prameters
26400  -sm {number} sm 4
26401  -md {number} md 5
26402  @cfg {String} graphtype  bar | vbar | pie
26403  @cfg {number} g_x coodinator | centre x (pie)
26404  @cfg {number} g_y coodinator | centre y (pie)
26405  @cfg {number} g_r radius (pie)
26406  @cfg {number} g_height height of the chart (respected by all elements in the set)
26407  @cfg {number} g_width width of the chart (respected by all elements in the set)
26408  @cfg {Object} title The title of the chart
26409     
26410  -{Array}  values
26411  -opts (object) options for the chart 
26412      o {
26413      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
26414      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
26415      o vgutter (number)
26416      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.
26417      o stacked (boolean) whether or not to tread values as in a stacked bar chart
26418      o to
26419      o stretch (boolean)
26420      o }
26421  -opts (object) options for the pie
26422      o{
26423      o cut
26424      o startAngle (number)
26425      o endAngle (number)
26426      } 
26427  *
26428  * @constructor
26429  * Create a new Input
26430  * @param {Object} config The config object
26431  */
26432
26433 Roo.bootstrap.Graph = function(config){
26434     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
26435     
26436     this.addEvents({
26437         // img events
26438         /**
26439          * @event click
26440          * The img click event for the img.
26441          * @param {Roo.EventObject} e
26442          */
26443         "click" : true
26444     });
26445 };
26446
26447 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
26448     
26449     sm: 4,
26450     md: 5,
26451     graphtype: 'bar',
26452     g_height: 250,
26453     g_width: 400,
26454     g_x: 50,
26455     g_y: 50,
26456     g_r: 30,
26457     opts:{
26458         //g_colors: this.colors,
26459         g_type: 'soft',
26460         g_gutter: '20%'
26461
26462     },
26463     title : false,
26464
26465     getAutoCreate : function(){
26466         
26467         var cfg = {
26468             tag: 'div',
26469             html : null
26470         };
26471         
26472         
26473         return  cfg;
26474     },
26475
26476     onRender : function(ct,position){
26477         
26478         
26479         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
26480         
26481         if (typeof(Raphael) == 'undefined') {
26482             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
26483             return;
26484         }
26485         
26486         this.raphael = Raphael(this.el.dom);
26487         
26488                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
26489                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
26490                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
26491                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
26492                 /*
26493                 r.text(160, 10, "Single Series Chart").attr(txtattr);
26494                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
26495                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
26496                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
26497                 
26498                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
26499                 r.barchart(330, 10, 300, 220, data1);
26500                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
26501                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
26502                 */
26503                 
26504                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
26505                 // r.barchart(30, 30, 560, 250,  xdata, {
26506                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
26507                 //     axis : "0 0 1 1",
26508                 //     axisxlabels :  xdata
26509                 //     //yvalues : cols,
26510                    
26511                 // });
26512 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
26513 //        
26514 //        this.load(null,xdata,{
26515 //                axis : "0 0 1 1",
26516 //                axisxlabels :  xdata
26517 //                });
26518
26519     },
26520
26521     load : function(graphtype,xdata,opts)
26522     {
26523         this.raphael.clear();
26524         if(!graphtype) {
26525             graphtype = this.graphtype;
26526         }
26527         if(!opts){
26528             opts = this.opts;
26529         }
26530         var r = this.raphael,
26531             fin = function () {
26532                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
26533             },
26534             fout = function () {
26535                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
26536             },
26537             pfin = function() {
26538                 this.sector.stop();
26539                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
26540
26541                 if (this.label) {
26542                     this.label[0].stop();
26543                     this.label[0].attr({ r: 7.5 });
26544                     this.label[1].attr({ "font-weight": 800 });
26545                 }
26546             },
26547             pfout = function() {
26548                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
26549
26550                 if (this.label) {
26551                     this.label[0].animate({ r: 5 }, 500, "bounce");
26552                     this.label[1].attr({ "font-weight": 400 });
26553                 }
26554             };
26555
26556         switch(graphtype){
26557             case 'bar':
26558                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
26559                 break;
26560             case 'hbar':
26561                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
26562                 break;
26563             case 'pie':
26564 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
26565 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
26566 //            
26567                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
26568                 
26569                 break;
26570
26571         }
26572         
26573         if(this.title){
26574             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
26575         }
26576         
26577     },
26578     
26579     setTitle: function(o)
26580     {
26581         this.title = o;
26582     },
26583     
26584     initEvents: function() {
26585         
26586         if(!this.href){
26587             this.el.on('click', this.onClick, this);
26588         }
26589     },
26590     
26591     onClick : function(e)
26592     {
26593         Roo.log('img onclick');
26594         this.fireEvent('click', this, e);
26595     }
26596    
26597 });
26598
26599  
26600 /*
26601  * - LGPL
26602  *
26603  * numberBox
26604  * 
26605  */
26606 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
26607
26608 /**
26609  * @class Roo.bootstrap.dash.NumberBox
26610  * @extends Roo.bootstrap.Component
26611  * Bootstrap NumberBox class
26612  * @cfg {String} headline Box headline
26613  * @cfg {String} content Box content
26614  * @cfg {String} icon Box icon
26615  * @cfg {String} footer Footer text
26616  * @cfg {String} fhref Footer href
26617  * 
26618  * @constructor
26619  * Create a new NumberBox
26620  * @param {Object} config The config object
26621  */
26622
26623
26624 Roo.bootstrap.dash.NumberBox = function(config){
26625     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
26626     
26627 };
26628
26629 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
26630     
26631     headline : '',
26632     content : '',
26633     icon : '',
26634     footer : '',
26635     fhref : '',
26636     ficon : '',
26637     
26638     getAutoCreate : function(){
26639         
26640         var cfg = {
26641             tag : 'div',
26642             cls : 'small-box ',
26643             cn : [
26644                 {
26645                     tag : 'div',
26646                     cls : 'inner',
26647                     cn :[
26648                         {
26649                             tag : 'h3',
26650                             cls : 'roo-headline',
26651                             html : this.headline
26652                         },
26653                         {
26654                             tag : 'p',
26655                             cls : 'roo-content',
26656                             html : this.content
26657                         }
26658                     ]
26659                 }
26660             ]
26661         };
26662         
26663         if(this.icon){
26664             cfg.cn.push({
26665                 tag : 'div',
26666                 cls : 'icon',
26667                 cn :[
26668                     {
26669                         tag : 'i',
26670                         cls : 'ion ' + this.icon
26671                     }
26672                 ]
26673             });
26674         }
26675         
26676         if(this.footer){
26677             var footer = {
26678                 tag : 'a',
26679                 cls : 'small-box-footer',
26680                 href : this.fhref || '#',
26681                 html : this.footer
26682             };
26683             
26684             cfg.cn.push(footer);
26685             
26686         }
26687         
26688         return  cfg;
26689     },
26690
26691     onRender : function(ct,position){
26692         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
26693
26694
26695        
26696                 
26697     },
26698
26699     setHeadline: function (value)
26700     {
26701         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
26702     },
26703     
26704     setFooter: function (value, href)
26705     {
26706         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
26707         
26708         if(href){
26709             this.el.select('a.small-box-footer',true).first().attr('href', href);
26710         }
26711         
26712     },
26713
26714     setContent: function (value)
26715     {
26716         this.el.select('.roo-content',true).first().dom.innerHTML = value;
26717     },
26718
26719     initEvents: function() 
26720     {   
26721         
26722     }
26723     
26724 });
26725
26726  
26727 /*
26728  * - LGPL
26729  *
26730  * TabBox
26731  * 
26732  */
26733 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
26734
26735 /**
26736  * @class Roo.bootstrap.dash.TabBox
26737  * @extends Roo.bootstrap.Component
26738  * Bootstrap TabBox class
26739  * @cfg {String} title Title of the TabBox
26740  * @cfg {String} icon Icon of the TabBox
26741  * @cfg {Boolean} showtabs (true|false) show the tabs default true
26742  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
26743  * 
26744  * @constructor
26745  * Create a new TabBox
26746  * @param {Object} config The config object
26747  */
26748
26749
26750 Roo.bootstrap.dash.TabBox = function(config){
26751     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
26752     this.addEvents({
26753         // raw events
26754         /**
26755          * @event addpane
26756          * When a pane is added
26757          * @param {Roo.bootstrap.dash.TabPane} pane
26758          */
26759         "addpane" : true,
26760         /**
26761          * @event activatepane
26762          * When a pane is activated
26763          * @param {Roo.bootstrap.dash.TabPane} pane
26764          */
26765         "activatepane" : true
26766         
26767          
26768     });
26769     
26770     this.panes = [];
26771 };
26772
26773 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
26774
26775     title : '',
26776     icon : false,
26777     showtabs : true,
26778     tabScrollable : false,
26779     
26780     getChildContainer : function()
26781     {
26782         return this.el.select('.tab-content', true).first();
26783     },
26784     
26785     getAutoCreate : function(){
26786         
26787         var header = {
26788             tag: 'li',
26789             cls: 'pull-left header',
26790             html: this.title,
26791             cn : []
26792         };
26793         
26794         if(this.icon){
26795             header.cn.push({
26796                 tag: 'i',
26797                 cls: 'fa ' + this.icon
26798             });
26799         }
26800         
26801         var h = {
26802             tag: 'ul',
26803             cls: 'nav nav-tabs pull-right',
26804             cn: [
26805                 header
26806             ]
26807         };
26808         
26809         if(this.tabScrollable){
26810             h = {
26811                 tag: 'div',
26812                 cls: 'tab-header',
26813                 cn: [
26814                     {
26815                         tag: 'ul',
26816                         cls: 'nav nav-tabs pull-right',
26817                         cn: [
26818                             header
26819                         ]
26820                     }
26821                 ]
26822             };
26823         }
26824         
26825         var cfg = {
26826             tag: 'div',
26827             cls: 'nav-tabs-custom',
26828             cn: [
26829                 h,
26830                 {
26831                     tag: 'div',
26832                     cls: 'tab-content no-padding',
26833                     cn: []
26834                 }
26835             ]
26836         };
26837
26838         return  cfg;
26839     },
26840     initEvents : function()
26841     {
26842         //Roo.log('add add pane handler');
26843         this.on('addpane', this.onAddPane, this);
26844     },
26845      /**
26846      * Updates the box title
26847      * @param {String} html to set the title to.
26848      */
26849     setTitle : function(value)
26850     {
26851         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
26852     },
26853     onAddPane : function(pane)
26854     {
26855         this.panes.push(pane);
26856         //Roo.log('addpane');
26857         //Roo.log(pane);
26858         // tabs are rendere left to right..
26859         if(!this.showtabs){
26860             return;
26861         }
26862         
26863         var ctr = this.el.select('.nav-tabs', true).first();
26864          
26865          
26866         var existing = ctr.select('.nav-tab',true);
26867         var qty = existing.getCount();;
26868         
26869         
26870         var tab = ctr.createChild({
26871             tag : 'li',
26872             cls : 'nav-tab' + (qty ? '' : ' active'),
26873             cn : [
26874                 {
26875                     tag : 'a',
26876                     href:'#',
26877                     html : pane.title
26878                 }
26879             ]
26880         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
26881         pane.tab = tab;
26882         
26883         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
26884         if (!qty) {
26885             pane.el.addClass('active');
26886         }
26887         
26888                 
26889     },
26890     onTabClick : function(ev,un,ob,pane)
26891     {
26892         //Roo.log('tab - prev default');
26893         ev.preventDefault();
26894         
26895         
26896         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
26897         pane.tab.addClass('active');
26898         //Roo.log(pane.title);
26899         this.getChildContainer().select('.tab-pane',true).removeClass('active');
26900         // technically we should have a deactivate event.. but maybe add later.
26901         // and it should not de-activate the selected tab...
26902         this.fireEvent('activatepane', pane);
26903         pane.el.addClass('active');
26904         pane.fireEvent('activate');
26905         
26906         
26907     },
26908     
26909     getActivePane : function()
26910     {
26911         var r = false;
26912         Roo.each(this.panes, function(p) {
26913             if(p.el.hasClass('active')){
26914                 r = p;
26915                 return false;
26916             }
26917             
26918             return;
26919         });
26920         
26921         return r;
26922     }
26923     
26924     
26925 });
26926
26927  
26928 /*
26929  * - LGPL
26930  *
26931  * Tab pane
26932  * 
26933  */
26934 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
26935 /**
26936  * @class Roo.bootstrap.TabPane
26937  * @extends Roo.bootstrap.Component
26938  * Bootstrap TabPane class
26939  * @cfg {Boolean} active (false | true) Default false
26940  * @cfg {String} title title of panel
26941
26942  * 
26943  * @constructor
26944  * Create a new TabPane
26945  * @param {Object} config The config object
26946  */
26947
26948 Roo.bootstrap.dash.TabPane = function(config){
26949     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
26950     
26951     this.addEvents({
26952         // raw events
26953         /**
26954          * @event activate
26955          * When a pane is activated
26956          * @param {Roo.bootstrap.dash.TabPane} pane
26957          */
26958         "activate" : true
26959          
26960     });
26961 };
26962
26963 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
26964     
26965     active : false,
26966     title : '',
26967     
26968     // the tabBox that this is attached to.
26969     tab : false,
26970      
26971     getAutoCreate : function() 
26972     {
26973         var cfg = {
26974             tag: 'div',
26975             cls: 'tab-pane'
26976         };
26977         
26978         if(this.active){
26979             cfg.cls += ' active';
26980         }
26981         
26982         return cfg;
26983     },
26984     initEvents  : function()
26985     {
26986         //Roo.log('trigger add pane handler');
26987         this.parent().fireEvent('addpane', this)
26988     },
26989     
26990      /**
26991      * Updates the tab title 
26992      * @param {String} html to set the title to.
26993      */
26994     setTitle: function(str)
26995     {
26996         if (!this.tab) {
26997             return;
26998         }
26999         this.title = str;
27000         this.tab.select('a', true).first().dom.innerHTML = str;
27001         
27002     }
27003     
27004     
27005     
27006 });
27007
27008  
27009
27010
27011  /*
27012  * - LGPL
27013  *
27014  * menu
27015  * 
27016  */
27017 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
27018
27019 /**
27020  * @class Roo.bootstrap.menu.Menu
27021  * @extends Roo.bootstrap.Component
27022  * Bootstrap Menu class - container for Menu
27023  * @cfg {String} html Text of the menu
27024  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
27025  * @cfg {String} icon Font awesome icon
27026  * @cfg {String} pos Menu align to (top | bottom) default bottom
27027  * 
27028  * 
27029  * @constructor
27030  * Create a new Menu
27031  * @param {Object} config The config object
27032  */
27033
27034
27035 Roo.bootstrap.menu.Menu = function(config){
27036     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
27037     
27038     this.addEvents({
27039         /**
27040          * @event beforeshow
27041          * Fires before this menu is displayed
27042          * @param {Roo.bootstrap.menu.Menu} this
27043          */
27044         beforeshow : true,
27045         /**
27046          * @event beforehide
27047          * Fires before this menu is hidden
27048          * @param {Roo.bootstrap.menu.Menu} this
27049          */
27050         beforehide : true,
27051         /**
27052          * @event show
27053          * Fires after this menu is displayed
27054          * @param {Roo.bootstrap.menu.Menu} this
27055          */
27056         show : true,
27057         /**
27058          * @event hide
27059          * Fires after this menu is hidden
27060          * @param {Roo.bootstrap.menu.Menu} this
27061          */
27062         hide : true,
27063         /**
27064          * @event click
27065          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
27066          * @param {Roo.bootstrap.menu.Menu} this
27067          * @param {Roo.EventObject} e
27068          */
27069         click : true
27070     });
27071     
27072 };
27073
27074 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
27075     
27076     submenu : false,
27077     html : '',
27078     weight : 'default',
27079     icon : false,
27080     pos : 'bottom',
27081     
27082     
27083     getChildContainer : function() {
27084         if(this.isSubMenu){
27085             return this.el;
27086         }
27087         
27088         return this.el.select('ul.dropdown-menu', true).first();  
27089     },
27090     
27091     getAutoCreate : function()
27092     {
27093         var text = [
27094             {
27095                 tag : 'span',
27096                 cls : 'roo-menu-text',
27097                 html : this.html
27098             }
27099         ];
27100         
27101         if(this.icon){
27102             text.unshift({
27103                 tag : 'i',
27104                 cls : 'fa ' + this.icon
27105             })
27106         }
27107         
27108         
27109         var cfg = {
27110             tag : 'div',
27111             cls : 'btn-group',
27112             cn : [
27113                 {
27114                     tag : 'button',
27115                     cls : 'dropdown-button btn btn-' + this.weight,
27116                     cn : text
27117                 },
27118                 {
27119                     tag : 'button',
27120                     cls : 'dropdown-toggle btn btn-' + this.weight,
27121                     cn : [
27122                         {
27123                             tag : 'span',
27124                             cls : 'caret'
27125                         }
27126                     ]
27127                 },
27128                 {
27129                     tag : 'ul',
27130                     cls : 'dropdown-menu'
27131                 }
27132             ]
27133             
27134         };
27135         
27136         if(this.pos == 'top'){
27137             cfg.cls += ' dropup';
27138         }
27139         
27140         if(this.isSubMenu){
27141             cfg = {
27142                 tag : 'ul',
27143                 cls : 'dropdown-menu'
27144             }
27145         }
27146         
27147         return cfg;
27148     },
27149     
27150     onRender : function(ct, position)
27151     {
27152         this.isSubMenu = ct.hasClass('dropdown-submenu');
27153         
27154         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
27155     },
27156     
27157     initEvents : function() 
27158     {
27159         if(this.isSubMenu){
27160             return;
27161         }
27162         
27163         this.hidden = true;
27164         
27165         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
27166         this.triggerEl.on('click', this.onTriggerPress, this);
27167         
27168         this.buttonEl = this.el.select('button.dropdown-button', true).first();
27169         this.buttonEl.on('click', this.onClick, this);
27170         
27171     },
27172     
27173     list : function()
27174     {
27175         if(this.isSubMenu){
27176             return this.el;
27177         }
27178         
27179         return this.el.select('ul.dropdown-menu', true).first();
27180     },
27181     
27182     onClick : function(e)
27183     {
27184         this.fireEvent("click", this, e);
27185     },
27186     
27187     onTriggerPress  : function(e)
27188     {   
27189         if (this.isVisible()) {
27190             this.hide();
27191         } else {
27192             this.show();
27193         }
27194     },
27195     
27196     isVisible : function(){
27197         return !this.hidden;
27198     },
27199     
27200     show : function()
27201     {
27202         this.fireEvent("beforeshow", this);
27203         
27204         this.hidden = false;
27205         this.el.addClass('open');
27206         
27207         Roo.get(document).on("mouseup", this.onMouseUp, this);
27208         
27209         this.fireEvent("show", this);
27210         
27211         
27212     },
27213     
27214     hide : function()
27215     {
27216         this.fireEvent("beforehide", this);
27217         
27218         this.hidden = true;
27219         this.el.removeClass('open');
27220         
27221         Roo.get(document).un("mouseup", this.onMouseUp);
27222         
27223         this.fireEvent("hide", this);
27224     },
27225     
27226     onMouseUp : function()
27227     {
27228         this.hide();
27229     }
27230     
27231 });
27232
27233  
27234  /*
27235  * - LGPL
27236  *
27237  * menu item
27238  * 
27239  */
27240 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
27241
27242 /**
27243  * @class Roo.bootstrap.menu.Item
27244  * @extends Roo.bootstrap.Component
27245  * Bootstrap MenuItem class
27246  * @cfg {Boolean} submenu (true | false) default false
27247  * @cfg {String} html text of the item
27248  * @cfg {String} href the link
27249  * @cfg {Boolean} disable (true | false) default false
27250  * @cfg {Boolean} preventDefault (true | false) default true
27251  * @cfg {String} icon Font awesome icon
27252  * @cfg {String} pos Submenu align to (left | right) default right 
27253  * 
27254  * 
27255  * @constructor
27256  * Create a new Item
27257  * @param {Object} config The config object
27258  */
27259
27260
27261 Roo.bootstrap.menu.Item = function(config){
27262     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
27263     this.addEvents({
27264         /**
27265          * @event mouseover
27266          * Fires when the mouse is hovering over this menu
27267          * @param {Roo.bootstrap.menu.Item} this
27268          * @param {Roo.EventObject} e
27269          */
27270         mouseover : true,
27271         /**
27272          * @event mouseout
27273          * Fires when the mouse exits this menu
27274          * @param {Roo.bootstrap.menu.Item} this
27275          * @param {Roo.EventObject} e
27276          */
27277         mouseout : true,
27278         // raw events
27279         /**
27280          * @event click
27281          * The raw click event for the entire grid.
27282          * @param {Roo.EventObject} e
27283          */
27284         click : true
27285     });
27286 };
27287
27288 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
27289     
27290     submenu : false,
27291     href : '',
27292     html : '',
27293     preventDefault: true,
27294     disable : false,
27295     icon : false,
27296     pos : 'right',
27297     
27298     getAutoCreate : function()
27299     {
27300         var text = [
27301             {
27302                 tag : 'span',
27303                 cls : 'roo-menu-item-text',
27304                 html : this.html
27305             }
27306         ];
27307         
27308         if(this.icon){
27309             text.unshift({
27310                 tag : 'i',
27311                 cls : 'fa ' + this.icon
27312             })
27313         }
27314         
27315         var cfg = {
27316             tag : 'li',
27317             cn : [
27318                 {
27319                     tag : 'a',
27320                     href : this.href || '#',
27321                     cn : text
27322                 }
27323             ]
27324         };
27325         
27326         if(this.disable){
27327             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
27328         }
27329         
27330         if(this.submenu){
27331             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
27332             
27333             if(this.pos == 'left'){
27334                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
27335             }
27336         }
27337         
27338         return cfg;
27339     },
27340     
27341     initEvents : function() 
27342     {
27343         this.el.on('mouseover', this.onMouseOver, this);
27344         this.el.on('mouseout', this.onMouseOut, this);
27345         
27346         this.el.select('a', true).first().on('click', this.onClick, this);
27347         
27348     },
27349     
27350     onClick : function(e)
27351     {
27352         if(this.preventDefault){
27353             e.preventDefault();
27354         }
27355         
27356         this.fireEvent("click", this, e);
27357     },
27358     
27359     onMouseOver : function(e)
27360     {
27361         if(this.submenu && this.pos == 'left'){
27362             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
27363         }
27364         
27365         this.fireEvent("mouseover", this, e);
27366     },
27367     
27368     onMouseOut : function(e)
27369     {
27370         this.fireEvent("mouseout", this, e);
27371     }
27372 });
27373
27374  
27375
27376  /*
27377  * - LGPL
27378  *
27379  * menu separator
27380  * 
27381  */
27382 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
27383
27384 /**
27385  * @class Roo.bootstrap.menu.Separator
27386  * @extends Roo.bootstrap.Component
27387  * Bootstrap Separator class
27388  * 
27389  * @constructor
27390  * Create a new Separator
27391  * @param {Object} config The config object
27392  */
27393
27394
27395 Roo.bootstrap.menu.Separator = function(config){
27396     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
27397 };
27398
27399 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
27400     
27401     getAutoCreate : function(){
27402         var cfg = {
27403             tag : 'li',
27404             cls: 'divider'
27405         };
27406         
27407         return cfg;
27408     }
27409    
27410 });
27411
27412  
27413
27414  /*
27415  * - LGPL
27416  *
27417  * Tooltip
27418  * 
27419  */
27420
27421 /**
27422  * @class Roo.bootstrap.Tooltip
27423  * Bootstrap Tooltip class
27424  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
27425  * to determine which dom element triggers the tooltip.
27426  * 
27427  * It needs to add support for additional attributes like tooltip-position
27428  * 
27429  * @constructor
27430  * Create a new Toolti
27431  * @param {Object} config The config object
27432  */
27433
27434 Roo.bootstrap.Tooltip = function(config){
27435     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
27436     
27437     this.alignment = Roo.bootstrap.Tooltip.alignment;
27438     
27439     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
27440         this.alignment = config.alignment;
27441     }
27442     
27443 };
27444
27445 Roo.apply(Roo.bootstrap.Tooltip, {
27446     /**
27447      * @function init initialize tooltip monitoring.
27448      * @static
27449      */
27450     currentEl : false,
27451     currentTip : false,
27452     currentRegion : false,
27453     
27454     //  init : delay?
27455     
27456     init : function()
27457     {
27458         Roo.get(document).on('mouseover', this.enter ,this);
27459         Roo.get(document).on('mouseout', this.leave, this);
27460          
27461         
27462         this.currentTip = new Roo.bootstrap.Tooltip();
27463     },
27464     
27465     enter : function(ev)
27466     {
27467         var dom = ev.getTarget();
27468         
27469         //Roo.log(['enter',dom]);
27470         var el = Roo.fly(dom);
27471         if (this.currentEl) {
27472             //Roo.log(dom);
27473             //Roo.log(this.currentEl);
27474             //Roo.log(this.currentEl.contains(dom));
27475             if (this.currentEl == el) {
27476                 return;
27477             }
27478             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
27479                 return;
27480             }
27481
27482         }
27483         
27484         if (this.currentTip.el) {
27485             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
27486         }    
27487         //Roo.log(ev);
27488         
27489         if(!el || el.dom == document){
27490             return;
27491         }
27492         
27493         var bindEl = el;
27494         
27495         // you can not look for children, as if el is the body.. then everythign is the child..
27496         if (!el.attr('tooltip')) { //
27497             if (!el.select("[tooltip]").elements.length) {
27498                 return;
27499             }
27500             // is the mouse over this child...?
27501             bindEl = el.select("[tooltip]").first();
27502             var xy = ev.getXY();
27503             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
27504                 //Roo.log("not in region.");
27505                 return;
27506             }
27507             //Roo.log("child element over..");
27508             
27509         }
27510         this.currentEl = bindEl;
27511         this.currentTip.bind(bindEl);
27512         this.currentRegion = Roo.lib.Region.getRegion(dom);
27513         this.currentTip.enter();
27514         
27515     },
27516     leave : function(ev)
27517     {
27518         var dom = ev.getTarget();
27519         //Roo.log(['leave',dom]);
27520         if (!this.currentEl) {
27521             return;
27522         }
27523         
27524         
27525         if (dom != this.currentEl.dom) {
27526             return;
27527         }
27528         var xy = ev.getXY();
27529         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
27530             return;
27531         }
27532         // only activate leave if mouse cursor is outside... bounding box..
27533         
27534         
27535         
27536         
27537         if (this.currentTip) {
27538             this.currentTip.leave();
27539         }
27540         //Roo.log('clear currentEl');
27541         this.currentEl = false;
27542         
27543         
27544     },
27545     alignment : {
27546         'left' : ['r-l', [-2,0], 'right'],
27547         'right' : ['l-r', [2,0], 'left'],
27548         'bottom' : ['t-b', [0,2], 'top'],
27549         'top' : [ 'b-t', [0,-2], 'bottom']
27550     }
27551     
27552 });
27553
27554
27555 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
27556     
27557     
27558     bindEl : false,
27559     
27560     delay : null, // can be { show : 300 , hide: 500}
27561     
27562     timeout : null,
27563     
27564     hoverState : null, //???
27565     
27566     placement : 'bottom', 
27567     
27568     alignment : false,
27569     
27570     getAutoCreate : function(){
27571     
27572         var cfg = {
27573            cls : 'tooltip',
27574            role : 'tooltip',
27575            cn : [
27576                 {
27577                     cls : 'tooltip-arrow'
27578                 },
27579                 {
27580                     cls : 'tooltip-inner'
27581                 }
27582            ]
27583         };
27584         
27585         return cfg;
27586     },
27587     bind : function(el)
27588     {
27589         this.bindEl = el;
27590     },
27591       
27592     
27593     enter : function () {
27594        
27595         if (this.timeout != null) {
27596             clearTimeout(this.timeout);
27597         }
27598         
27599         this.hoverState = 'in';
27600          //Roo.log("enter - show");
27601         if (!this.delay || !this.delay.show) {
27602             this.show();
27603             return;
27604         }
27605         var _t = this;
27606         this.timeout = setTimeout(function () {
27607             if (_t.hoverState == 'in') {
27608                 _t.show();
27609             }
27610         }, this.delay.show);
27611     },
27612     leave : function()
27613     {
27614         clearTimeout(this.timeout);
27615     
27616         this.hoverState = 'out';
27617          if (!this.delay || !this.delay.hide) {
27618             this.hide();
27619             return;
27620         }
27621        
27622         var _t = this;
27623         this.timeout = setTimeout(function () {
27624             //Roo.log("leave - timeout");
27625             
27626             if (_t.hoverState == 'out') {
27627                 _t.hide();
27628                 Roo.bootstrap.Tooltip.currentEl = false;
27629             }
27630         }, delay);
27631     },
27632     
27633     show : function (msg)
27634     {
27635         if (!this.el) {
27636             this.render(document.body);
27637         }
27638         // set content.
27639         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
27640         
27641         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
27642         
27643         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
27644         
27645         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
27646         
27647         var placement = typeof this.placement == 'function' ?
27648             this.placement.call(this, this.el, on_el) :
27649             this.placement;
27650             
27651         var autoToken = /\s?auto?\s?/i;
27652         var autoPlace = autoToken.test(placement);
27653         if (autoPlace) {
27654             placement = placement.replace(autoToken, '') || 'top';
27655         }
27656         
27657         //this.el.detach()
27658         //this.el.setXY([0,0]);
27659         this.el.show();
27660         //this.el.dom.style.display='block';
27661         
27662         //this.el.appendTo(on_el);
27663         
27664         var p = this.getPosition();
27665         var box = this.el.getBox();
27666         
27667         if (autoPlace) {
27668             // fixme..
27669         }
27670         
27671         var align = this.alignment[placement];
27672         
27673         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
27674         
27675         if(placement == 'top' || placement == 'bottom'){
27676             if(xy[0] < 0){
27677                 placement = 'right';
27678             }
27679             
27680             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
27681                 placement = 'left';
27682             }
27683             
27684             var scroll = Roo.select('body', true).first().getScroll();
27685             
27686             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
27687                 placement = 'top';
27688             }
27689             
27690             align = this.alignment[placement];
27691         }
27692         
27693         this.el.alignTo(this.bindEl, align[0],align[1]);
27694         //var arrow = this.el.select('.arrow',true).first();
27695         //arrow.set(align[2], 
27696         
27697         this.el.addClass(placement);
27698         
27699         this.el.addClass('in fade');
27700         
27701         this.hoverState = null;
27702         
27703         if (this.el.hasClass('fade')) {
27704             // fade it?
27705         }
27706         
27707     },
27708     hide : function()
27709     {
27710          
27711         if (!this.el) {
27712             return;
27713         }
27714         //this.el.setXY([0,0]);
27715         this.el.removeClass('in');
27716         //this.el.hide();
27717         
27718     }
27719     
27720 });
27721  
27722
27723  /*
27724  * - LGPL
27725  *
27726  * Location Picker
27727  * 
27728  */
27729
27730 /**
27731  * @class Roo.bootstrap.LocationPicker
27732  * @extends Roo.bootstrap.Component
27733  * Bootstrap LocationPicker class
27734  * @cfg {Number} latitude Position when init default 0
27735  * @cfg {Number} longitude Position when init default 0
27736  * @cfg {Number} zoom default 15
27737  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
27738  * @cfg {Boolean} mapTypeControl default false
27739  * @cfg {Boolean} disableDoubleClickZoom default false
27740  * @cfg {Boolean} scrollwheel default true
27741  * @cfg {Boolean} streetViewControl default false
27742  * @cfg {Number} radius default 0
27743  * @cfg {String} locationName
27744  * @cfg {Boolean} draggable default true
27745  * @cfg {Boolean} enableAutocomplete default false
27746  * @cfg {Boolean} enableReverseGeocode default true
27747  * @cfg {String} markerTitle
27748  * 
27749  * @constructor
27750  * Create a new LocationPicker
27751  * @param {Object} config The config object
27752  */
27753
27754
27755 Roo.bootstrap.LocationPicker = function(config){
27756     
27757     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
27758     
27759     this.addEvents({
27760         /**
27761          * @event initial
27762          * Fires when the picker initialized.
27763          * @param {Roo.bootstrap.LocationPicker} this
27764          * @param {Google Location} location
27765          */
27766         initial : true,
27767         /**
27768          * @event positionchanged
27769          * Fires when the picker position changed.
27770          * @param {Roo.bootstrap.LocationPicker} this
27771          * @param {Google Location} location
27772          */
27773         positionchanged : true,
27774         /**
27775          * @event resize
27776          * Fires when the map resize.
27777          * @param {Roo.bootstrap.LocationPicker} this
27778          */
27779         resize : true,
27780         /**
27781          * @event show
27782          * Fires when the map show.
27783          * @param {Roo.bootstrap.LocationPicker} this
27784          */
27785         show : true,
27786         /**
27787          * @event hide
27788          * Fires when the map hide.
27789          * @param {Roo.bootstrap.LocationPicker} this
27790          */
27791         hide : true,
27792         /**
27793          * @event mapClick
27794          * Fires when click the map.
27795          * @param {Roo.bootstrap.LocationPicker} this
27796          * @param {Map event} e
27797          */
27798         mapClick : true,
27799         /**
27800          * @event mapRightClick
27801          * Fires when right click the map.
27802          * @param {Roo.bootstrap.LocationPicker} this
27803          * @param {Map event} e
27804          */
27805         mapRightClick : true,
27806         /**
27807          * @event markerClick
27808          * Fires when click the marker.
27809          * @param {Roo.bootstrap.LocationPicker} this
27810          * @param {Map event} e
27811          */
27812         markerClick : true,
27813         /**
27814          * @event markerRightClick
27815          * Fires when right click the marker.
27816          * @param {Roo.bootstrap.LocationPicker} this
27817          * @param {Map event} e
27818          */
27819         markerRightClick : true,
27820         /**
27821          * @event OverlayViewDraw
27822          * Fires when OverlayView Draw
27823          * @param {Roo.bootstrap.LocationPicker} this
27824          */
27825         OverlayViewDraw : true,
27826         /**
27827          * @event OverlayViewOnAdd
27828          * Fires when OverlayView Draw
27829          * @param {Roo.bootstrap.LocationPicker} this
27830          */
27831         OverlayViewOnAdd : true,
27832         /**
27833          * @event OverlayViewOnRemove
27834          * Fires when OverlayView Draw
27835          * @param {Roo.bootstrap.LocationPicker} this
27836          */
27837         OverlayViewOnRemove : true,
27838         /**
27839          * @event OverlayViewShow
27840          * Fires when OverlayView Draw
27841          * @param {Roo.bootstrap.LocationPicker} this
27842          * @param {Pixel} cpx
27843          */
27844         OverlayViewShow : true,
27845         /**
27846          * @event OverlayViewHide
27847          * Fires when OverlayView Draw
27848          * @param {Roo.bootstrap.LocationPicker} this
27849          */
27850         OverlayViewHide : true,
27851         /**
27852          * @event loadexception
27853          * Fires when load google lib failed.
27854          * @param {Roo.bootstrap.LocationPicker} this
27855          */
27856         loadexception : true
27857     });
27858         
27859 };
27860
27861 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
27862     
27863     gMapContext: false,
27864     
27865     latitude: 0,
27866     longitude: 0,
27867     zoom: 15,
27868     mapTypeId: false,
27869     mapTypeControl: false,
27870     disableDoubleClickZoom: false,
27871     scrollwheel: true,
27872     streetViewControl: false,
27873     radius: 0,
27874     locationName: '',
27875     draggable: true,
27876     enableAutocomplete: false,
27877     enableReverseGeocode: true,
27878     markerTitle: '',
27879     
27880     getAutoCreate: function()
27881     {
27882
27883         var cfg = {
27884             tag: 'div',
27885             cls: 'roo-location-picker'
27886         };
27887         
27888         return cfg
27889     },
27890     
27891     initEvents: function(ct, position)
27892     {       
27893         if(!this.el.getWidth() || this.isApplied()){
27894             return;
27895         }
27896         
27897         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27898         
27899         this.initial();
27900     },
27901     
27902     initial: function()
27903     {
27904         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
27905             this.fireEvent('loadexception', this);
27906             return;
27907         }
27908         
27909         if(!this.mapTypeId){
27910             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
27911         }
27912         
27913         this.gMapContext = this.GMapContext();
27914         
27915         this.initOverlayView();
27916         
27917         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
27918         
27919         var _this = this;
27920                 
27921         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
27922             _this.setPosition(_this.gMapContext.marker.position);
27923         });
27924         
27925         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
27926             _this.fireEvent('mapClick', this, event);
27927             
27928         });
27929
27930         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
27931             _this.fireEvent('mapRightClick', this, event);
27932             
27933         });
27934         
27935         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
27936             _this.fireEvent('markerClick', this, event);
27937             
27938         });
27939
27940         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
27941             _this.fireEvent('markerRightClick', this, event);
27942             
27943         });
27944         
27945         this.setPosition(this.gMapContext.location);
27946         
27947         this.fireEvent('initial', this, this.gMapContext.location);
27948     },
27949     
27950     initOverlayView: function()
27951     {
27952         var _this = this;
27953         
27954         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
27955             
27956             draw: function()
27957             {
27958                 _this.fireEvent('OverlayViewDraw', _this);
27959             },
27960             
27961             onAdd: function()
27962             {
27963                 _this.fireEvent('OverlayViewOnAdd', _this);
27964             },
27965             
27966             onRemove: function()
27967             {
27968                 _this.fireEvent('OverlayViewOnRemove', _this);
27969             },
27970             
27971             show: function(cpx)
27972             {
27973                 _this.fireEvent('OverlayViewShow', _this, cpx);
27974             },
27975             
27976             hide: function()
27977             {
27978                 _this.fireEvent('OverlayViewHide', _this);
27979             }
27980             
27981         });
27982     },
27983     
27984     fromLatLngToContainerPixel: function(event)
27985     {
27986         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
27987     },
27988     
27989     isApplied: function() 
27990     {
27991         return this.getGmapContext() == false ? false : true;
27992     },
27993     
27994     getGmapContext: function() 
27995     {
27996         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
27997     },
27998     
27999     GMapContext: function() 
28000     {
28001         var position = new google.maps.LatLng(this.latitude, this.longitude);
28002         
28003         var _map = new google.maps.Map(this.el.dom, {
28004             center: position,
28005             zoom: this.zoom,
28006             mapTypeId: this.mapTypeId,
28007             mapTypeControl: this.mapTypeControl,
28008             disableDoubleClickZoom: this.disableDoubleClickZoom,
28009             scrollwheel: this.scrollwheel,
28010             streetViewControl: this.streetViewControl,
28011             locationName: this.locationName,
28012             draggable: this.draggable,
28013             enableAutocomplete: this.enableAutocomplete,
28014             enableReverseGeocode: this.enableReverseGeocode
28015         });
28016         
28017         var _marker = new google.maps.Marker({
28018             position: position,
28019             map: _map,
28020             title: this.markerTitle,
28021             draggable: this.draggable
28022         });
28023         
28024         return {
28025             map: _map,
28026             marker: _marker,
28027             circle: null,
28028             location: position,
28029             radius: this.radius,
28030             locationName: this.locationName,
28031             addressComponents: {
28032                 formatted_address: null,
28033                 addressLine1: null,
28034                 addressLine2: null,
28035                 streetName: null,
28036                 streetNumber: null,
28037                 city: null,
28038                 district: null,
28039                 state: null,
28040                 stateOrProvince: null
28041             },
28042             settings: this,
28043             domContainer: this.el.dom,
28044             geodecoder: new google.maps.Geocoder()
28045         };
28046     },
28047     
28048     drawCircle: function(center, radius, options) 
28049     {
28050         if (this.gMapContext.circle != null) {
28051             this.gMapContext.circle.setMap(null);
28052         }
28053         if (radius > 0) {
28054             radius *= 1;
28055             options = Roo.apply({}, options, {
28056                 strokeColor: "#0000FF",
28057                 strokeOpacity: .35,
28058                 strokeWeight: 2,
28059                 fillColor: "#0000FF",
28060                 fillOpacity: .2
28061             });
28062             
28063             options.map = this.gMapContext.map;
28064             options.radius = radius;
28065             options.center = center;
28066             this.gMapContext.circle = new google.maps.Circle(options);
28067             return this.gMapContext.circle;
28068         }
28069         
28070         return null;
28071     },
28072     
28073     setPosition: function(location) 
28074     {
28075         this.gMapContext.location = location;
28076         this.gMapContext.marker.setPosition(location);
28077         this.gMapContext.map.panTo(location);
28078         this.drawCircle(location, this.gMapContext.radius, {});
28079         
28080         var _this = this;
28081         
28082         if (this.gMapContext.settings.enableReverseGeocode) {
28083             this.gMapContext.geodecoder.geocode({
28084                 latLng: this.gMapContext.location
28085             }, function(results, status) {
28086                 
28087                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
28088                     _this.gMapContext.locationName = results[0].formatted_address;
28089                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
28090                     
28091                     _this.fireEvent('positionchanged', this, location);
28092                 }
28093             });
28094             
28095             return;
28096         }
28097         
28098         this.fireEvent('positionchanged', this, location);
28099     },
28100     
28101     resize: function()
28102     {
28103         google.maps.event.trigger(this.gMapContext.map, "resize");
28104         
28105         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
28106         
28107         this.fireEvent('resize', this);
28108     },
28109     
28110     setPositionByLatLng: function(latitude, longitude)
28111     {
28112         this.setPosition(new google.maps.LatLng(latitude, longitude));
28113     },
28114     
28115     getCurrentPosition: function() 
28116     {
28117         return {
28118             latitude: this.gMapContext.location.lat(),
28119             longitude: this.gMapContext.location.lng()
28120         };
28121     },
28122     
28123     getAddressName: function() 
28124     {
28125         return this.gMapContext.locationName;
28126     },
28127     
28128     getAddressComponents: function() 
28129     {
28130         return this.gMapContext.addressComponents;
28131     },
28132     
28133     address_component_from_google_geocode: function(address_components) 
28134     {
28135         var result = {};
28136         
28137         for (var i = 0; i < address_components.length; i++) {
28138             var component = address_components[i];
28139             if (component.types.indexOf("postal_code") >= 0) {
28140                 result.postalCode = component.short_name;
28141             } else if (component.types.indexOf("street_number") >= 0) {
28142                 result.streetNumber = component.short_name;
28143             } else if (component.types.indexOf("route") >= 0) {
28144                 result.streetName = component.short_name;
28145             } else if (component.types.indexOf("neighborhood") >= 0) {
28146                 result.city = component.short_name;
28147             } else if (component.types.indexOf("locality") >= 0) {
28148                 result.city = component.short_name;
28149             } else if (component.types.indexOf("sublocality") >= 0) {
28150                 result.district = component.short_name;
28151             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
28152                 result.stateOrProvince = component.short_name;
28153             } else if (component.types.indexOf("country") >= 0) {
28154                 result.country = component.short_name;
28155             }
28156         }
28157         
28158         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
28159         result.addressLine2 = "";
28160         return result;
28161     },
28162     
28163     setZoomLevel: function(zoom)
28164     {
28165         this.gMapContext.map.setZoom(zoom);
28166     },
28167     
28168     show: function()
28169     {
28170         if(!this.el){
28171             return;
28172         }
28173         
28174         this.el.show();
28175         
28176         this.resize();
28177         
28178         this.fireEvent('show', this);
28179     },
28180     
28181     hide: function()
28182     {
28183         if(!this.el){
28184             return;
28185         }
28186         
28187         this.el.hide();
28188         
28189         this.fireEvent('hide', this);
28190     }
28191     
28192 });
28193
28194 Roo.apply(Roo.bootstrap.LocationPicker, {
28195     
28196     OverlayView : function(map, options)
28197     {
28198         options = options || {};
28199         
28200         this.setMap(map);
28201     }
28202     
28203     
28204 });/**
28205  * @class Roo.bootstrap.Alert
28206  * @extends Roo.bootstrap.Component
28207  * Bootstrap Alert class - shows an alert area box
28208  * eg
28209  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
28210   Enter a valid email address
28211 </div>
28212  * @licence LGPL
28213  * @cfg {String} title The title of alert
28214  * @cfg {String} html The content of alert
28215  * @cfg {String} weight (  success | info | warning | danger )
28216  * @cfg {String} faicon font-awesomeicon
28217  * 
28218  * @constructor
28219  * Create a new alert
28220  * @param {Object} config The config object
28221  */
28222
28223
28224 Roo.bootstrap.Alert = function(config){
28225     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
28226     
28227 };
28228
28229 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
28230     
28231     title: '',
28232     html: '',
28233     weight: false,
28234     faicon: false,
28235     
28236     getAutoCreate : function()
28237     {
28238         
28239         var cfg = {
28240             tag : 'div',
28241             cls : 'alert',
28242             cn : [
28243                 {
28244                     tag : 'i',
28245                     cls : 'roo-alert-icon'
28246                     
28247                 },
28248                 {
28249                     tag : 'b',
28250                     cls : 'roo-alert-title',
28251                     html : this.title
28252                 },
28253                 {
28254                     tag : 'span',
28255                     cls : 'roo-alert-text',
28256                     html : this.html
28257                 }
28258             ]
28259         };
28260         
28261         if(this.faicon){
28262             cfg.cn[0].cls += ' fa ' + this.faicon;
28263         }
28264         
28265         if(this.weight){
28266             cfg.cls += ' alert-' + this.weight;
28267         }
28268         
28269         return cfg;
28270     },
28271     
28272     initEvents: function() 
28273     {
28274         this.el.setVisibilityMode(Roo.Element.DISPLAY);
28275     },
28276     
28277     setTitle : function(str)
28278     {
28279         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
28280     },
28281     
28282     setText : function(str)
28283     {
28284         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
28285     },
28286     
28287     setWeight : function(weight)
28288     {
28289         if(this.weight){
28290             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
28291         }
28292         
28293         this.weight = weight;
28294         
28295         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
28296     },
28297     
28298     setIcon : function(icon)
28299     {
28300         if(this.faicon){
28301             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
28302         }
28303         
28304         this.faicon = icon;
28305         
28306         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
28307     },
28308     
28309     hide: function() 
28310     {
28311         this.el.hide();   
28312     },
28313     
28314     show: function() 
28315     {  
28316         this.el.show();   
28317     }
28318     
28319 });
28320
28321  
28322 /*
28323 * Licence: LGPL
28324 */
28325
28326 /**
28327  * @class Roo.bootstrap.UploadCropbox
28328  * @extends Roo.bootstrap.Component
28329  * Bootstrap UploadCropbox class
28330  * @cfg {String} emptyText show when image has been loaded
28331  * @cfg {String} rotateNotify show when image too small to rotate
28332  * @cfg {Number} errorTimeout default 3000
28333  * @cfg {Number} minWidth default 300
28334  * @cfg {Number} minHeight default 300
28335  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
28336  * @cfg {Boolean} isDocument (true|false) default false
28337  * @cfg {String} url action url
28338  * @cfg {String} paramName default 'imageUpload'
28339  * @cfg {String} method default POST
28340  * @cfg {Boolean} loadMask (true|false) default true
28341  * @cfg {Boolean} loadingText default 'Loading...'
28342  * 
28343  * @constructor
28344  * Create a new UploadCropbox
28345  * @param {Object} config The config object
28346  */
28347
28348 Roo.bootstrap.UploadCropbox = function(config){
28349     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
28350     
28351     this.addEvents({
28352         /**
28353          * @event beforeselectfile
28354          * Fire before select file
28355          * @param {Roo.bootstrap.UploadCropbox} this
28356          */
28357         "beforeselectfile" : true,
28358         /**
28359          * @event initial
28360          * Fire after initEvent
28361          * @param {Roo.bootstrap.UploadCropbox} this
28362          */
28363         "initial" : true,
28364         /**
28365          * @event crop
28366          * Fire after initEvent
28367          * @param {Roo.bootstrap.UploadCropbox} this
28368          * @param {String} data
28369          */
28370         "crop" : true,
28371         /**
28372          * @event prepare
28373          * Fire when preparing the file data
28374          * @param {Roo.bootstrap.UploadCropbox} this
28375          * @param {Object} file
28376          */
28377         "prepare" : true,
28378         /**
28379          * @event exception
28380          * Fire when get exception
28381          * @param {Roo.bootstrap.UploadCropbox} this
28382          * @param {XMLHttpRequest} xhr
28383          */
28384         "exception" : true,
28385         /**
28386          * @event beforeloadcanvas
28387          * Fire before load the canvas
28388          * @param {Roo.bootstrap.UploadCropbox} this
28389          * @param {String} src
28390          */
28391         "beforeloadcanvas" : true,
28392         /**
28393          * @event trash
28394          * Fire when trash image
28395          * @param {Roo.bootstrap.UploadCropbox} this
28396          */
28397         "trash" : true,
28398         /**
28399          * @event download
28400          * Fire when download the image
28401          * @param {Roo.bootstrap.UploadCropbox} this
28402          */
28403         "download" : true,
28404         /**
28405          * @event footerbuttonclick
28406          * Fire when footerbuttonclick
28407          * @param {Roo.bootstrap.UploadCropbox} this
28408          * @param {String} type
28409          */
28410         "footerbuttonclick" : true,
28411         /**
28412          * @event resize
28413          * Fire when resize
28414          * @param {Roo.bootstrap.UploadCropbox} this
28415          */
28416         "resize" : true,
28417         /**
28418          * @event rotate
28419          * Fire when rotate the image
28420          * @param {Roo.bootstrap.UploadCropbox} this
28421          * @param {String} pos
28422          */
28423         "rotate" : true,
28424         /**
28425          * @event inspect
28426          * Fire when inspect the file
28427          * @param {Roo.bootstrap.UploadCropbox} this
28428          * @param {Object} file
28429          */
28430         "inspect" : true,
28431         /**
28432          * @event upload
28433          * Fire when xhr upload the file
28434          * @param {Roo.bootstrap.UploadCropbox} this
28435          * @param {Object} data
28436          */
28437         "upload" : true,
28438         /**
28439          * @event arrange
28440          * Fire when arrange the file data
28441          * @param {Roo.bootstrap.UploadCropbox} this
28442          * @param {Object} formData
28443          */
28444         "arrange" : true
28445     });
28446     
28447     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
28448 };
28449
28450 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
28451     
28452     emptyText : 'Click to upload image',
28453     rotateNotify : 'Image is too small to rotate',
28454     errorTimeout : 3000,
28455     scale : 0,
28456     baseScale : 1,
28457     rotate : 0,
28458     dragable : false,
28459     pinching : false,
28460     mouseX : 0,
28461     mouseY : 0,
28462     cropData : false,
28463     minWidth : 300,
28464     minHeight : 300,
28465     file : false,
28466     exif : {},
28467     baseRotate : 1,
28468     cropType : 'image/jpeg',
28469     buttons : false,
28470     canvasLoaded : false,
28471     isDocument : false,
28472     method : 'POST',
28473     paramName : 'imageUpload',
28474     loadMask : true,
28475     loadingText : 'Loading...',
28476     maskEl : false,
28477     
28478     getAutoCreate : function()
28479     {
28480         var cfg = {
28481             tag : 'div',
28482             cls : 'roo-upload-cropbox',
28483             cn : [
28484                 {
28485                     tag : 'input',
28486                     cls : 'roo-upload-cropbox-selector',
28487                     type : 'file'
28488                 },
28489                 {
28490                     tag : 'div',
28491                     cls : 'roo-upload-cropbox-body',
28492                     style : 'cursor:pointer',
28493                     cn : [
28494                         {
28495                             tag : 'div',
28496                             cls : 'roo-upload-cropbox-preview'
28497                         },
28498                         {
28499                             tag : 'div',
28500                             cls : 'roo-upload-cropbox-thumb'
28501                         },
28502                         {
28503                             tag : 'div',
28504                             cls : 'roo-upload-cropbox-empty-notify',
28505                             html : this.emptyText
28506                         },
28507                         {
28508                             tag : 'div',
28509                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
28510                             html : this.rotateNotify
28511                         }
28512                     ]
28513                 },
28514                 {
28515                     tag : 'div',
28516                     cls : 'roo-upload-cropbox-footer',
28517                     cn : {
28518                         tag : 'div',
28519                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
28520                         cn : []
28521                     }
28522                 }
28523             ]
28524         };
28525         
28526         return cfg;
28527     },
28528     
28529     onRender : function(ct, position)
28530     {
28531         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
28532         
28533         if (this.buttons.length) {
28534             
28535             Roo.each(this.buttons, function(bb) {
28536                 
28537                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
28538                 
28539                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
28540                 
28541             }, this);
28542         }
28543         
28544         if(this.loadMask){
28545             this.maskEl = this.el;
28546         }
28547     },
28548     
28549     initEvents : function()
28550     {
28551         this.urlAPI = (window.createObjectURL && window) || 
28552                                 (window.URL && URL.revokeObjectURL && URL) || 
28553                                 (window.webkitURL && webkitURL);
28554                         
28555         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
28556         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28557         
28558         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
28559         this.selectorEl.hide();
28560         
28561         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
28562         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28563         
28564         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
28565         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28566         this.thumbEl.hide();
28567         
28568         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
28569         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28570         
28571         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
28572         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28573         this.errorEl.hide();
28574         
28575         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
28576         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28577         this.footerEl.hide();
28578         
28579         this.setThumbBoxSize();
28580         
28581         this.bind();
28582         
28583         this.resize();
28584         
28585         this.fireEvent('initial', this);
28586     },
28587
28588     bind : function()
28589     {
28590         var _this = this;
28591         
28592         window.addEventListener("resize", function() { _this.resize(); } );
28593         
28594         this.bodyEl.on('click', this.beforeSelectFile, this);
28595         
28596         if(Roo.isTouch){
28597             this.bodyEl.on('touchstart', this.onTouchStart, this);
28598             this.bodyEl.on('touchmove', this.onTouchMove, this);
28599             this.bodyEl.on('touchend', this.onTouchEnd, this);
28600         }
28601         
28602         if(!Roo.isTouch){
28603             this.bodyEl.on('mousedown', this.onMouseDown, this);
28604             this.bodyEl.on('mousemove', this.onMouseMove, this);
28605             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
28606             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
28607             Roo.get(document).on('mouseup', this.onMouseUp, this);
28608         }
28609         
28610         this.selectorEl.on('change', this.onFileSelected, this);
28611     },
28612     
28613     reset : function()
28614     {    
28615         this.scale = 0;
28616         this.baseScale = 1;
28617         this.rotate = 0;
28618         this.baseRotate = 1;
28619         this.dragable = false;
28620         this.pinching = false;
28621         this.mouseX = 0;
28622         this.mouseY = 0;
28623         this.cropData = false;
28624         this.notifyEl.dom.innerHTML = this.emptyText;
28625         
28626         this.selectorEl.dom.value = '';
28627         
28628     },
28629     
28630     resize : function()
28631     {
28632         if(this.fireEvent('resize', this) != false){
28633             this.setThumbBoxPosition();
28634             this.setCanvasPosition();
28635         }
28636     },
28637     
28638     onFooterButtonClick : function(e, el, o, type)
28639     {
28640         switch (type) {
28641             case 'rotate-left' :
28642                 this.onRotateLeft(e);
28643                 break;
28644             case 'rotate-right' :
28645                 this.onRotateRight(e);
28646                 break;
28647             case 'picture' :
28648                 this.beforeSelectFile(e);
28649                 break;
28650             case 'trash' :
28651                 this.trash(e);
28652                 break;
28653             case 'crop' :
28654                 this.crop(e);
28655                 break;
28656             case 'download' :
28657                 this.download(e);
28658                 break;
28659             default :
28660                 break;
28661         }
28662         
28663         this.fireEvent('footerbuttonclick', this, type);
28664     },
28665     
28666     beforeSelectFile : function(e)
28667     {
28668         e.preventDefault();
28669         
28670         if(this.fireEvent('beforeselectfile', this) != false){
28671             this.selectorEl.dom.click();
28672         }
28673     },
28674     
28675     onFileSelected : function(e)
28676     {
28677         e.preventDefault();
28678         
28679         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28680             return;
28681         }
28682         
28683         var file = this.selectorEl.dom.files[0];
28684         
28685         if(this.fireEvent('inspect', this, file) != false){
28686             this.prepare(file);
28687         }
28688         
28689     },
28690     
28691     trash : function(e)
28692     {
28693         this.fireEvent('trash', this);
28694     },
28695     
28696     download : function(e)
28697     {
28698         this.fireEvent('download', this);
28699     },
28700     
28701     loadCanvas : function(src)
28702     {   
28703         if(this.fireEvent('beforeloadcanvas', this, src) != false){
28704             
28705             this.reset();
28706             
28707             this.imageEl = document.createElement('img');
28708             
28709             var _this = this;
28710             
28711             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
28712             
28713             this.imageEl.src = src;
28714         }
28715     },
28716     
28717     onLoadCanvas : function()
28718     {   
28719         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
28720         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
28721         
28722         this.bodyEl.un('click', this.beforeSelectFile, this);
28723         
28724         this.notifyEl.hide();
28725         this.thumbEl.show();
28726         this.footerEl.show();
28727         
28728         this.baseRotateLevel();
28729         
28730         if(this.isDocument){
28731             this.setThumbBoxSize();
28732         }
28733         
28734         this.setThumbBoxPosition();
28735         
28736         this.baseScaleLevel();
28737         
28738         this.draw();
28739         
28740         this.resize();
28741         
28742         this.canvasLoaded = true;
28743         
28744         if(this.loadMask){
28745             this.maskEl.unmask();
28746         }
28747         
28748     },
28749     
28750     setCanvasPosition : function()
28751     {   
28752         if(!this.canvasEl){
28753             return;
28754         }
28755         
28756         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
28757         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
28758         
28759         this.previewEl.setLeft(pw);
28760         this.previewEl.setTop(ph);
28761         
28762     },
28763     
28764     onMouseDown : function(e)
28765     {   
28766         e.stopEvent();
28767         
28768         this.dragable = true;
28769         this.pinching = false;
28770         
28771         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
28772             this.dragable = false;
28773             return;
28774         }
28775         
28776         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
28777         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
28778         
28779     },
28780     
28781     onMouseMove : function(e)
28782     {   
28783         e.stopEvent();
28784         
28785         if(!this.canvasLoaded){
28786             return;
28787         }
28788         
28789         if (!this.dragable){
28790             return;
28791         }
28792         
28793         var minX = Math.ceil(this.thumbEl.getLeft(true));
28794         var minY = Math.ceil(this.thumbEl.getTop(true));
28795         
28796         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
28797         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
28798         
28799         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
28800         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
28801         
28802         x = x - this.mouseX;
28803         y = y - this.mouseY;
28804         
28805         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
28806         var bgY = Math.ceil(y + this.previewEl.getTop(true));
28807         
28808         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
28809         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
28810         
28811         this.previewEl.setLeft(bgX);
28812         this.previewEl.setTop(bgY);
28813         
28814         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
28815         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
28816     },
28817     
28818     onMouseUp : function(e)
28819     {   
28820         e.stopEvent();
28821         
28822         this.dragable = false;
28823     },
28824     
28825     onMouseWheel : function(e)
28826     {   
28827         e.stopEvent();
28828         
28829         this.startScale = this.scale;
28830         
28831         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
28832         
28833         if(!this.zoomable()){
28834             this.scale = this.startScale;
28835             return;
28836         }
28837         
28838         this.draw();
28839         
28840         return;
28841     },
28842     
28843     zoomable : function()
28844     {
28845         var minScale = this.thumbEl.getWidth() / this.minWidth;
28846         
28847         if(this.minWidth < this.minHeight){
28848             minScale = this.thumbEl.getHeight() / this.minHeight;
28849         }
28850         
28851         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
28852         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
28853         
28854         if(
28855                 this.isDocument &&
28856                 (this.rotate == 0 || this.rotate == 180) && 
28857                 (
28858                     width > this.imageEl.OriginWidth || 
28859                     height > this.imageEl.OriginHeight ||
28860                     (width < this.minWidth && height < this.minHeight)
28861                 )
28862         ){
28863             return false;
28864         }
28865         
28866         if(
28867                 this.isDocument &&
28868                 (this.rotate == 90 || this.rotate == 270) && 
28869                 (
28870                     width > this.imageEl.OriginWidth || 
28871                     height > this.imageEl.OriginHeight ||
28872                     (width < this.minHeight && height < this.minWidth)
28873                 )
28874         ){
28875             return false;
28876         }
28877         
28878         if(
28879                 !this.isDocument &&
28880                 (this.rotate == 0 || this.rotate == 180) && 
28881                 (
28882                     width < this.minWidth || 
28883                     width > this.imageEl.OriginWidth || 
28884                     height < this.minHeight || 
28885                     height > this.imageEl.OriginHeight
28886                 )
28887         ){
28888             return false;
28889         }
28890         
28891         if(
28892                 !this.isDocument &&
28893                 (this.rotate == 90 || this.rotate == 270) && 
28894                 (
28895                     width < this.minHeight || 
28896                     width > this.imageEl.OriginWidth || 
28897                     height < this.minWidth || 
28898                     height > this.imageEl.OriginHeight
28899                 )
28900         ){
28901             return false;
28902         }
28903         
28904         return true;
28905         
28906     },
28907     
28908     onRotateLeft : function(e)
28909     {   
28910         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
28911             
28912             var minScale = this.thumbEl.getWidth() / this.minWidth;
28913             
28914             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
28915             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
28916             
28917             this.startScale = this.scale;
28918             
28919             while (this.getScaleLevel() < minScale){
28920             
28921                 this.scale = this.scale + 1;
28922                 
28923                 if(!this.zoomable()){
28924                     break;
28925                 }
28926                 
28927                 if(
28928                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
28929                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
28930                 ){
28931                     continue;
28932                 }
28933                 
28934                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
28935
28936                 this.draw();
28937                 
28938                 return;
28939             }
28940             
28941             this.scale = this.startScale;
28942             
28943             this.onRotateFail();
28944             
28945             return false;
28946         }
28947         
28948         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
28949
28950         if(this.isDocument){
28951             this.setThumbBoxSize();
28952             this.setThumbBoxPosition();
28953             this.setCanvasPosition();
28954         }
28955         
28956         this.draw();
28957         
28958         this.fireEvent('rotate', this, 'left');
28959         
28960     },
28961     
28962     onRotateRight : function(e)
28963     {
28964         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
28965             
28966             var minScale = this.thumbEl.getWidth() / this.minWidth;
28967         
28968             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
28969             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
28970             
28971             this.startScale = this.scale;
28972             
28973             while (this.getScaleLevel() < minScale){
28974             
28975                 this.scale = this.scale + 1;
28976                 
28977                 if(!this.zoomable()){
28978                     break;
28979                 }
28980                 
28981                 if(
28982                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
28983                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
28984                 ){
28985                     continue;
28986                 }
28987                 
28988                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28989
28990                 this.draw();
28991                 
28992                 return;
28993             }
28994             
28995             this.scale = this.startScale;
28996             
28997             this.onRotateFail();
28998             
28999             return false;
29000         }
29001         
29002         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29003
29004         if(this.isDocument){
29005             this.setThumbBoxSize();
29006             this.setThumbBoxPosition();
29007             this.setCanvasPosition();
29008         }
29009         
29010         this.draw();
29011         
29012         this.fireEvent('rotate', this, 'right');
29013     },
29014     
29015     onRotateFail : function()
29016     {
29017         this.errorEl.show(true);
29018         
29019         var _this = this;
29020         
29021         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
29022     },
29023     
29024     draw : function()
29025     {
29026         this.previewEl.dom.innerHTML = '';
29027         
29028         var canvasEl = document.createElement("canvas");
29029         
29030         var contextEl = canvasEl.getContext("2d");
29031         
29032         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29033         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29034         var center = this.imageEl.OriginWidth / 2;
29035         
29036         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
29037             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29038             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29039             center = this.imageEl.OriginHeight / 2;
29040         }
29041         
29042         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
29043         
29044         contextEl.translate(center, center);
29045         contextEl.rotate(this.rotate * Math.PI / 180);
29046
29047         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
29048         
29049         this.canvasEl = document.createElement("canvas");
29050         
29051         this.contextEl = this.canvasEl.getContext("2d");
29052         
29053         switch (this.rotate) {
29054             case 0 :
29055                 
29056                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29057                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29058                 
29059                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29060                 
29061                 break;
29062             case 90 : 
29063                 
29064                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29065                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29066                 
29067                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29068                     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);
29069                     break;
29070                 }
29071                 
29072                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29073                 
29074                 break;
29075             case 180 :
29076                 
29077                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29078                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29079                 
29080                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29081                     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);
29082                     break;
29083                 }
29084                 
29085                 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);
29086                 
29087                 break;
29088             case 270 :
29089                 
29090                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29091                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29092         
29093                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29094                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29095                     break;
29096                 }
29097                 
29098                 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);
29099                 
29100                 break;
29101             default : 
29102                 break;
29103         }
29104         
29105         this.previewEl.appendChild(this.canvasEl);
29106         
29107         this.setCanvasPosition();
29108     },
29109     
29110     crop : function()
29111     {
29112         if(!this.canvasLoaded){
29113             return;
29114         }
29115         
29116         var imageCanvas = document.createElement("canvas");
29117         
29118         var imageContext = imageCanvas.getContext("2d");
29119         
29120         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
29121         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
29122         
29123         var center = imageCanvas.width / 2;
29124         
29125         imageContext.translate(center, center);
29126         
29127         imageContext.rotate(this.rotate * Math.PI / 180);
29128         
29129         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
29130         
29131         var canvas = document.createElement("canvas");
29132         
29133         var context = canvas.getContext("2d");
29134                 
29135         canvas.width = this.minWidth;
29136         canvas.height = this.minHeight;
29137
29138         switch (this.rotate) {
29139             case 0 :
29140                 
29141                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
29142                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
29143                 
29144                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29145                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29146                 
29147                 var targetWidth = this.minWidth - 2 * x;
29148                 var targetHeight = this.minHeight - 2 * y;
29149                 
29150                 var scale = 1;
29151                 
29152                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29153                     scale = targetWidth / width;
29154                 }
29155                 
29156                 if(x > 0 && y == 0){
29157                     scale = targetHeight / height;
29158                 }
29159                 
29160                 if(x > 0 && y > 0){
29161                     scale = targetWidth / width;
29162                     
29163                     if(width < height){
29164                         scale = targetHeight / height;
29165                     }
29166                 }
29167                 
29168                 context.scale(scale, scale);
29169                 
29170                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29171                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29172
29173                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29174                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29175
29176                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29177                 
29178                 break;
29179             case 90 : 
29180                 
29181                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
29182                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
29183                 
29184                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29185                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29186                 
29187                 var targetWidth = this.minWidth - 2 * x;
29188                 var targetHeight = this.minHeight - 2 * y;
29189                 
29190                 var scale = 1;
29191                 
29192                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29193                     scale = targetWidth / width;
29194                 }
29195                 
29196                 if(x > 0 && y == 0){
29197                     scale = targetHeight / height;
29198                 }
29199                 
29200                 if(x > 0 && y > 0){
29201                     scale = targetWidth / width;
29202                     
29203                     if(width < height){
29204                         scale = targetHeight / height;
29205                     }
29206                 }
29207                 
29208                 context.scale(scale, scale);
29209                 
29210                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29211                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29212
29213                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29214                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29215                 
29216                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
29217                 
29218                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29219                 
29220                 break;
29221             case 180 :
29222                 
29223                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
29224                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
29225                 
29226                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29227                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29228                 
29229                 var targetWidth = this.minWidth - 2 * x;
29230                 var targetHeight = this.minHeight - 2 * y;
29231                 
29232                 var scale = 1;
29233                 
29234                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29235                     scale = targetWidth / width;
29236                 }
29237                 
29238                 if(x > 0 && y == 0){
29239                     scale = targetHeight / height;
29240                 }
29241                 
29242                 if(x > 0 && y > 0){
29243                     scale = targetWidth / width;
29244                     
29245                     if(width < height){
29246                         scale = targetHeight / height;
29247                     }
29248                 }
29249                 
29250                 context.scale(scale, scale);
29251                 
29252                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29253                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29254
29255                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29256                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29257
29258                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
29259                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
29260                 
29261                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29262                 
29263                 break;
29264             case 270 :
29265                 
29266                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
29267                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
29268                 
29269                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29270                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29271                 
29272                 var targetWidth = this.minWidth - 2 * x;
29273                 var targetHeight = this.minHeight - 2 * y;
29274                 
29275                 var scale = 1;
29276                 
29277                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29278                     scale = targetWidth / width;
29279                 }
29280                 
29281                 if(x > 0 && y == 0){
29282                     scale = targetHeight / height;
29283                 }
29284                 
29285                 if(x > 0 && y > 0){
29286                     scale = targetWidth / width;
29287                     
29288                     if(width < height){
29289                         scale = targetHeight / height;
29290                     }
29291                 }
29292                 
29293                 context.scale(scale, scale);
29294                 
29295                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29296                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29297
29298                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29299                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29300                 
29301                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
29302                 
29303                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29304                 
29305                 break;
29306             default : 
29307                 break;
29308         }
29309         
29310         this.cropData = canvas.toDataURL(this.cropType);
29311         
29312         if(this.fireEvent('crop', this, this.cropData) !== false){
29313             this.process(this.file, this.cropData);
29314         }
29315         
29316         return;
29317         
29318     },
29319     
29320     setThumbBoxSize : function()
29321     {
29322         var width, height;
29323         
29324         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
29325             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
29326             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
29327             
29328             this.minWidth = width;
29329             this.minHeight = height;
29330             
29331             if(this.rotate == 90 || this.rotate == 270){
29332                 this.minWidth = height;
29333                 this.minHeight = width;
29334             }
29335         }
29336         
29337         height = 300;
29338         width = Math.ceil(this.minWidth * height / this.minHeight);
29339         
29340         if(this.minWidth > this.minHeight){
29341             width = 300;
29342             height = Math.ceil(this.minHeight * width / this.minWidth);
29343         }
29344         
29345         this.thumbEl.setStyle({
29346             width : width + 'px',
29347             height : height + 'px'
29348         });
29349
29350         return;
29351             
29352     },
29353     
29354     setThumbBoxPosition : function()
29355     {
29356         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
29357         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
29358         
29359         this.thumbEl.setLeft(x);
29360         this.thumbEl.setTop(y);
29361         
29362     },
29363     
29364     baseRotateLevel : function()
29365     {
29366         this.baseRotate = 1;
29367         
29368         if(
29369                 typeof(this.exif) != 'undefined' &&
29370                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
29371                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
29372         ){
29373             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
29374         }
29375         
29376         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
29377         
29378     },
29379     
29380     baseScaleLevel : function()
29381     {
29382         var width, height;
29383         
29384         if(this.isDocument){
29385             
29386             if(this.baseRotate == 6 || this.baseRotate == 8){
29387             
29388                 height = this.thumbEl.getHeight();
29389                 this.baseScale = height / this.imageEl.OriginWidth;
29390
29391                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
29392                     width = this.thumbEl.getWidth();
29393                     this.baseScale = width / this.imageEl.OriginHeight;
29394                 }
29395
29396                 return;
29397             }
29398
29399             height = this.thumbEl.getHeight();
29400             this.baseScale = height / this.imageEl.OriginHeight;
29401
29402             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
29403                 width = this.thumbEl.getWidth();
29404                 this.baseScale = width / this.imageEl.OriginWidth;
29405             }
29406
29407             return;
29408         }
29409         
29410         if(this.baseRotate == 6 || this.baseRotate == 8){
29411             
29412             width = this.thumbEl.getHeight();
29413             this.baseScale = width / this.imageEl.OriginHeight;
29414             
29415             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
29416                 height = this.thumbEl.getWidth();
29417                 this.baseScale = height / this.imageEl.OriginHeight;
29418             }
29419             
29420             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29421                 height = this.thumbEl.getWidth();
29422                 this.baseScale = height / this.imageEl.OriginHeight;
29423                 
29424                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
29425                     width = this.thumbEl.getHeight();
29426                     this.baseScale = width / this.imageEl.OriginWidth;
29427                 }
29428             }
29429             
29430             return;
29431         }
29432         
29433         width = this.thumbEl.getWidth();
29434         this.baseScale = width / this.imageEl.OriginWidth;
29435         
29436         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
29437             height = this.thumbEl.getHeight();
29438             this.baseScale = height / this.imageEl.OriginHeight;
29439         }
29440         
29441         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
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         }
29452         
29453         return;
29454     },
29455     
29456     getScaleLevel : function()
29457     {
29458         return this.baseScale * Math.pow(1.1, this.scale);
29459     },
29460     
29461     onTouchStart : function(e)
29462     {
29463         if(!this.canvasLoaded){
29464             this.beforeSelectFile(e);
29465             return;
29466         }
29467         
29468         var touches = e.browserEvent.touches;
29469         
29470         if(!touches){
29471             return;
29472         }
29473         
29474         if(touches.length == 1){
29475             this.onMouseDown(e);
29476             return;
29477         }
29478         
29479         if(touches.length != 2){
29480             return;
29481         }
29482         
29483         var coords = [];
29484         
29485         for(var i = 0, finger; finger = touches[i]; i++){
29486             coords.push(finger.pageX, finger.pageY);
29487         }
29488         
29489         var x = Math.pow(coords[0] - coords[2], 2);
29490         var y = Math.pow(coords[1] - coords[3], 2);
29491         
29492         this.startDistance = Math.sqrt(x + y);
29493         
29494         this.startScale = this.scale;
29495         
29496         this.pinching = true;
29497         this.dragable = false;
29498         
29499     },
29500     
29501     onTouchMove : function(e)
29502     {
29503         if(!this.pinching && !this.dragable){
29504             return;
29505         }
29506         
29507         var touches = e.browserEvent.touches;
29508         
29509         if(!touches){
29510             return;
29511         }
29512         
29513         if(this.dragable){
29514             this.onMouseMove(e);
29515             return;
29516         }
29517         
29518         var coords = [];
29519         
29520         for(var i = 0, finger; finger = touches[i]; i++){
29521             coords.push(finger.pageX, finger.pageY);
29522         }
29523         
29524         var x = Math.pow(coords[0] - coords[2], 2);
29525         var y = Math.pow(coords[1] - coords[3], 2);
29526         
29527         this.endDistance = Math.sqrt(x + y);
29528         
29529         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
29530         
29531         if(!this.zoomable()){
29532             this.scale = this.startScale;
29533             return;
29534         }
29535         
29536         this.draw();
29537         
29538     },
29539     
29540     onTouchEnd : function(e)
29541     {
29542         this.pinching = false;
29543         this.dragable = false;
29544         
29545     },
29546     
29547     process : function(file, crop)
29548     {
29549         if(this.loadMask){
29550             this.maskEl.mask(this.loadingText);
29551         }
29552         
29553         this.xhr = new XMLHttpRequest();
29554         
29555         file.xhr = this.xhr;
29556
29557         this.xhr.open(this.method, this.url, true);
29558         
29559         var headers = {
29560             "Accept": "application/json",
29561             "Cache-Control": "no-cache",
29562             "X-Requested-With": "XMLHttpRequest"
29563         };
29564         
29565         for (var headerName in headers) {
29566             var headerValue = headers[headerName];
29567             if (headerValue) {
29568                 this.xhr.setRequestHeader(headerName, headerValue);
29569             }
29570         }
29571         
29572         var _this = this;
29573         
29574         this.xhr.onload = function()
29575         {
29576             _this.xhrOnLoad(_this.xhr);
29577         }
29578         
29579         this.xhr.onerror = function()
29580         {
29581             _this.xhrOnError(_this.xhr);
29582         }
29583         
29584         var formData = new FormData();
29585
29586         formData.append('returnHTML', 'NO');
29587         
29588         if(crop){
29589             formData.append('crop', crop);
29590         }
29591         
29592         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
29593             formData.append(this.paramName, file, file.name);
29594         }
29595         
29596         if(typeof(file.filename) != 'undefined'){
29597             formData.append('filename', file.filename);
29598         }
29599         
29600         if(typeof(file.mimetype) != 'undefined'){
29601             formData.append('mimetype', file.mimetype);
29602         }
29603         
29604         if(this.fireEvent('arrange', this, formData) != false){
29605             this.xhr.send(formData);
29606         };
29607     },
29608     
29609     xhrOnLoad : function(xhr)
29610     {
29611         if(this.loadMask){
29612             this.maskEl.unmask();
29613         }
29614         
29615         if (xhr.readyState !== 4) {
29616             this.fireEvent('exception', this, xhr);
29617             return;
29618         }
29619
29620         var response = Roo.decode(xhr.responseText);
29621         
29622         if(!response.success){
29623             this.fireEvent('exception', this, xhr);
29624             return;
29625         }
29626         
29627         var response = Roo.decode(xhr.responseText);
29628         
29629         this.fireEvent('upload', this, response);
29630         
29631     },
29632     
29633     xhrOnError : function()
29634     {
29635         if(this.loadMask){
29636             this.maskEl.unmask();
29637         }
29638         
29639         Roo.log('xhr on error');
29640         
29641         var response = Roo.decode(xhr.responseText);
29642           
29643         Roo.log(response);
29644         
29645     },
29646     
29647     prepare : function(file)
29648     {   
29649         if(this.loadMask){
29650             this.maskEl.mask(this.loadingText);
29651         }
29652         
29653         this.file = false;
29654         this.exif = {};
29655         
29656         if(typeof(file) === 'string'){
29657             this.loadCanvas(file);
29658             return;
29659         }
29660         
29661         if(!file || !this.urlAPI){
29662             return;
29663         }
29664         
29665         this.file = file;
29666         this.cropType = file.type;
29667         
29668         var _this = this;
29669         
29670         if(this.fireEvent('prepare', this, this.file) != false){
29671             
29672             var reader = new FileReader();
29673             
29674             reader.onload = function (e) {
29675                 if (e.target.error) {
29676                     Roo.log(e.target.error);
29677                     return;
29678                 }
29679                 
29680                 var buffer = e.target.result,
29681                     dataView = new DataView(buffer),
29682                     offset = 2,
29683                     maxOffset = dataView.byteLength - 4,
29684                     markerBytes,
29685                     markerLength;
29686                 
29687                 if (dataView.getUint16(0) === 0xffd8) {
29688                     while (offset < maxOffset) {
29689                         markerBytes = dataView.getUint16(offset);
29690                         
29691                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
29692                             markerLength = dataView.getUint16(offset + 2) + 2;
29693                             if (offset + markerLength > dataView.byteLength) {
29694                                 Roo.log('Invalid meta data: Invalid segment size.');
29695                                 break;
29696                             }
29697                             
29698                             if(markerBytes == 0xffe1){
29699                                 _this.parseExifData(
29700                                     dataView,
29701                                     offset,
29702                                     markerLength
29703                                 );
29704                             }
29705                             
29706                             offset += markerLength;
29707                             
29708                             continue;
29709                         }
29710                         
29711                         break;
29712                     }
29713                     
29714                 }
29715                 
29716                 var url = _this.urlAPI.createObjectURL(_this.file);
29717                 
29718                 _this.loadCanvas(url);
29719                 
29720                 return;
29721             }
29722             
29723             reader.readAsArrayBuffer(this.file);
29724             
29725         }
29726         
29727     },
29728     
29729     parseExifData : function(dataView, offset, length)
29730     {
29731         var tiffOffset = offset + 10,
29732             littleEndian,
29733             dirOffset;
29734     
29735         if (dataView.getUint32(offset + 4) !== 0x45786966) {
29736             // No Exif data, might be XMP data instead
29737             return;
29738         }
29739         
29740         // Check for the ASCII code for "Exif" (0x45786966):
29741         if (dataView.getUint32(offset + 4) !== 0x45786966) {
29742             // No Exif data, might be XMP data instead
29743             return;
29744         }
29745         if (tiffOffset + 8 > dataView.byteLength) {
29746             Roo.log('Invalid Exif data: Invalid segment size.');
29747             return;
29748         }
29749         // Check for the two null bytes:
29750         if (dataView.getUint16(offset + 8) !== 0x0000) {
29751             Roo.log('Invalid Exif data: Missing byte alignment offset.');
29752             return;
29753         }
29754         // Check the byte alignment:
29755         switch (dataView.getUint16(tiffOffset)) {
29756         case 0x4949:
29757             littleEndian = true;
29758             break;
29759         case 0x4D4D:
29760             littleEndian = false;
29761             break;
29762         default:
29763             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
29764             return;
29765         }
29766         // Check for the TIFF tag marker (0x002A):
29767         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
29768             Roo.log('Invalid Exif data: Missing TIFF marker.');
29769             return;
29770         }
29771         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
29772         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
29773         
29774         this.parseExifTags(
29775             dataView,
29776             tiffOffset,
29777             tiffOffset + dirOffset,
29778             littleEndian
29779         );
29780     },
29781     
29782     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
29783     {
29784         var tagsNumber,
29785             dirEndOffset,
29786             i;
29787         if (dirOffset + 6 > dataView.byteLength) {
29788             Roo.log('Invalid Exif data: Invalid directory offset.');
29789             return;
29790         }
29791         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
29792         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
29793         if (dirEndOffset + 4 > dataView.byteLength) {
29794             Roo.log('Invalid Exif data: Invalid directory size.');
29795             return;
29796         }
29797         for (i = 0; i < tagsNumber; i += 1) {
29798             this.parseExifTag(
29799                 dataView,
29800                 tiffOffset,
29801                 dirOffset + 2 + 12 * i, // tag offset
29802                 littleEndian
29803             );
29804         }
29805         // Return the offset to the next directory:
29806         return dataView.getUint32(dirEndOffset, littleEndian);
29807     },
29808     
29809     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
29810     {
29811         var tag = dataView.getUint16(offset, littleEndian);
29812         
29813         this.exif[tag] = this.getExifValue(
29814             dataView,
29815             tiffOffset,
29816             offset,
29817             dataView.getUint16(offset + 2, littleEndian), // tag type
29818             dataView.getUint32(offset + 4, littleEndian), // tag length
29819             littleEndian
29820         );
29821     },
29822     
29823     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
29824     {
29825         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
29826             tagSize,
29827             dataOffset,
29828             values,
29829             i,
29830             str,
29831             c;
29832     
29833         if (!tagType) {
29834             Roo.log('Invalid Exif data: Invalid tag type.');
29835             return;
29836         }
29837         
29838         tagSize = tagType.size * length;
29839         // Determine if the value is contained in the dataOffset bytes,
29840         // or if the value at the dataOffset is a pointer to the actual data:
29841         dataOffset = tagSize > 4 ?
29842                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
29843         if (dataOffset + tagSize > dataView.byteLength) {
29844             Roo.log('Invalid Exif data: Invalid data offset.');
29845             return;
29846         }
29847         if (length === 1) {
29848             return tagType.getValue(dataView, dataOffset, littleEndian);
29849         }
29850         values = [];
29851         for (i = 0; i < length; i += 1) {
29852             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
29853         }
29854         
29855         if (tagType.ascii) {
29856             str = '';
29857             // Concatenate the chars:
29858             for (i = 0; i < values.length; i += 1) {
29859                 c = values[i];
29860                 // Ignore the terminating NULL byte(s):
29861                 if (c === '\u0000') {
29862                     break;
29863                 }
29864                 str += c;
29865             }
29866             return str;
29867         }
29868         return values;
29869     }
29870     
29871 });
29872
29873 Roo.apply(Roo.bootstrap.UploadCropbox, {
29874     tags : {
29875         'Orientation': 0x0112
29876     },
29877     
29878     Orientation: {
29879             1: 0, //'top-left',
29880 //            2: 'top-right',
29881             3: 180, //'bottom-right',
29882 //            4: 'bottom-left',
29883 //            5: 'left-top',
29884             6: 90, //'right-top',
29885 //            7: 'right-bottom',
29886             8: 270 //'left-bottom'
29887     },
29888     
29889     exifTagTypes : {
29890         // byte, 8-bit unsigned int:
29891         1: {
29892             getValue: function (dataView, dataOffset) {
29893                 return dataView.getUint8(dataOffset);
29894             },
29895             size: 1
29896         },
29897         // ascii, 8-bit byte:
29898         2: {
29899             getValue: function (dataView, dataOffset) {
29900                 return String.fromCharCode(dataView.getUint8(dataOffset));
29901             },
29902             size: 1,
29903             ascii: true
29904         },
29905         // short, 16 bit int:
29906         3: {
29907             getValue: function (dataView, dataOffset, littleEndian) {
29908                 return dataView.getUint16(dataOffset, littleEndian);
29909             },
29910             size: 2
29911         },
29912         // long, 32 bit int:
29913         4: {
29914             getValue: function (dataView, dataOffset, littleEndian) {
29915                 return dataView.getUint32(dataOffset, littleEndian);
29916             },
29917             size: 4
29918         },
29919         // rational = two long values, first is numerator, second is denominator:
29920         5: {
29921             getValue: function (dataView, dataOffset, littleEndian) {
29922                 return dataView.getUint32(dataOffset, littleEndian) /
29923                     dataView.getUint32(dataOffset + 4, littleEndian);
29924             },
29925             size: 8
29926         },
29927         // slong, 32 bit signed int:
29928         9: {
29929             getValue: function (dataView, dataOffset, littleEndian) {
29930                 return dataView.getInt32(dataOffset, littleEndian);
29931             },
29932             size: 4
29933         },
29934         // srational, two slongs, first is numerator, second is denominator:
29935         10: {
29936             getValue: function (dataView, dataOffset, littleEndian) {
29937                 return dataView.getInt32(dataOffset, littleEndian) /
29938                     dataView.getInt32(dataOffset + 4, littleEndian);
29939             },
29940             size: 8
29941         }
29942     },
29943     
29944     footer : {
29945         STANDARD : [
29946             {
29947                 tag : 'div',
29948                 cls : 'btn-group roo-upload-cropbox-rotate-left',
29949                 action : 'rotate-left',
29950                 cn : [
29951                     {
29952                         tag : 'button',
29953                         cls : 'btn btn-default',
29954                         html : '<i class="fa fa-undo"></i>'
29955                     }
29956                 ]
29957             },
29958             {
29959                 tag : 'div',
29960                 cls : 'btn-group roo-upload-cropbox-picture',
29961                 action : 'picture',
29962                 cn : [
29963                     {
29964                         tag : 'button',
29965                         cls : 'btn btn-default',
29966                         html : '<i class="fa fa-picture-o"></i>'
29967                     }
29968                 ]
29969             },
29970             {
29971                 tag : 'div',
29972                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29973                 action : 'rotate-right',
29974                 cn : [
29975                     {
29976                         tag : 'button',
29977                         cls : 'btn btn-default',
29978                         html : '<i class="fa fa-repeat"></i>'
29979                     }
29980                 ]
29981             }
29982         ],
29983         DOCUMENT : [
29984             {
29985                 tag : 'div',
29986                 cls : 'btn-group roo-upload-cropbox-rotate-left',
29987                 action : 'rotate-left',
29988                 cn : [
29989                     {
29990                         tag : 'button',
29991                         cls : 'btn btn-default',
29992                         html : '<i class="fa fa-undo"></i>'
29993                     }
29994                 ]
29995             },
29996             {
29997                 tag : 'div',
29998                 cls : 'btn-group roo-upload-cropbox-download',
29999                 action : 'download',
30000                 cn : [
30001                     {
30002                         tag : 'button',
30003                         cls : 'btn btn-default',
30004                         html : '<i class="fa fa-download"></i>'
30005                     }
30006                 ]
30007             },
30008             {
30009                 tag : 'div',
30010                 cls : 'btn-group roo-upload-cropbox-crop',
30011                 action : 'crop',
30012                 cn : [
30013                     {
30014                         tag : 'button',
30015                         cls : 'btn btn-default',
30016                         html : '<i class="fa fa-crop"></i>'
30017                     }
30018                 ]
30019             },
30020             {
30021                 tag : 'div',
30022                 cls : 'btn-group roo-upload-cropbox-trash',
30023                 action : 'trash',
30024                 cn : [
30025                     {
30026                         tag : 'button',
30027                         cls : 'btn btn-default',
30028                         html : '<i class="fa fa-trash"></i>'
30029                     }
30030                 ]
30031             },
30032             {
30033                 tag : 'div',
30034                 cls : 'btn-group roo-upload-cropbox-rotate-right',
30035                 action : 'rotate-right',
30036                 cn : [
30037                     {
30038                         tag : 'button',
30039                         cls : 'btn btn-default',
30040                         html : '<i class="fa fa-repeat"></i>'
30041                     }
30042                 ]
30043             }
30044         ],
30045         ROTATOR : [
30046             {
30047                 tag : 'div',
30048                 cls : 'btn-group roo-upload-cropbox-rotate-left',
30049                 action : 'rotate-left',
30050                 cn : [
30051                     {
30052                         tag : 'button',
30053                         cls : 'btn btn-default',
30054                         html : '<i class="fa fa-undo"></i>'
30055                     }
30056                 ]
30057             },
30058             {
30059                 tag : 'div',
30060                 cls : 'btn-group roo-upload-cropbox-rotate-right',
30061                 action : 'rotate-right',
30062                 cn : [
30063                     {
30064                         tag : 'button',
30065                         cls : 'btn btn-default',
30066                         html : '<i class="fa fa-repeat"></i>'
30067                     }
30068                 ]
30069             }
30070         ]
30071     }
30072 });
30073
30074 /*
30075 * Licence: LGPL
30076 */
30077
30078 /**
30079  * @class Roo.bootstrap.DocumentManager
30080  * @extends Roo.bootstrap.Component
30081  * Bootstrap DocumentManager class
30082  * @cfg {String} paramName default 'imageUpload'
30083  * @cfg {String} toolTipName default 'filename'
30084  * @cfg {String} method default POST
30085  * @cfg {String} url action url
30086  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
30087  * @cfg {Boolean} multiple multiple upload default true
30088  * @cfg {Number} thumbSize default 300
30089  * @cfg {String} fieldLabel
30090  * @cfg {Number} labelWidth default 4
30091  * @cfg {String} labelAlign (left|top) default left
30092  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
30093 * @cfg {Number} labellg set the width of label (1-12)
30094  * @cfg {Number} labelmd set the width of label (1-12)
30095  * @cfg {Number} labelsm set the width of label (1-12)
30096  * @cfg {Number} labelxs set the width of label (1-12)
30097  * 
30098  * @constructor
30099  * Create a new DocumentManager
30100  * @param {Object} config The config object
30101  */
30102
30103 Roo.bootstrap.DocumentManager = function(config){
30104     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
30105     
30106     this.files = [];
30107     this.delegates = [];
30108     
30109     this.addEvents({
30110         /**
30111          * @event initial
30112          * Fire when initial the DocumentManager
30113          * @param {Roo.bootstrap.DocumentManager} this
30114          */
30115         "initial" : true,
30116         /**
30117          * @event inspect
30118          * inspect selected file
30119          * @param {Roo.bootstrap.DocumentManager} this
30120          * @param {File} file
30121          */
30122         "inspect" : true,
30123         /**
30124          * @event exception
30125          * Fire when xhr load exception
30126          * @param {Roo.bootstrap.DocumentManager} this
30127          * @param {XMLHttpRequest} xhr
30128          */
30129         "exception" : true,
30130         /**
30131          * @event afterupload
30132          * Fire when xhr load exception
30133          * @param {Roo.bootstrap.DocumentManager} this
30134          * @param {XMLHttpRequest} xhr
30135          */
30136         "afterupload" : true,
30137         /**
30138          * @event prepare
30139          * prepare the form data
30140          * @param {Roo.bootstrap.DocumentManager} this
30141          * @param {Object} formData
30142          */
30143         "prepare" : true,
30144         /**
30145          * @event remove
30146          * Fire when remove the file
30147          * @param {Roo.bootstrap.DocumentManager} this
30148          * @param {Object} file
30149          */
30150         "remove" : true,
30151         /**
30152          * @event refresh
30153          * Fire after refresh the file
30154          * @param {Roo.bootstrap.DocumentManager} this
30155          */
30156         "refresh" : true,
30157         /**
30158          * @event click
30159          * Fire after click the image
30160          * @param {Roo.bootstrap.DocumentManager} this
30161          * @param {Object} file
30162          */
30163         "click" : true,
30164         /**
30165          * @event edit
30166          * Fire when upload a image and editable set to true
30167          * @param {Roo.bootstrap.DocumentManager} this
30168          * @param {Object} file
30169          */
30170         "edit" : true,
30171         /**
30172          * @event beforeselectfile
30173          * Fire before select file
30174          * @param {Roo.bootstrap.DocumentManager} this
30175          */
30176         "beforeselectfile" : true,
30177         /**
30178          * @event process
30179          * Fire before process file
30180          * @param {Roo.bootstrap.DocumentManager} this
30181          * @param {Object} file
30182          */
30183         "process" : true,
30184         /**
30185          * @event previewrendered
30186          * Fire when preview rendered
30187          * @param {Roo.bootstrap.DocumentManager} this
30188          * @param {Object} file
30189          */
30190         "previewrendered" : true,
30191         /**
30192          */
30193         "previewResize" : true
30194         
30195     });
30196 };
30197
30198 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
30199     
30200     boxes : 0,
30201     inputName : '',
30202     thumbSize : 300,
30203     multiple : true,
30204     files : false,
30205     method : 'POST',
30206     url : '',
30207     paramName : 'imageUpload',
30208     toolTipName : 'filename',
30209     fieldLabel : '',
30210     labelWidth : 4,
30211     labelAlign : 'left',
30212     editable : true,
30213     delegates : false,
30214     xhr : false, 
30215     
30216     labellg : 0,
30217     labelmd : 0,
30218     labelsm : 0,
30219     labelxs : 0,
30220     
30221     getAutoCreate : function()
30222     {   
30223         var managerWidget = {
30224             tag : 'div',
30225             cls : 'roo-document-manager',
30226             cn : [
30227                 {
30228                     tag : 'input',
30229                     cls : 'roo-document-manager-selector',
30230                     type : 'file'
30231                 },
30232                 {
30233                     tag : 'div',
30234                     cls : 'roo-document-manager-uploader',
30235                     cn : [
30236                         {
30237                             tag : 'div',
30238                             cls : 'roo-document-manager-upload-btn',
30239                             html : '<i class="fa fa-plus"></i>'
30240                         }
30241                     ]
30242                     
30243                 }
30244             ]
30245         };
30246         
30247         var content = [
30248             {
30249                 tag : 'div',
30250                 cls : 'column col-md-12',
30251                 cn : managerWidget
30252             }
30253         ];
30254         
30255         if(this.fieldLabel.length){
30256             
30257             content = [
30258                 {
30259                     tag : 'div',
30260                     cls : 'column col-md-12',
30261                     html : this.fieldLabel
30262                 },
30263                 {
30264                     tag : 'div',
30265                     cls : 'column col-md-12',
30266                     cn : managerWidget
30267                 }
30268             ];
30269
30270             if(this.labelAlign == 'left'){
30271                 content = [
30272                     {
30273                         tag : 'div',
30274                         cls : 'column',
30275                         html : this.fieldLabel
30276                     },
30277                     {
30278                         tag : 'div',
30279                         cls : 'column',
30280                         cn : managerWidget
30281                     }
30282                 ];
30283                 
30284                 if(this.labelWidth > 12){
30285                     content[0].style = "width: " + this.labelWidth + 'px';
30286                 }
30287
30288                 if(this.labelWidth < 13 && this.labelmd == 0){
30289                     this.labelmd = this.labelWidth;
30290                 }
30291
30292                 if(this.labellg > 0){
30293                     content[0].cls += ' col-lg-' + this.labellg;
30294                     content[1].cls += ' col-lg-' + (12 - this.labellg);
30295                 }
30296
30297                 if(this.labelmd > 0){
30298                     content[0].cls += ' col-md-' + this.labelmd;
30299                     content[1].cls += ' col-md-' + (12 - this.labelmd);
30300                 }
30301
30302                 if(this.labelsm > 0){
30303                     content[0].cls += ' col-sm-' + this.labelsm;
30304                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
30305                 }
30306
30307                 if(this.labelxs > 0){
30308                     content[0].cls += ' col-xs-' + this.labelxs;
30309                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
30310                 }
30311                 
30312             }
30313         }
30314         
30315         var cfg = {
30316             tag : 'div',
30317             cls : 'row clearfix',
30318             cn : content
30319         };
30320         
30321         return cfg;
30322         
30323     },
30324     
30325     initEvents : function()
30326     {
30327         this.managerEl = this.el.select('.roo-document-manager', true).first();
30328         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30329         
30330         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
30331         this.selectorEl.hide();
30332         
30333         if(this.multiple){
30334             this.selectorEl.attr('multiple', 'multiple');
30335         }
30336         
30337         this.selectorEl.on('change', this.onFileSelected, this);
30338         
30339         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
30340         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30341         
30342         this.uploader.on('click', this.onUploaderClick, this);
30343         
30344         this.renderProgressDialog();
30345         
30346         var _this = this;
30347         
30348         window.addEventListener("resize", function() { _this.refresh(); } );
30349         
30350         this.fireEvent('initial', this);
30351     },
30352     
30353     renderProgressDialog : function()
30354     {
30355         var _this = this;
30356         
30357         this.progressDialog = new Roo.bootstrap.Modal({
30358             cls : 'roo-document-manager-progress-dialog',
30359             allow_close : false,
30360             animate : false,
30361             title : '',
30362             buttons : [
30363                 {
30364                     name  :'cancel',
30365                     weight : 'danger',
30366                     html : 'Cancel'
30367                 }
30368             ], 
30369             listeners : { 
30370                 btnclick : function() {
30371                     _this.uploadCancel();
30372                     this.hide();
30373                 }
30374             }
30375         });
30376          
30377         this.progressDialog.render(Roo.get(document.body));
30378          
30379         this.progress = new Roo.bootstrap.Progress({
30380             cls : 'roo-document-manager-progress',
30381             active : true,
30382             striped : true
30383         });
30384         
30385         this.progress.render(this.progressDialog.getChildContainer());
30386         
30387         this.progressBar = new Roo.bootstrap.ProgressBar({
30388             cls : 'roo-document-manager-progress-bar',
30389             aria_valuenow : 0,
30390             aria_valuemin : 0,
30391             aria_valuemax : 12,
30392             panel : 'success'
30393         });
30394         
30395         this.progressBar.render(this.progress.getChildContainer());
30396     },
30397     
30398     onUploaderClick : function(e)
30399     {
30400         e.preventDefault();
30401      
30402         if(this.fireEvent('beforeselectfile', this) != false){
30403             this.selectorEl.dom.click();
30404         }
30405         
30406     },
30407     
30408     onFileSelected : function(e)
30409     {
30410         e.preventDefault();
30411         
30412         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30413             return;
30414         }
30415         
30416         Roo.each(this.selectorEl.dom.files, function(file){
30417             if(this.fireEvent('inspect', this, file) != false){
30418                 this.files.push(file);
30419             }
30420         }, this);
30421         
30422         this.queue();
30423         
30424     },
30425     
30426     queue : function()
30427     {
30428         this.selectorEl.dom.value = '';
30429         
30430         if(!this.files || !this.files.length){
30431             return;
30432         }
30433         
30434         if(this.boxes > 0 && this.files.length > this.boxes){
30435             this.files = this.files.slice(0, this.boxes);
30436         }
30437         
30438         this.uploader.show();
30439         
30440         if(this.boxes > 0 && this.files.length > this.boxes - 1){
30441             this.uploader.hide();
30442         }
30443         
30444         var _this = this;
30445         
30446         var files = [];
30447         
30448         var docs = [];
30449         
30450         Roo.each(this.files, function(file){
30451             
30452             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
30453                 var f = this.renderPreview(file);
30454                 files.push(f);
30455                 return;
30456             }
30457             
30458             if(file.type.indexOf('image') != -1){
30459                 this.delegates.push(
30460                     (function(){
30461                         _this.process(file);
30462                     }).createDelegate(this)
30463                 );
30464         
30465                 return;
30466             }
30467             
30468             docs.push(
30469                 (function(){
30470                     _this.process(file);
30471                 }).createDelegate(this)
30472             );
30473             
30474         }, this);
30475         
30476         this.files = files;
30477         
30478         this.delegates = this.delegates.concat(docs);
30479         
30480         if(!this.delegates.length){
30481             this.refresh();
30482             return;
30483         }
30484         
30485         this.progressBar.aria_valuemax = this.delegates.length;
30486         
30487         this.arrange();
30488         
30489         return;
30490     },
30491     
30492     arrange : function()
30493     {
30494         if(!this.delegates.length){
30495             this.progressDialog.hide();
30496             this.refresh();
30497             return;
30498         }
30499         
30500         var delegate = this.delegates.shift();
30501         
30502         this.progressDialog.show();
30503         
30504         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
30505         
30506         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
30507         
30508         delegate();
30509     },
30510     
30511     refresh : function()
30512     {
30513         this.uploader.show();
30514         
30515         if(this.boxes > 0 && this.files.length > this.boxes - 1){
30516             this.uploader.hide();
30517         }
30518         
30519         Roo.isTouch ? this.closable(false) : this.closable(true);
30520         
30521         this.fireEvent('refresh', this);
30522     },
30523     
30524     onRemove : function(e, el, o)
30525     {
30526         e.preventDefault();
30527         
30528         this.fireEvent('remove', this, o);
30529         
30530     },
30531     
30532     remove : function(o)
30533     {
30534         var files = [];
30535         
30536         Roo.each(this.files, function(file){
30537             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
30538                 files.push(file);
30539                 return;
30540             }
30541
30542             o.target.remove();
30543
30544         }, this);
30545         
30546         this.files = files;
30547         
30548         this.refresh();
30549     },
30550     
30551     clear : function()
30552     {
30553         Roo.each(this.files, function(file){
30554             if(!file.target){
30555                 return;
30556             }
30557             
30558             file.target.remove();
30559
30560         }, this);
30561         
30562         this.files = [];
30563         
30564         this.refresh();
30565     },
30566     
30567     onClick : function(e, el, o)
30568     {
30569         e.preventDefault();
30570         
30571         this.fireEvent('click', this, o);
30572         
30573     },
30574     
30575     closable : function(closable)
30576     {
30577         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
30578             
30579             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30580             
30581             if(closable){
30582                 el.show();
30583                 return;
30584             }
30585             
30586             el.hide();
30587             
30588         }, this);
30589     },
30590     
30591     xhrOnLoad : function(xhr)
30592     {
30593         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
30594             el.remove();
30595         }, this);
30596         
30597         if (xhr.readyState !== 4) {
30598             this.arrange();
30599             this.fireEvent('exception', this, xhr);
30600             return;
30601         }
30602
30603         var response = Roo.decode(xhr.responseText);
30604         
30605         if(!response.success){
30606             this.arrange();
30607             this.fireEvent('exception', this, xhr);
30608             return;
30609         }
30610         
30611         var file = this.renderPreview(response.data);
30612         
30613         this.files.push(file);
30614         
30615         this.arrange();
30616         
30617         this.fireEvent('afterupload', this, xhr);
30618         
30619     },
30620     
30621     xhrOnError : function(xhr)
30622     {
30623         Roo.log('xhr on error');
30624         
30625         var response = Roo.decode(xhr.responseText);
30626           
30627         Roo.log(response);
30628         
30629         this.arrange();
30630     },
30631     
30632     process : function(file)
30633     {
30634         if(this.fireEvent('process', this, file) !== false){
30635             if(this.editable && file.type.indexOf('image') != -1){
30636                 this.fireEvent('edit', this, file);
30637                 return;
30638             }
30639
30640             this.uploadStart(file, false);
30641
30642             return;
30643         }
30644         
30645     },
30646     
30647     uploadStart : function(file, crop)
30648     {
30649         this.xhr = new XMLHttpRequest();
30650         
30651         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
30652             this.arrange();
30653             return;
30654         }
30655         
30656         file.xhr = this.xhr;
30657             
30658         this.managerEl.createChild({
30659             tag : 'div',
30660             cls : 'roo-document-manager-loading',
30661             cn : [
30662                 {
30663                     tag : 'div',
30664                     tooltip : file.name,
30665                     cls : 'roo-document-manager-thumb',
30666                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
30667                 }
30668             ]
30669
30670         });
30671
30672         this.xhr.open(this.method, this.url, true);
30673         
30674         var headers = {
30675             "Accept": "application/json",
30676             "Cache-Control": "no-cache",
30677             "X-Requested-With": "XMLHttpRequest"
30678         };
30679         
30680         for (var headerName in headers) {
30681             var headerValue = headers[headerName];
30682             if (headerValue) {
30683                 this.xhr.setRequestHeader(headerName, headerValue);
30684             }
30685         }
30686         
30687         var _this = this;
30688         
30689         this.xhr.onload = function()
30690         {
30691             _this.xhrOnLoad(_this.xhr);
30692         }
30693         
30694         this.xhr.onerror = function()
30695         {
30696             _this.xhrOnError(_this.xhr);
30697         }
30698         
30699         var formData = new FormData();
30700
30701         formData.append('returnHTML', 'NO');
30702         
30703         if(crop){
30704             formData.append('crop', crop);
30705         }
30706         
30707         formData.append(this.paramName, file, file.name);
30708         
30709         var options = {
30710             file : file, 
30711             manually : false
30712         };
30713         
30714         if(this.fireEvent('prepare', this, formData, options) != false){
30715             
30716             if(options.manually){
30717                 return;
30718             }
30719             
30720             this.xhr.send(formData);
30721             return;
30722         };
30723         
30724         this.uploadCancel();
30725     },
30726     
30727     uploadCancel : function()
30728     {
30729         if (this.xhr) {
30730             this.xhr.abort();
30731         }
30732         
30733         this.delegates = [];
30734         
30735         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
30736             el.remove();
30737         }, this);
30738         
30739         this.arrange();
30740     },
30741     
30742     renderPreview : function(file)
30743     {
30744         if(typeof(file.target) != 'undefined' && file.target){
30745             return file;
30746         }
30747         
30748         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
30749         
30750         var previewEl = this.managerEl.createChild({
30751             tag : 'div',
30752             cls : 'roo-document-manager-preview',
30753             cn : [
30754                 {
30755                     tag : 'div',
30756                     tooltip : file[this.toolTipName],
30757                     cls : 'roo-document-manager-thumb',
30758                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
30759                 },
30760                 {
30761                     tag : 'button',
30762                     cls : 'close',
30763                     html : '<i class="fa fa-times-circle"></i>'
30764                 }
30765             ]
30766         });
30767
30768         var close = previewEl.select('button.close', true).first();
30769
30770         close.on('click', this.onRemove, this, file);
30771
30772         file.target = previewEl;
30773
30774         var image = previewEl.select('img', true).first();
30775         
30776         var _this = this;
30777         
30778         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
30779         
30780         image.on('click', this.onClick, this, file);
30781         
30782         this.fireEvent('previewrendered', this, file);
30783         
30784         return file;
30785         
30786     },
30787     
30788     onPreviewLoad : function(file, image)
30789     {
30790         if(typeof(file.target) == 'undefined' || !file.target){
30791             return;
30792         }
30793         
30794         var width = image.dom.naturalWidth || image.dom.width;
30795         var height = image.dom.naturalHeight || image.dom.height;
30796         
30797         if(!this.previewResize) {
30798             return;
30799         }
30800         
30801         if(width > height){
30802             file.target.addClass('wide');
30803             return;
30804         }
30805         
30806         file.target.addClass('tall');
30807         return;
30808         
30809     },
30810     
30811     uploadFromSource : function(file, crop)
30812     {
30813         this.xhr = new XMLHttpRequest();
30814         
30815         this.managerEl.createChild({
30816             tag : 'div',
30817             cls : 'roo-document-manager-loading',
30818             cn : [
30819                 {
30820                     tag : 'div',
30821                     tooltip : file.name,
30822                     cls : 'roo-document-manager-thumb',
30823                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
30824                 }
30825             ]
30826
30827         });
30828
30829         this.xhr.open(this.method, this.url, true);
30830         
30831         var headers = {
30832             "Accept": "application/json",
30833             "Cache-Control": "no-cache",
30834             "X-Requested-With": "XMLHttpRequest"
30835         };
30836         
30837         for (var headerName in headers) {
30838             var headerValue = headers[headerName];
30839             if (headerValue) {
30840                 this.xhr.setRequestHeader(headerName, headerValue);
30841             }
30842         }
30843         
30844         var _this = this;
30845         
30846         this.xhr.onload = function()
30847         {
30848             _this.xhrOnLoad(_this.xhr);
30849         }
30850         
30851         this.xhr.onerror = function()
30852         {
30853             _this.xhrOnError(_this.xhr);
30854         }
30855         
30856         var formData = new FormData();
30857
30858         formData.append('returnHTML', 'NO');
30859         
30860         formData.append('crop', crop);
30861         
30862         if(typeof(file.filename) != 'undefined'){
30863             formData.append('filename', file.filename);
30864         }
30865         
30866         if(typeof(file.mimetype) != 'undefined'){
30867             formData.append('mimetype', file.mimetype);
30868         }
30869         
30870         Roo.log(formData);
30871         
30872         if(this.fireEvent('prepare', this, formData) != false){
30873             this.xhr.send(formData);
30874         };
30875     }
30876 });
30877
30878 /*
30879 * Licence: LGPL
30880 */
30881
30882 /**
30883  * @class Roo.bootstrap.DocumentViewer
30884  * @extends Roo.bootstrap.Component
30885  * Bootstrap DocumentViewer class
30886  * @cfg {Boolean} showDownload (true|false) show download button (default true)
30887  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
30888  * 
30889  * @constructor
30890  * Create a new DocumentViewer
30891  * @param {Object} config The config object
30892  */
30893
30894 Roo.bootstrap.DocumentViewer = function(config){
30895     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
30896     
30897     this.addEvents({
30898         /**
30899          * @event initial
30900          * Fire after initEvent
30901          * @param {Roo.bootstrap.DocumentViewer} this
30902          */
30903         "initial" : true,
30904         /**
30905          * @event click
30906          * Fire after click
30907          * @param {Roo.bootstrap.DocumentViewer} this
30908          */
30909         "click" : true,
30910         /**
30911          * @event download
30912          * Fire after download button
30913          * @param {Roo.bootstrap.DocumentViewer} this
30914          */
30915         "download" : true,
30916         /**
30917          * @event trash
30918          * Fire after trash button
30919          * @param {Roo.bootstrap.DocumentViewer} this
30920          */
30921         "trash" : true
30922         
30923     });
30924 };
30925
30926 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
30927     
30928     showDownload : true,
30929     
30930     showTrash : true,
30931     
30932     getAutoCreate : function()
30933     {
30934         var cfg = {
30935             tag : 'div',
30936             cls : 'roo-document-viewer',
30937             cn : [
30938                 {
30939                     tag : 'div',
30940                     cls : 'roo-document-viewer-body',
30941                     cn : [
30942                         {
30943                             tag : 'div',
30944                             cls : 'roo-document-viewer-thumb',
30945                             cn : [
30946                                 {
30947                                     tag : 'img',
30948                                     cls : 'roo-document-viewer-image'
30949                                 }
30950                             ]
30951                         }
30952                     ]
30953                 },
30954                 {
30955                     tag : 'div',
30956                     cls : 'roo-document-viewer-footer',
30957                     cn : {
30958                         tag : 'div',
30959                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
30960                         cn : [
30961                             {
30962                                 tag : 'div',
30963                                 cls : 'btn-group roo-document-viewer-download',
30964                                 cn : [
30965                                     {
30966                                         tag : 'button',
30967                                         cls : 'btn btn-default',
30968                                         html : '<i class="fa fa-download"></i>'
30969                                     }
30970                                 ]
30971                             },
30972                             {
30973                                 tag : 'div',
30974                                 cls : 'btn-group roo-document-viewer-trash',
30975                                 cn : [
30976                                     {
30977                                         tag : 'button',
30978                                         cls : 'btn btn-default',
30979                                         html : '<i class="fa fa-trash"></i>'
30980                                     }
30981                                 ]
30982                             }
30983                         ]
30984                     }
30985                 }
30986             ]
30987         };
30988         
30989         return cfg;
30990     },
30991     
30992     initEvents : function()
30993     {
30994         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
30995         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
30996         
30997         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
30998         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
30999         
31000         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
31001         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
31002         
31003         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
31004         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
31005         
31006         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
31007         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
31008         
31009         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
31010         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
31011         
31012         this.bodyEl.on('click', this.onClick, this);
31013         this.downloadBtn.on('click', this.onDownload, this);
31014         this.trashBtn.on('click', this.onTrash, this);
31015         
31016         this.downloadBtn.hide();
31017         this.trashBtn.hide();
31018         
31019         if(this.showDownload){
31020             this.downloadBtn.show();
31021         }
31022         
31023         if(this.showTrash){
31024             this.trashBtn.show();
31025         }
31026         
31027         if(!this.showDownload && !this.showTrash) {
31028             this.footerEl.hide();
31029         }
31030         
31031     },
31032     
31033     initial : function()
31034     {
31035         this.fireEvent('initial', this);
31036         
31037     },
31038     
31039     onClick : function(e)
31040     {
31041         e.preventDefault();
31042         
31043         this.fireEvent('click', this);
31044     },
31045     
31046     onDownload : function(e)
31047     {
31048         e.preventDefault();
31049         
31050         this.fireEvent('download', this);
31051     },
31052     
31053     onTrash : function(e)
31054     {
31055         e.preventDefault();
31056         
31057         this.fireEvent('trash', this);
31058     }
31059     
31060 });
31061 /*
31062  * - LGPL
31063  *
31064  * nav progress bar
31065  * 
31066  */
31067
31068 /**
31069  * @class Roo.bootstrap.NavProgressBar
31070  * @extends Roo.bootstrap.Component
31071  * Bootstrap NavProgressBar class
31072  * 
31073  * @constructor
31074  * Create a new nav progress bar
31075  * @param {Object} config The config object
31076  */
31077
31078 Roo.bootstrap.NavProgressBar = function(config){
31079     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
31080
31081     this.bullets = this.bullets || [];
31082    
31083 //    Roo.bootstrap.NavProgressBar.register(this);
31084      this.addEvents({
31085         /**
31086              * @event changed
31087              * Fires when the active item changes
31088              * @param {Roo.bootstrap.NavProgressBar} this
31089              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
31090              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
31091          */
31092         'changed': true
31093      });
31094     
31095 };
31096
31097 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
31098     
31099     bullets : [],
31100     barItems : [],
31101     
31102     getAutoCreate : function()
31103     {
31104         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
31105         
31106         cfg = {
31107             tag : 'div',
31108             cls : 'roo-navigation-bar-group',
31109             cn : [
31110                 {
31111                     tag : 'div',
31112                     cls : 'roo-navigation-top-bar'
31113                 },
31114                 {
31115                     tag : 'div',
31116                     cls : 'roo-navigation-bullets-bar',
31117                     cn : [
31118                         {
31119                             tag : 'ul',
31120                             cls : 'roo-navigation-bar'
31121                         }
31122                     ]
31123                 },
31124                 
31125                 {
31126                     tag : 'div',
31127                     cls : 'roo-navigation-bottom-bar'
31128                 }
31129             ]
31130             
31131         };
31132         
31133         return cfg;
31134         
31135     },
31136     
31137     initEvents: function() 
31138     {
31139         
31140     },
31141     
31142     onRender : function(ct, position) 
31143     {
31144         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
31145         
31146         if(this.bullets.length){
31147             Roo.each(this.bullets, function(b){
31148                this.addItem(b);
31149             }, this);
31150         }
31151         
31152         this.format();
31153         
31154     },
31155     
31156     addItem : function(cfg)
31157     {
31158         var item = new Roo.bootstrap.NavProgressItem(cfg);
31159         
31160         item.parentId = this.id;
31161         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
31162         
31163         if(cfg.html){
31164             var top = new Roo.bootstrap.Element({
31165                 tag : 'div',
31166                 cls : 'roo-navigation-bar-text'
31167             });
31168             
31169             var bottom = new Roo.bootstrap.Element({
31170                 tag : 'div',
31171                 cls : 'roo-navigation-bar-text'
31172             });
31173             
31174             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
31175             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
31176             
31177             var topText = new Roo.bootstrap.Element({
31178                 tag : 'span',
31179                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
31180             });
31181             
31182             var bottomText = new Roo.bootstrap.Element({
31183                 tag : 'span',
31184                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
31185             });
31186             
31187             topText.onRender(top.el, null);
31188             bottomText.onRender(bottom.el, null);
31189             
31190             item.topEl = top;
31191             item.bottomEl = bottom;
31192         }
31193         
31194         this.barItems.push(item);
31195         
31196         return item;
31197     },
31198     
31199     getActive : function()
31200     {
31201         var active = false;
31202         
31203         Roo.each(this.barItems, function(v){
31204             
31205             if (!v.isActive()) {
31206                 return;
31207             }
31208             
31209             active = v;
31210             return false;
31211             
31212         });
31213         
31214         return active;
31215     },
31216     
31217     setActiveItem : function(item)
31218     {
31219         var prev = false;
31220         
31221         Roo.each(this.barItems, function(v){
31222             if (v.rid == item.rid) {
31223                 return ;
31224             }
31225             
31226             if (v.isActive()) {
31227                 v.setActive(false);
31228                 prev = v;
31229             }
31230         });
31231
31232         item.setActive(true);
31233         
31234         this.fireEvent('changed', this, item, prev);
31235     },
31236     
31237     getBarItem: function(rid)
31238     {
31239         var ret = false;
31240         
31241         Roo.each(this.barItems, function(e) {
31242             if (e.rid != rid) {
31243                 return;
31244             }
31245             
31246             ret =  e;
31247             return false;
31248         });
31249         
31250         return ret;
31251     },
31252     
31253     indexOfItem : function(item)
31254     {
31255         var index = false;
31256         
31257         Roo.each(this.barItems, function(v, i){
31258             
31259             if (v.rid != item.rid) {
31260                 return;
31261             }
31262             
31263             index = i;
31264             return false
31265         });
31266         
31267         return index;
31268     },
31269     
31270     setActiveNext : function()
31271     {
31272         var i = this.indexOfItem(this.getActive());
31273         
31274         if (i > this.barItems.length) {
31275             return;
31276         }
31277         
31278         this.setActiveItem(this.barItems[i+1]);
31279     },
31280     
31281     setActivePrev : function()
31282     {
31283         var i = this.indexOfItem(this.getActive());
31284         
31285         if (i  < 1) {
31286             return;
31287         }
31288         
31289         this.setActiveItem(this.barItems[i-1]);
31290     },
31291     
31292     format : function()
31293     {
31294         if(!this.barItems.length){
31295             return;
31296         }
31297      
31298         var width = 100 / this.barItems.length;
31299         
31300         Roo.each(this.barItems, function(i){
31301             i.el.setStyle('width', width + '%');
31302             i.topEl.el.setStyle('width', width + '%');
31303             i.bottomEl.el.setStyle('width', width + '%');
31304         }, this);
31305         
31306     }
31307     
31308 });
31309 /*
31310  * - LGPL
31311  *
31312  * Nav Progress Item
31313  * 
31314  */
31315
31316 /**
31317  * @class Roo.bootstrap.NavProgressItem
31318  * @extends Roo.bootstrap.Component
31319  * Bootstrap NavProgressItem class
31320  * @cfg {String} rid the reference id
31321  * @cfg {Boolean} active (true|false) Is item active default false
31322  * @cfg {Boolean} disabled (true|false) Is item active default false
31323  * @cfg {String} html
31324  * @cfg {String} position (top|bottom) text position default bottom
31325  * @cfg {String} icon show icon instead of number
31326  * 
31327  * @constructor
31328  * Create a new NavProgressItem
31329  * @param {Object} config The config object
31330  */
31331 Roo.bootstrap.NavProgressItem = function(config){
31332     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
31333     this.addEvents({
31334         // raw events
31335         /**
31336          * @event click
31337          * The raw click event for the entire grid.
31338          * @param {Roo.bootstrap.NavProgressItem} this
31339          * @param {Roo.EventObject} e
31340          */
31341         "click" : true
31342     });
31343    
31344 };
31345
31346 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
31347     
31348     rid : '',
31349     active : false,
31350     disabled : false,
31351     html : '',
31352     position : 'bottom',
31353     icon : false,
31354     
31355     getAutoCreate : function()
31356     {
31357         var iconCls = 'roo-navigation-bar-item-icon';
31358         
31359         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
31360         
31361         var cfg = {
31362             tag: 'li',
31363             cls: 'roo-navigation-bar-item',
31364             cn : [
31365                 {
31366                     tag : 'i',
31367                     cls : iconCls
31368                 }
31369             ]
31370         };
31371         
31372         if(this.active){
31373             cfg.cls += ' active';
31374         }
31375         if(this.disabled){
31376             cfg.cls += ' disabled';
31377         }
31378         
31379         return cfg;
31380     },
31381     
31382     disable : function()
31383     {
31384         this.setDisabled(true);
31385     },
31386     
31387     enable : function()
31388     {
31389         this.setDisabled(false);
31390     },
31391     
31392     initEvents: function() 
31393     {
31394         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
31395         
31396         this.iconEl.on('click', this.onClick, this);
31397     },
31398     
31399     onClick : function(e)
31400     {
31401         e.preventDefault();
31402         
31403         if(this.disabled){
31404             return;
31405         }
31406         
31407         if(this.fireEvent('click', this, e) === false){
31408             return;
31409         };
31410         
31411         this.parent().setActiveItem(this);
31412     },
31413     
31414     isActive: function () 
31415     {
31416         return this.active;
31417     },
31418     
31419     setActive : function(state)
31420     {
31421         if(this.active == state){
31422             return;
31423         }
31424         
31425         this.active = state;
31426         
31427         if (state) {
31428             this.el.addClass('active');
31429             return;
31430         }
31431         
31432         this.el.removeClass('active');
31433         
31434         return;
31435     },
31436     
31437     setDisabled : function(state)
31438     {
31439         if(this.disabled == state){
31440             return;
31441         }
31442         
31443         this.disabled = state;
31444         
31445         if (state) {
31446             this.el.addClass('disabled');
31447             return;
31448         }
31449         
31450         this.el.removeClass('disabled');
31451     },
31452     
31453     tooltipEl : function()
31454     {
31455         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
31456     }
31457 });
31458  
31459
31460  /*
31461  * - LGPL
31462  *
31463  * FieldLabel
31464  * 
31465  */
31466
31467 /**
31468  * @class Roo.bootstrap.FieldLabel
31469  * @extends Roo.bootstrap.Component
31470  * Bootstrap FieldLabel class
31471  * @cfg {String} html contents of the element
31472  * @cfg {String} tag tag of the element default label
31473  * @cfg {String} cls class of the element
31474  * @cfg {String} target label target 
31475  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
31476  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
31477  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
31478  * @cfg {String} iconTooltip default "This field is required"
31479  * @cfg {String} indicatorpos (left|right) default left
31480  * 
31481  * @constructor
31482  * Create a new FieldLabel
31483  * @param {Object} config The config object
31484  */
31485
31486 Roo.bootstrap.FieldLabel = function(config){
31487     Roo.bootstrap.Element.superclass.constructor.call(this, config);
31488     
31489     this.addEvents({
31490             /**
31491              * @event invalid
31492              * Fires after the field has been marked as invalid.
31493              * @param {Roo.form.FieldLabel} this
31494              * @param {String} msg The validation message
31495              */
31496             invalid : true,
31497             /**
31498              * @event valid
31499              * Fires after the field has been validated with no errors.
31500              * @param {Roo.form.FieldLabel} this
31501              */
31502             valid : true
31503         });
31504 };
31505
31506 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
31507     
31508     tag: 'label',
31509     cls: '',
31510     html: '',
31511     target: '',
31512     allowBlank : true,
31513     invalidClass : 'has-warning',
31514     validClass : 'has-success',
31515     iconTooltip : 'This field is required',
31516     indicatorpos : 'left',
31517     
31518     getAutoCreate : function(){
31519         
31520         var cls = "";
31521         if (!this.allowBlank) {
31522             cls  = "visible";
31523         }
31524         
31525         var cfg = {
31526             tag : this.tag,
31527             cls : 'roo-bootstrap-field-label ' + this.cls,
31528             for : this.target,
31529             cn : [
31530                 {
31531                     tag : 'i',
31532                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
31533                     tooltip : this.iconTooltip
31534                 },
31535                 {
31536                     tag : 'span',
31537                     html : this.html
31538                 }
31539             ] 
31540         };
31541         
31542         if(this.indicatorpos == 'right'){
31543             var cfg = {
31544                 tag : this.tag,
31545                 cls : 'roo-bootstrap-field-label ' + this.cls,
31546                 for : this.target,
31547                 cn : [
31548                     {
31549                         tag : 'span',
31550                         html : this.html
31551                     },
31552                     {
31553                         tag : 'i',
31554                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
31555                         tooltip : this.iconTooltip
31556                     }
31557                 ] 
31558             };
31559         }
31560         
31561         return cfg;
31562     },
31563     
31564     initEvents: function() 
31565     {
31566         Roo.bootstrap.Element.superclass.initEvents.call(this);
31567         
31568         this.indicator = this.indicatorEl();
31569         
31570         if(this.indicator){
31571             this.indicator.removeClass('visible');
31572             this.indicator.addClass('invisible');
31573         }
31574         
31575         Roo.bootstrap.FieldLabel.register(this);
31576     },
31577     
31578     indicatorEl : function()
31579     {
31580         var indicator = this.el.select('i.roo-required-indicator',true).first();
31581         
31582         if(!indicator){
31583             return false;
31584         }
31585         
31586         return indicator;
31587         
31588     },
31589     
31590     /**
31591      * Mark this field as valid
31592      */
31593     markValid : function()
31594     {
31595         if(this.indicator){
31596             this.indicator.removeClass('visible');
31597             this.indicator.addClass('invisible');
31598         }
31599         if (Roo.bootstrap.version == 3) {
31600             this.el.removeClass(this.invalidClass);
31601             this.el.addClass(this.validClass);
31602         } else {
31603             this.el.removeClass('is-invalid');
31604             this.el.addClass('is-valid');
31605         }
31606         
31607         
31608         this.fireEvent('valid', this);
31609     },
31610     
31611     /**
31612      * Mark this field as invalid
31613      * @param {String} msg The validation message
31614      */
31615     markInvalid : function(msg)
31616     {
31617         if(this.indicator){
31618             this.indicator.removeClass('invisible');
31619             this.indicator.addClass('visible');
31620         }
31621           if (Roo.bootstrap.version == 3) {
31622             this.el.removeClass(this.validClass);
31623             this.el.addClass(this.invalidClass);
31624         } else {
31625             this.el.removeClass('is-valid');
31626             this.el.addClass('is-invalid');
31627         }
31628         
31629         
31630         this.fireEvent('invalid', this, msg);
31631     }
31632     
31633    
31634 });
31635
31636 Roo.apply(Roo.bootstrap.FieldLabel, {
31637     
31638     groups: {},
31639     
31640      /**
31641     * register a FieldLabel Group
31642     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
31643     */
31644     register : function(label)
31645     {
31646         if(this.groups.hasOwnProperty(label.target)){
31647             return;
31648         }
31649      
31650         this.groups[label.target] = label;
31651         
31652     },
31653     /**
31654     * fetch a FieldLabel Group based on the target
31655     * @param {string} target
31656     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
31657     */
31658     get: function(target) {
31659         if (typeof(this.groups[target]) == 'undefined') {
31660             return false;
31661         }
31662         
31663         return this.groups[target] ;
31664     }
31665 });
31666
31667  
31668
31669  /*
31670  * - LGPL
31671  *
31672  * page DateSplitField.
31673  * 
31674  */
31675
31676
31677 /**
31678  * @class Roo.bootstrap.DateSplitField
31679  * @extends Roo.bootstrap.Component
31680  * Bootstrap DateSplitField class
31681  * @cfg {string} fieldLabel - the label associated
31682  * @cfg {Number} labelWidth set the width of label (0-12)
31683  * @cfg {String} labelAlign (top|left)
31684  * @cfg {Boolean} dayAllowBlank (true|false) default false
31685  * @cfg {Boolean} monthAllowBlank (true|false) default false
31686  * @cfg {Boolean} yearAllowBlank (true|false) default false
31687  * @cfg {string} dayPlaceholder 
31688  * @cfg {string} monthPlaceholder
31689  * @cfg {string} yearPlaceholder
31690  * @cfg {string} dayFormat default 'd'
31691  * @cfg {string} monthFormat default 'm'
31692  * @cfg {string} yearFormat default 'Y'
31693  * @cfg {Number} labellg set the width of label (1-12)
31694  * @cfg {Number} labelmd set the width of label (1-12)
31695  * @cfg {Number} labelsm set the width of label (1-12)
31696  * @cfg {Number} labelxs set the width of label (1-12)
31697
31698  *     
31699  * @constructor
31700  * Create a new DateSplitField
31701  * @param {Object} config The config object
31702  */
31703
31704 Roo.bootstrap.DateSplitField = function(config){
31705     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
31706     
31707     this.addEvents({
31708         // raw events
31709          /**
31710          * @event years
31711          * getting the data of years
31712          * @param {Roo.bootstrap.DateSplitField} this
31713          * @param {Object} years
31714          */
31715         "years" : true,
31716         /**
31717          * @event days
31718          * getting the data of days
31719          * @param {Roo.bootstrap.DateSplitField} this
31720          * @param {Object} days
31721          */
31722         "days" : true,
31723         /**
31724          * @event invalid
31725          * Fires after the field has been marked as invalid.
31726          * @param {Roo.form.Field} this
31727          * @param {String} msg The validation message
31728          */
31729         invalid : true,
31730        /**
31731          * @event valid
31732          * Fires after the field has been validated with no errors.
31733          * @param {Roo.form.Field} this
31734          */
31735         valid : true
31736     });
31737 };
31738
31739 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
31740     
31741     fieldLabel : '',
31742     labelAlign : 'top',
31743     labelWidth : 3,
31744     dayAllowBlank : false,
31745     monthAllowBlank : false,
31746     yearAllowBlank : false,
31747     dayPlaceholder : '',
31748     monthPlaceholder : '',
31749     yearPlaceholder : '',
31750     dayFormat : 'd',
31751     monthFormat : 'm',
31752     yearFormat : 'Y',
31753     isFormField : true,
31754     labellg : 0,
31755     labelmd : 0,
31756     labelsm : 0,
31757     labelxs : 0,
31758     
31759     getAutoCreate : function()
31760     {
31761         var cfg = {
31762             tag : 'div',
31763             cls : 'row roo-date-split-field-group',
31764             cn : [
31765                 {
31766                     tag : 'input',
31767                     type : 'hidden',
31768                     cls : 'form-hidden-field roo-date-split-field-group-value',
31769                     name : this.name
31770                 }
31771             ]
31772         };
31773         
31774         var labelCls = 'col-md-12';
31775         var contentCls = 'col-md-4';
31776         
31777         if(this.fieldLabel){
31778             
31779             var label = {
31780                 tag : 'div',
31781                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
31782                 cn : [
31783                     {
31784                         tag : 'label',
31785                         html : this.fieldLabel
31786                     }
31787                 ]
31788             };
31789             
31790             if(this.labelAlign == 'left'){
31791             
31792                 if(this.labelWidth > 12){
31793                     label.style = "width: " + this.labelWidth + 'px';
31794                 }
31795
31796                 if(this.labelWidth < 13 && this.labelmd == 0){
31797                     this.labelmd = this.labelWidth;
31798                 }
31799
31800                 if(this.labellg > 0){
31801                     labelCls = ' col-lg-' + this.labellg;
31802                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
31803                 }
31804
31805                 if(this.labelmd > 0){
31806                     labelCls = ' col-md-' + this.labelmd;
31807                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
31808                 }
31809
31810                 if(this.labelsm > 0){
31811                     labelCls = ' col-sm-' + this.labelsm;
31812                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
31813                 }
31814
31815                 if(this.labelxs > 0){
31816                     labelCls = ' col-xs-' + this.labelxs;
31817                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
31818                 }
31819             }
31820             
31821             label.cls += ' ' + labelCls;
31822             
31823             cfg.cn.push(label);
31824         }
31825         
31826         Roo.each(['day', 'month', 'year'], function(t){
31827             cfg.cn.push({
31828                 tag : 'div',
31829                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
31830             });
31831         }, this);
31832         
31833         return cfg;
31834     },
31835     
31836     inputEl: function ()
31837     {
31838         return this.el.select('.roo-date-split-field-group-value', true).first();
31839     },
31840     
31841     onRender : function(ct, position) 
31842     {
31843         var _this = this;
31844         
31845         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
31846         
31847         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
31848         
31849         this.dayField = new Roo.bootstrap.ComboBox({
31850             allowBlank : this.dayAllowBlank,
31851             alwaysQuery : true,
31852             displayField : 'value',
31853             editable : false,
31854             fieldLabel : '',
31855             forceSelection : true,
31856             mode : 'local',
31857             placeholder : this.dayPlaceholder,
31858             selectOnFocus : true,
31859             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
31860             triggerAction : 'all',
31861             typeAhead : true,
31862             valueField : 'value',
31863             store : new Roo.data.SimpleStore({
31864                 data : (function() {    
31865                     var days = [];
31866                     _this.fireEvent('days', _this, days);
31867                     return days;
31868                 })(),
31869                 fields : [ 'value' ]
31870             }),
31871             listeners : {
31872                 select : function (_self, record, index)
31873                 {
31874                     _this.setValue(_this.getValue());
31875                 }
31876             }
31877         });
31878
31879         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
31880         
31881         this.monthField = new Roo.bootstrap.MonthField({
31882             after : '<i class=\"fa fa-calendar\"></i>',
31883             allowBlank : this.monthAllowBlank,
31884             placeholder : this.monthPlaceholder,
31885             readOnly : true,
31886             listeners : {
31887                 render : function (_self)
31888                 {
31889                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
31890                         e.preventDefault();
31891                         _self.focus();
31892                     });
31893                 },
31894                 select : function (_self, oldvalue, newvalue)
31895                 {
31896                     _this.setValue(_this.getValue());
31897                 }
31898             }
31899         });
31900         
31901         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
31902         
31903         this.yearField = new Roo.bootstrap.ComboBox({
31904             allowBlank : this.yearAllowBlank,
31905             alwaysQuery : true,
31906             displayField : 'value',
31907             editable : false,
31908             fieldLabel : '',
31909             forceSelection : true,
31910             mode : 'local',
31911             placeholder : this.yearPlaceholder,
31912             selectOnFocus : true,
31913             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
31914             triggerAction : 'all',
31915             typeAhead : true,
31916             valueField : 'value',
31917             store : new Roo.data.SimpleStore({
31918                 data : (function() {
31919                     var years = [];
31920                     _this.fireEvent('years', _this, years);
31921                     return years;
31922                 })(),
31923                 fields : [ 'value' ]
31924             }),
31925             listeners : {
31926                 select : function (_self, record, index)
31927                 {
31928                     _this.setValue(_this.getValue());
31929                 }
31930             }
31931         });
31932
31933         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
31934     },
31935     
31936     setValue : function(v, format)
31937     {
31938         this.inputEl.dom.value = v;
31939         
31940         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
31941         
31942         var d = Date.parseDate(v, f);
31943         
31944         if(!d){
31945             this.validate();
31946             return;
31947         }
31948         
31949         this.setDay(d.format(this.dayFormat));
31950         this.setMonth(d.format(this.monthFormat));
31951         this.setYear(d.format(this.yearFormat));
31952         
31953         this.validate();
31954         
31955         return;
31956     },
31957     
31958     setDay : function(v)
31959     {
31960         this.dayField.setValue(v);
31961         this.inputEl.dom.value = this.getValue();
31962         this.validate();
31963         return;
31964     },
31965     
31966     setMonth : function(v)
31967     {
31968         this.monthField.setValue(v, true);
31969         this.inputEl.dom.value = this.getValue();
31970         this.validate();
31971         return;
31972     },
31973     
31974     setYear : function(v)
31975     {
31976         this.yearField.setValue(v);
31977         this.inputEl.dom.value = this.getValue();
31978         this.validate();
31979         return;
31980     },
31981     
31982     getDay : function()
31983     {
31984         return this.dayField.getValue();
31985     },
31986     
31987     getMonth : function()
31988     {
31989         return this.monthField.getValue();
31990     },
31991     
31992     getYear : function()
31993     {
31994         return this.yearField.getValue();
31995     },
31996     
31997     getValue : function()
31998     {
31999         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
32000         
32001         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
32002         
32003         return date;
32004     },
32005     
32006     reset : function()
32007     {
32008         this.setDay('');
32009         this.setMonth('');
32010         this.setYear('');
32011         this.inputEl.dom.value = '';
32012         this.validate();
32013         return;
32014     },
32015     
32016     validate : function()
32017     {
32018         var d = this.dayField.validate();
32019         var m = this.monthField.validate();
32020         var y = this.yearField.validate();
32021         
32022         var valid = true;
32023         
32024         if(
32025                 (!this.dayAllowBlank && !d) ||
32026                 (!this.monthAllowBlank && !m) ||
32027                 (!this.yearAllowBlank && !y)
32028         ){
32029             valid = false;
32030         }
32031         
32032         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
32033             return valid;
32034         }
32035         
32036         if(valid){
32037             this.markValid();
32038             return valid;
32039         }
32040         
32041         this.markInvalid();
32042         
32043         return valid;
32044     },
32045     
32046     markValid : function()
32047     {
32048         
32049         var label = this.el.select('label', true).first();
32050         var icon = this.el.select('i.fa-star', true).first();
32051
32052         if(label && icon){
32053             icon.remove();
32054         }
32055         
32056         this.fireEvent('valid', this);
32057     },
32058     
32059      /**
32060      * Mark this field as invalid
32061      * @param {String} msg The validation message
32062      */
32063     markInvalid : function(msg)
32064     {
32065         
32066         var label = this.el.select('label', true).first();
32067         var icon = this.el.select('i.fa-star', true).first();
32068
32069         if(label && !icon){
32070             this.el.select('.roo-date-split-field-label', true).createChild({
32071                 tag : 'i',
32072                 cls : 'text-danger fa fa-lg fa-star',
32073                 tooltip : 'This field is required',
32074                 style : 'margin-right:5px;'
32075             }, label, true);
32076         }
32077         
32078         this.fireEvent('invalid', this, msg);
32079     },
32080     
32081     clearInvalid : function()
32082     {
32083         var label = this.el.select('label', true).first();
32084         var icon = this.el.select('i.fa-star', true).first();
32085
32086         if(label && icon){
32087             icon.remove();
32088         }
32089         
32090         this.fireEvent('valid', this);
32091     },
32092     
32093     getName: function()
32094     {
32095         return this.name;
32096     }
32097     
32098 });
32099
32100  /**
32101  *
32102  * This is based on 
32103  * http://masonry.desandro.com
32104  *
32105  * The idea is to render all the bricks based on vertical width...
32106  *
32107  * The original code extends 'outlayer' - we might need to use that....
32108  * 
32109  */
32110
32111
32112 /**
32113  * @class Roo.bootstrap.LayoutMasonry
32114  * @extends Roo.bootstrap.Component
32115  * Bootstrap Layout Masonry class
32116  * 
32117  * @constructor
32118  * Create a new Element
32119  * @param {Object} config The config object
32120  */
32121
32122 Roo.bootstrap.LayoutMasonry = function(config){
32123     
32124     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
32125     
32126     this.bricks = [];
32127     
32128     Roo.bootstrap.LayoutMasonry.register(this);
32129     
32130     this.addEvents({
32131         // raw events
32132         /**
32133          * @event layout
32134          * Fire after layout the items
32135          * @param {Roo.bootstrap.LayoutMasonry} this
32136          * @param {Roo.EventObject} e
32137          */
32138         "layout" : true
32139     });
32140     
32141 };
32142
32143 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
32144     
32145     /**
32146      * @cfg {Boolean} isLayoutInstant = no animation?
32147      */   
32148     isLayoutInstant : false, // needed?
32149    
32150     /**
32151      * @cfg {Number} boxWidth  width of the columns
32152      */   
32153     boxWidth : 450,
32154     
32155       /**
32156      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
32157      */   
32158     boxHeight : 0,
32159     
32160     /**
32161      * @cfg {Number} padWidth padding below box..
32162      */   
32163     padWidth : 10, 
32164     
32165     /**
32166      * @cfg {Number} gutter gutter width..
32167      */   
32168     gutter : 10,
32169     
32170      /**
32171      * @cfg {Number} maxCols maximum number of columns
32172      */   
32173     
32174     maxCols: 0,
32175     
32176     /**
32177      * @cfg {Boolean} isAutoInitial defalut true
32178      */   
32179     isAutoInitial : true, 
32180     
32181     containerWidth: 0,
32182     
32183     /**
32184      * @cfg {Boolean} isHorizontal defalut false
32185      */   
32186     isHorizontal : false, 
32187
32188     currentSize : null,
32189     
32190     tag: 'div',
32191     
32192     cls: '',
32193     
32194     bricks: null, //CompositeElement
32195     
32196     cols : 1,
32197     
32198     _isLayoutInited : false,
32199     
32200 //    isAlternative : false, // only use for vertical layout...
32201     
32202     /**
32203      * @cfg {Number} alternativePadWidth padding below box..
32204      */   
32205     alternativePadWidth : 50,
32206     
32207     selectedBrick : [],
32208     
32209     getAutoCreate : function(){
32210         
32211         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
32212         
32213         var cfg = {
32214             tag: this.tag,
32215             cls: 'blog-masonary-wrapper ' + this.cls,
32216             cn : {
32217                 cls : 'mas-boxes masonary'
32218             }
32219         };
32220         
32221         return cfg;
32222     },
32223     
32224     getChildContainer: function( )
32225     {
32226         if (this.boxesEl) {
32227             return this.boxesEl;
32228         }
32229         
32230         this.boxesEl = this.el.select('.mas-boxes').first();
32231         
32232         return this.boxesEl;
32233     },
32234     
32235     
32236     initEvents : function()
32237     {
32238         var _this = this;
32239         
32240         if(this.isAutoInitial){
32241             Roo.log('hook children rendered');
32242             this.on('childrenrendered', function() {
32243                 Roo.log('children rendered');
32244                 _this.initial();
32245             } ,this);
32246         }
32247     },
32248     
32249     initial : function()
32250     {
32251         this.selectedBrick = [];
32252         
32253         this.currentSize = this.el.getBox(true);
32254         
32255         Roo.EventManager.onWindowResize(this.resize, this); 
32256
32257         if(!this.isAutoInitial){
32258             this.layout();
32259             return;
32260         }
32261         
32262         this.layout();
32263         
32264         return;
32265         //this.layout.defer(500,this);
32266         
32267     },
32268     
32269     resize : function()
32270     {
32271         var cs = this.el.getBox(true);
32272         
32273         if (
32274                 this.currentSize.width == cs.width && 
32275                 this.currentSize.x == cs.x && 
32276                 this.currentSize.height == cs.height && 
32277                 this.currentSize.y == cs.y 
32278         ) {
32279             Roo.log("no change in with or X or Y");
32280             return;
32281         }
32282         
32283         this.currentSize = cs;
32284         
32285         this.layout();
32286         
32287     },
32288     
32289     layout : function()
32290     {   
32291         this._resetLayout();
32292         
32293         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32294         
32295         this.layoutItems( isInstant );
32296       
32297         this._isLayoutInited = true;
32298         
32299         this.fireEvent('layout', this);
32300         
32301     },
32302     
32303     _resetLayout : function()
32304     {
32305         if(this.isHorizontal){
32306             this.horizontalMeasureColumns();
32307             return;
32308         }
32309         
32310         this.verticalMeasureColumns();
32311         
32312     },
32313     
32314     verticalMeasureColumns : function()
32315     {
32316         this.getContainerWidth();
32317         
32318 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
32319 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
32320 //            return;
32321 //        }
32322         
32323         var boxWidth = this.boxWidth + this.padWidth;
32324         
32325         if(this.containerWidth < this.boxWidth){
32326             boxWidth = this.containerWidth
32327         }
32328         
32329         var containerWidth = this.containerWidth;
32330         
32331         var cols = Math.floor(containerWidth / boxWidth);
32332         
32333         this.cols = Math.max( cols, 1 );
32334         
32335         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32336         
32337         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
32338         
32339         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
32340         
32341         this.colWidth = boxWidth + avail - this.padWidth;
32342         
32343         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
32344         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
32345     },
32346     
32347     horizontalMeasureColumns : function()
32348     {
32349         this.getContainerWidth();
32350         
32351         var boxWidth = this.boxWidth;
32352         
32353         if(this.containerWidth < boxWidth){
32354             boxWidth = this.containerWidth;
32355         }
32356         
32357         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
32358         
32359         this.el.setHeight(boxWidth);
32360         
32361     },
32362     
32363     getContainerWidth : function()
32364     {
32365         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32366     },
32367     
32368     layoutItems : function( isInstant )
32369     {
32370         Roo.log(this.bricks);
32371         
32372         var items = Roo.apply([], this.bricks);
32373         
32374         if(this.isHorizontal){
32375             this._horizontalLayoutItems( items , isInstant );
32376             return;
32377         }
32378         
32379 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
32380 //            this._verticalAlternativeLayoutItems( items , isInstant );
32381 //            return;
32382 //        }
32383         
32384         this._verticalLayoutItems( items , isInstant );
32385         
32386     },
32387     
32388     _verticalLayoutItems : function ( items , isInstant)
32389     {
32390         if ( !items || !items.length ) {
32391             return;
32392         }
32393         
32394         var standard = [
32395             ['xs', 'xs', 'xs', 'tall'],
32396             ['xs', 'xs', 'tall'],
32397             ['xs', 'xs', 'sm'],
32398             ['xs', 'xs', 'xs'],
32399             ['xs', 'tall'],
32400             ['xs', 'sm'],
32401             ['xs', 'xs'],
32402             ['xs'],
32403             
32404             ['sm', 'xs', 'xs'],
32405             ['sm', 'xs'],
32406             ['sm'],
32407             
32408             ['tall', 'xs', 'xs', 'xs'],
32409             ['tall', 'xs', 'xs'],
32410             ['tall', 'xs'],
32411             ['tall']
32412             
32413         ];
32414         
32415         var queue = [];
32416         
32417         var boxes = [];
32418         
32419         var box = [];
32420         
32421         Roo.each(items, function(item, k){
32422             
32423             switch (item.size) {
32424                 // these layouts take up a full box,
32425                 case 'md' :
32426                 case 'md-left' :
32427                 case 'md-right' :
32428                 case 'wide' :
32429                     
32430                     if(box.length){
32431                         boxes.push(box);
32432                         box = [];
32433                     }
32434                     
32435                     boxes.push([item]);
32436                     
32437                     break;
32438                     
32439                 case 'xs' :
32440                 case 'sm' :
32441                 case 'tall' :
32442                     
32443                     box.push(item);
32444                     
32445                     break;
32446                 default :
32447                     break;
32448                     
32449             }
32450             
32451         }, this);
32452         
32453         if(box.length){
32454             boxes.push(box);
32455             box = [];
32456         }
32457         
32458         var filterPattern = function(box, length)
32459         {
32460             if(!box.length){
32461                 return;
32462             }
32463             
32464             var match = false;
32465             
32466             var pattern = box.slice(0, length);
32467             
32468             var format = [];
32469             
32470             Roo.each(pattern, function(i){
32471                 format.push(i.size);
32472             }, this);
32473             
32474             Roo.each(standard, function(s){
32475                 
32476                 if(String(s) != String(format)){
32477                     return;
32478                 }
32479                 
32480                 match = true;
32481                 return false;
32482                 
32483             }, this);
32484             
32485             if(!match && length == 1){
32486                 return;
32487             }
32488             
32489             if(!match){
32490                 filterPattern(box, length - 1);
32491                 return;
32492             }
32493                 
32494             queue.push(pattern);
32495
32496             box = box.slice(length, box.length);
32497
32498             filterPattern(box, 4);
32499
32500             return;
32501             
32502         }
32503         
32504         Roo.each(boxes, function(box, k){
32505             
32506             if(!box.length){
32507                 return;
32508             }
32509             
32510             if(box.length == 1){
32511                 queue.push(box);
32512                 return;
32513             }
32514             
32515             filterPattern(box, 4);
32516             
32517         }, this);
32518         
32519         this._processVerticalLayoutQueue( queue, isInstant );
32520         
32521     },
32522     
32523 //    _verticalAlternativeLayoutItems : function( items , isInstant )
32524 //    {
32525 //        if ( !items || !items.length ) {
32526 //            return;
32527 //        }
32528 //
32529 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
32530 //        
32531 //    },
32532     
32533     _horizontalLayoutItems : function ( items , isInstant)
32534     {
32535         if ( !items || !items.length || items.length < 3) {
32536             return;
32537         }
32538         
32539         items.reverse();
32540         
32541         var eItems = items.slice(0, 3);
32542         
32543         items = items.slice(3, items.length);
32544         
32545         var standard = [
32546             ['xs', 'xs', 'xs', 'wide'],
32547             ['xs', 'xs', 'wide'],
32548             ['xs', 'xs', 'sm'],
32549             ['xs', 'xs', 'xs'],
32550             ['xs', 'wide'],
32551             ['xs', 'sm'],
32552             ['xs', 'xs'],
32553             ['xs'],
32554             
32555             ['sm', 'xs', 'xs'],
32556             ['sm', 'xs'],
32557             ['sm'],
32558             
32559             ['wide', 'xs', 'xs', 'xs'],
32560             ['wide', 'xs', 'xs'],
32561             ['wide', 'xs'],
32562             ['wide'],
32563             
32564             ['wide-thin']
32565         ];
32566         
32567         var queue = [];
32568         
32569         var boxes = [];
32570         
32571         var box = [];
32572         
32573         Roo.each(items, function(item, k){
32574             
32575             switch (item.size) {
32576                 case 'md' :
32577                 case 'md-left' :
32578                 case 'md-right' :
32579                 case 'tall' :
32580                     
32581                     if(box.length){
32582                         boxes.push(box);
32583                         box = [];
32584                     }
32585                     
32586                     boxes.push([item]);
32587                     
32588                     break;
32589                     
32590                 case 'xs' :
32591                 case 'sm' :
32592                 case 'wide' :
32593                 case 'wide-thin' :
32594                     
32595                     box.push(item);
32596                     
32597                     break;
32598                 default :
32599                     break;
32600                     
32601             }
32602             
32603         }, this);
32604         
32605         if(box.length){
32606             boxes.push(box);
32607             box = [];
32608         }
32609         
32610         var filterPattern = function(box, length)
32611         {
32612             if(!box.length){
32613                 return;
32614             }
32615             
32616             var match = false;
32617             
32618             var pattern = box.slice(0, length);
32619             
32620             var format = [];
32621             
32622             Roo.each(pattern, function(i){
32623                 format.push(i.size);
32624             }, this);
32625             
32626             Roo.each(standard, function(s){
32627                 
32628                 if(String(s) != String(format)){
32629                     return;
32630                 }
32631                 
32632                 match = true;
32633                 return false;
32634                 
32635             }, this);
32636             
32637             if(!match && length == 1){
32638                 return;
32639             }
32640             
32641             if(!match){
32642                 filterPattern(box, length - 1);
32643                 return;
32644             }
32645                 
32646             queue.push(pattern);
32647
32648             box = box.slice(length, box.length);
32649
32650             filterPattern(box, 4);
32651
32652             return;
32653             
32654         }
32655         
32656         Roo.each(boxes, function(box, k){
32657             
32658             if(!box.length){
32659                 return;
32660             }
32661             
32662             if(box.length == 1){
32663                 queue.push(box);
32664                 return;
32665             }
32666             
32667             filterPattern(box, 4);
32668             
32669         }, this);
32670         
32671         
32672         var prune = [];
32673         
32674         var pos = this.el.getBox(true);
32675         
32676         var minX = pos.x;
32677         
32678         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
32679         
32680         var hit_end = false;
32681         
32682         Roo.each(queue, function(box){
32683             
32684             if(hit_end){
32685                 
32686                 Roo.each(box, function(b){
32687                 
32688                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
32689                     b.el.hide();
32690
32691                 }, this);
32692
32693                 return;
32694             }
32695             
32696             var mx = 0;
32697             
32698             Roo.each(box, function(b){
32699                 
32700                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
32701                 b.el.show();
32702
32703                 mx = Math.max(mx, b.x);
32704                 
32705             }, this);
32706             
32707             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
32708             
32709             if(maxX < minX){
32710                 
32711                 Roo.each(box, function(b){
32712                 
32713                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
32714                     b.el.hide();
32715                     
32716                 }, this);
32717                 
32718                 hit_end = true;
32719                 
32720                 return;
32721             }
32722             
32723             prune.push(box);
32724             
32725         }, this);
32726         
32727         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
32728     },
32729     
32730     /** Sets position of item in DOM
32731     * @param {Element} item
32732     * @param {Number} x - horizontal position
32733     * @param {Number} y - vertical position
32734     * @param {Boolean} isInstant - disables transitions
32735     */
32736     _processVerticalLayoutQueue : function( queue, isInstant )
32737     {
32738         var pos = this.el.getBox(true);
32739         var x = pos.x;
32740         var y = pos.y;
32741         var maxY = [];
32742         
32743         for (var i = 0; i < this.cols; i++){
32744             maxY[i] = pos.y;
32745         }
32746         
32747         Roo.each(queue, function(box, k){
32748             
32749             var col = k % this.cols;
32750             
32751             Roo.each(box, function(b,kk){
32752                 
32753                 b.el.position('absolute');
32754                 
32755                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32756                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32757                 
32758                 if(b.size == 'md-left' || b.size == 'md-right'){
32759                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
32760                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
32761                 }
32762                 
32763                 b.el.setWidth(width);
32764                 b.el.setHeight(height);
32765                 // iframe?
32766                 b.el.select('iframe',true).setSize(width,height);
32767                 
32768             }, this);
32769             
32770             for (var i = 0; i < this.cols; i++){
32771                 
32772                 if(maxY[i] < maxY[col]){
32773                     col = i;
32774                     continue;
32775                 }
32776                 
32777                 col = Math.min(col, i);
32778                 
32779             }
32780             
32781             x = pos.x + col * (this.colWidth + this.padWidth);
32782             
32783             y = maxY[col];
32784             
32785             var positions = [];
32786             
32787             switch (box.length){
32788                 case 1 :
32789                     positions = this.getVerticalOneBoxColPositions(x, y, box);
32790                     break;
32791                 case 2 :
32792                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
32793                     break;
32794                 case 3 :
32795                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
32796                     break;
32797                 case 4 :
32798                     positions = this.getVerticalFourBoxColPositions(x, y, box);
32799                     break;
32800                 default :
32801                     break;
32802             }
32803             
32804             Roo.each(box, function(b,kk){
32805                 
32806                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
32807                 
32808                 var sz = b.el.getSize();
32809                 
32810                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
32811                 
32812             }, this);
32813             
32814         }, this);
32815         
32816         var mY = 0;
32817         
32818         for (var i = 0; i < this.cols; i++){
32819             mY = Math.max(mY, maxY[i]);
32820         }
32821         
32822         this.el.setHeight(mY - pos.y);
32823         
32824     },
32825     
32826 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
32827 //    {
32828 //        var pos = this.el.getBox(true);
32829 //        var x = pos.x;
32830 //        var y = pos.y;
32831 //        var maxX = pos.right;
32832 //        
32833 //        var maxHeight = 0;
32834 //        
32835 //        Roo.each(items, function(item, k){
32836 //            
32837 //            var c = k % 2;
32838 //            
32839 //            item.el.position('absolute');
32840 //                
32841 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
32842 //
32843 //            item.el.setWidth(width);
32844 //
32845 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
32846 //
32847 //            item.el.setHeight(height);
32848 //            
32849 //            if(c == 0){
32850 //                item.el.setXY([x, y], isInstant ? false : true);
32851 //            } else {
32852 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
32853 //            }
32854 //            
32855 //            y = y + height + this.alternativePadWidth;
32856 //            
32857 //            maxHeight = maxHeight + height + this.alternativePadWidth;
32858 //            
32859 //        }, this);
32860 //        
32861 //        this.el.setHeight(maxHeight);
32862 //        
32863 //    },
32864     
32865     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
32866     {
32867         var pos = this.el.getBox(true);
32868         
32869         var minX = pos.x;
32870         var minY = pos.y;
32871         
32872         var maxX = pos.right;
32873         
32874         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
32875         
32876         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
32877         
32878         Roo.each(queue, function(box, k){
32879             
32880             Roo.each(box, function(b, kk){
32881                 
32882                 b.el.position('absolute');
32883                 
32884                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32885                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32886                 
32887                 if(b.size == 'md-left' || b.size == 'md-right'){
32888                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
32889                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
32890                 }
32891                 
32892                 b.el.setWidth(width);
32893                 b.el.setHeight(height);
32894                 
32895             }, this);
32896             
32897             if(!box.length){
32898                 return;
32899             }
32900             
32901             var positions = [];
32902             
32903             switch (box.length){
32904                 case 1 :
32905                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
32906                     break;
32907                 case 2 :
32908                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
32909                     break;
32910                 case 3 :
32911                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
32912                     break;
32913                 case 4 :
32914                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
32915                     break;
32916                 default :
32917                     break;
32918             }
32919             
32920             Roo.each(box, function(b,kk){
32921                 
32922                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
32923                 
32924                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
32925                 
32926             }, this);
32927             
32928         }, this);
32929         
32930     },
32931     
32932     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
32933     {
32934         Roo.each(eItems, function(b,k){
32935             
32936             b.size = (k == 0) ? 'sm' : 'xs';
32937             b.x = (k == 0) ? 2 : 1;
32938             b.y = (k == 0) ? 2 : 1;
32939             
32940             b.el.position('absolute');
32941             
32942             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32943                 
32944             b.el.setWidth(width);
32945             
32946             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32947             
32948             b.el.setHeight(height);
32949             
32950         }, this);
32951
32952         var positions = [];
32953         
32954         positions.push({
32955             x : maxX - this.unitWidth * 2 - this.gutter,
32956             y : minY
32957         });
32958         
32959         positions.push({
32960             x : maxX - this.unitWidth,
32961             y : minY + (this.unitWidth + this.gutter) * 2
32962         });
32963         
32964         positions.push({
32965             x : maxX - this.unitWidth * 3 - this.gutter * 2,
32966             y : minY
32967         });
32968         
32969         Roo.each(eItems, function(b,k){
32970             
32971             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
32972
32973         }, this);
32974         
32975     },
32976     
32977     getVerticalOneBoxColPositions : function(x, y, box)
32978     {
32979         var pos = [];
32980         
32981         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
32982         
32983         if(box[0].size == 'md-left'){
32984             rand = 0;
32985         }
32986         
32987         if(box[0].size == 'md-right'){
32988             rand = 1;
32989         }
32990         
32991         pos.push({
32992             x : x + (this.unitWidth + this.gutter) * rand,
32993             y : y
32994         });
32995         
32996         return pos;
32997     },
32998     
32999     getVerticalTwoBoxColPositions : function(x, y, box)
33000     {
33001         var pos = [];
33002         
33003         if(box[0].size == 'xs'){
33004             
33005             pos.push({
33006                 x : x,
33007                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
33008             });
33009
33010             pos.push({
33011                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
33012                 y : y
33013             });
33014             
33015             return pos;
33016             
33017         }
33018         
33019         pos.push({
33020             x : x,
33021             y : y
33022         });
33023
33024         pos.push({
33025             x : x + (this.unitWidth + this.gutter) * 2,
33026             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
33027         });
33028         
33029         return pos;
33030         
33031     },
33032     
33033     getVerticalThreeBoxColPositions : function(x, y, box)
33034     {
33035         var pos = [];
33036         
33037         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
33038             
33039             pos.push({
33040                 x : x,
33041                 y : y
33042             });
33043
33044             pos.push({
33045                 x : x + (this.unitWidth + this.gutter) * 1,
33046                 y : y
33047             });
33048             
33049             pos.push({
33050                 x : x + (this.unitWidth + this.gutter) * 2,
33051                 y : y
33052             });
33053             
33054             return pos;
33055             
33056         }
33057         
33058         if(box[0].size == 'xs' && box[1].size == 'xs'){
33059             
33060             pos.push({
33061                 x : x,
33062                 y : y
33063             });
33064
33065             pos.push({
33066                 x : x,
33067                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
33068             });
33069             
33070             pos.push({
33071                 x : x + (this.unitWidth + this.gutter) * 1,
33072                 y : y
33073             });
33074             
33075             return pos;
33076             
33077         }
33078         
33079         pos.push({
33080             x : x,
33081             y : y
33082         });
33083
33084         pos.push({
33085             x : x + (this.unitWidth + this.gutter) * 2,
33086             y : y
33087         });
33088
33089         pos.push({
33090             x : x + (this.unitWidth + this.gutter) * 2,
33091             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
33092         });
33093             
33094         return pos;
33095         
33096     },
33097     
33098     getVerticalFourBoxColPositions : function(x, y, box)
33099     {
33100         var pos = [];
33101         
33102         if(box[0].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) * 1
33112             });
33113             
33114             pos.push({
33115                 x : x,
33116                 y : y + (this.unitHeight + this.gutter) * 2
33117             });
33118             
33119             pos.push({
33120                 x : x + (this.unitWidth + this.gutter) * 1,
33121                 y : y
33122             });
33123             
33124             return pos;
33125             
33126         }
33127         
33128         pos.push({
33129             x : x,
33130             y : y
33131         });
33132
33133         pos.push({
33134             x : x + (this.unitWidth + this.gutter) * 2,
33135             y : y
33136         });
33137
33138         pos.push({
33139             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
33140             y : y + (this.unitHeight + this.gutter) * 1
33141         });
33142
33143         pos.push({
33144             x : x + (this.unitWidth + this.gutter) * 2,
33145             y : y + (this.unitWidth + this.gutter) * 2
33146         });
33147
33148         return pos;
33149         
33150     },
33151     
33152     getHorizontalOneBoxColPositions : function(maxX, minY, box)
33153     {
33154         var pos = [];
33155         
33156         if(box[0].size == 'md-left'){
33157             pos.push({
33158                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
33159                 y : minY
33160             });
33161             
33162             return pos;
33163         }
33164         
33165         if(box[0].size == 'md-right'){
33166             pos.push({
33167                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
33168                 y : minY + (this.unitWidth + this.gutter) * 1
33169             });
33170             
33171             return pos;
33172         }
33173         
33174         var rand = Math.floor(Math.random() * (4 - box[0].y));
33175         
33176         pos.push({
33177             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33178             y : minY + (this.unitWidth + this.gutter) * rand
33179         });
33180         
33181         return pos;
33182         
33183     },
33184     
33185     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
33186     {
33187         var pos = [];
33188         
33189         if(box[0].size == 'xs'){
33190             
33191             pos.push({
33192                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33193                 y : minY
33194             });
33195
33196             pos.push({
33197                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33198                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
33199             });
33200             
33201             return pos;
33202             
33203         }
33204         
33205         pos.push({
33206             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33207             y : minY
33208         });
33209
33210         pos.push({
33211             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33212             y : minY + (this.unitWidth + this.gutter) * 2
33213         });
33214         
33215         return pos;
33216         
33217     },
33218     
33219     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
33220     {
33221         var pos = [];
33222         
33223         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
33224             
33225             pos.push({
33226                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33227                 y : minY
33228             });
33229
33230             pos.push({
33231                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33232                 y : minY + (this.unitWidth + this.gutter) * 1
33233             });
33234             
33235             pos.push({
33236                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33237                 y : minY + (this.unitWidth + this.gutter) * 2
33238             });
33239             
33240             return pos;
33241             
33242         }
33243         
33244         if(box[0].size == 'xs' && box[1].size == 'xs'){
33245             
33246             pos.push({
33247                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33248                 y : minY
33249             });
33250
33251             pos.push({
33252                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33253                 y : minY
33254             });
33255             
33256             pos.push({
33257                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33258                 y : minY + (this.unitWidth + this.gutter) * 1
33259             });
33260             
33261             return pos;
33262             
33263         }
33264         
33265         pos.push({
33266             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33267             y : minY
33268         });
33269
33270         pos.push({
33271             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33272             y : minY + (this.unitWidth + this.gutter) * 2
33273         });
33274
33275         pos.push({
33276             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33277             y : minY + (this.unitWidth + this.gutter) * 2
33278         });
33279             
33280         return pos;
33281         
33282     },
33283     
33284     getHorizontalFourBoxColPositions : function(maxX, minY, box)
33285     {
33286         var pos = [];
33287         
33288         if(box[0].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[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),
33302                 y : minY
33303             });
33304             
33305             pos.push({
33306                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
33307                 y : minY + (this.unitWidth + this.gutter) * 1
33308             });
33309             
33310             return pos;
33311             
33312         }
33313         
33314         pos.push({
33315             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33316             y : minY
33317         });
33318         
33319         pos.push({
33320             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33321             y : minY + (this.unitWidth + this.gutter) * 2
33322         });
33323         
33324         pos.push({
33325             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33326             y : minY + (this.unitWidth + this.gutter) * 2
33327         });
33328         
33329         pos.push({
33330             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),
33331             y : minY + (this.unitWidth + this.gutter) * 2
33332         });
33333
33334         return pos;
33335         
33336     },
33337     
33338     /**
33339     * remove a Masonry Brick
33340     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
33341     */
33342     removeBrick : function(brick_id)
33343     {
33344         if (!brick_id) {
33345             return;
33346         }
33347         
33348         for (var i = 0; i<this.bricks.length; i++) {
33349             if (this.bricks[i].id == brick_id) {
33350                 this.bricks.splice(i,1);
33351                 this.el.dom.removeChild(Roo.get(brick_id).dom);
33352                 this.initial();
33353             }
33354         }
33355     },
33356     
33357     /**
33358     * adds a Masonry Brick
33359     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33360     */
33361     addBrick : function(cfg)
33362     {
33363         var cn = new Roo.bootstrap.MasonryBrick(cfg);
33364         //this.register(cn);
33365         cn.parentId = this.id;
33366         cn.render(this.el);
33367         return cn;
33368     },
33369     
33370     /**
33371     * register a Masonry Brick
33372     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33373     */
33374     
33375     register : function(brick)
33376     {
33377         this.bricks.push(brick);
33378         brick.masonryId = this.id;
33379     },
33380     
33381     /**
33382     * clear all the Masonry Brick
33383     */
33384     clearAll : function()
33385     {
33386         this.bricks = [];
33387         //this.getChildContainer().dom.innerHTML = "";
33388         this.el.dom.innerHTML = '';
33389     },
33390     
33391     getSelected : function()
33392     {
33393         if (!this.selectedBrick) {
33394             return false;
33395         }
33396         
33397         return this.selectedBrick;
33398     }
33399 });
33400
33401 Roo.apply(Roo.bootstrap.LayoutMasonry, {
33402     
33403     groups: {},
33404      /**
33405     * register a Masonry Layout
33406     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
33407     */
33408     
33409     register : function(layout)
33410     {
33411         this.groups[layout.id] = layout;
33412     },
33413     /**
33414     * fetch a  Masonry Layout based on the masonry layout ID
33415     * @param {string} the masonry layout to add
33416     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
33417     */
33418     
33419     get: function(layout_id) {
33420         if (typeof(this.groups[layout_id]) == 'undefined') {
33421             return false;
33422         }
33423         return this.groups[layout_id] ;
33424     }
33425     
33426     
33427     
33428 });
33429
33430  
33431
33432  /**
33433  *
33434  * This is based on 
33435  * http://masonry.desandro.com
33436  *
33437  * The idea is to render all the bricks based on vertical width...
33438  *
33439  * The original code extends 'outlayer' - we might need to use that....
33440  * 
33441  */
33442
33443
33444 /**
33445  * @class Roo.bootstrap.LayoutMasonryAuto
33446  * @extends Roo.bootstrap.Component
33447  * Bootstrap Layout Masonry class
33448  * 
33449  * @constructor
33450  * Create a new Element
33451  * @param {Object} config The config object
33452  */
33453
33454 Roo.bootstrap.LayoutMasonryAuto = function(config){
33455     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
33456 };
33457
33458 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
33459     
33460       /**
33461      * @cfg {Boolean} isFitWidth  - resize the width..
33462      */   
33463     isFitWidth : false,  // options..
33464     /**
33465      * @cfg {Boolean} isOriginLeft = left align?
33466      */   
33467     isOriginLeft : true,
33468     /**
33469      * @cfg {Boolean} isOriginTop = top align?
33470      */   
33471     isOriginTop : false,
33472     /**
33473      * @cfg {Boolean} isLayoutInstant = no animation?
33474      */   
33475     isLayoutInstant : false, // needed?
33476     /**
33477      * @cfg {Boolean} isResizingContainer = not sure if this is used..
33478      */   
33479     isResizingContainer : true,
33480     /**
33481      * @cfg {Number} columnWidth  width of the columns 
33482      */   
33483     
33484     columnWidth : 0,
33485     
33486     /**
33487      * @cfg {Number} maxCols maximum number of columns
33488      */   
33489     
33490     maxCols: 0,
33491     /**
33492      * @cfg {Number} padHeight padding below box..
33493      */   
33494     
33495     padHeight : 10, 
33496     
33497     /**
33498      * @cfg {Boolean} isAutoInitial defalut true
33499      */   
33500     
33501     isAutoInitial : true, 
33502     
33503     // private?
33504     gutter : 0,
33505     
33506     containerWidth: 0,
33507     initialColumnWidth : 0,
33508     currentSize : null,
33509     
33510     colYs : null, // array.
33511     maxY : 0,
33512     padWidth: 10,
33513     
33514     
33515     tag: 'div',
33516     cls: '',
33517     bricks: null, //CompositeElement
33518     cols : 0, // array?
33519     // element : null, // wrapped now this.el
33520     _isLayoutInited : null, 
33521     
33522     
33523     getAutoCreate : function(){
33524         
33525         var cfg = {
33526             tag: this.tag,
33527             cls: 'blog-masonary-wrapper ' + this.cls,
33528             cn : {
33529                 cls : 'mas-boxes masonary'
33530             }
33531         };
33532         
33533         return cfg;
33534     },
33535     
33536     getChildContainer: function( )
33537     {
33538         if (this.boxesEl) {
33539             return this.boxesEl;
33540         }
33541         
33542         this.boxesEl = this.el.select('.mas-boxes').first();
33543         
33544         return this.boxesEl;
33545     },
33546     
33547     
33548     initEvents : function()
33549     {
33550         var _this = this;
33551         
33552         if(this.isAutoInitial){
33553             Roo.log('hook children rendered');
33554             this.on('childrenrendered', function() {
33555                 Roo.log('children rendered');
33556                 _this.initial();
33557             } ,this);
33558         }
33559         
33560     },
33561     
33562     initial : function()
33563     {
33564         this.reloadItems();
33565
33566         this.currentSize = this.el.getBox(true);
33567
33568         /// was window resize... - let's see if this works..
33569         Roo.EventManager.onWindowResize(this.resize, this); 
33570
33571         if(!this.isAutoInitial){
33572             this.layout();
33573             return;
33574         }
33575         
33576         this.layout.defer(500,this);
33577     },
33578     
33579     reloadItems: function()
33580     {
33581         this.bricks = this.el.select('.masonry-brick', true);
33582         
33583         this.bricks.each(function(b) {
33584             //Roo.log(b.getSize());
33585             if (!b.attr('originalwidth')) {
33586                 b.attr('originalwidth',  b.getSize().width);
33587             }
33588             
33589         });
33590         
33591         Roo.log(this.bricks.elements.length);
33592     },
33593     
33594     resize : function()
33595     {
33596         Roo.log('resize');
33597         var cs = this.el.getBox(true);
33598         
33599         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
33600             Roo.log("no change in with or X");
33601             return;
33602         }
33603         this.currentSize = cs;
33604         this.layout();
33605     },
33606     
33607     layout : function()
33608     {
33609          Roo.log('layout');
33610         this._resetLayout();
33611         //this._manageStamps();
33612       
33613         // don't animate first layout
33614         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33615         this.layoutItems( isInstant );
33616       
33617         // flag for initalized
33618         this._isLayoutInited = true;
33619     },
33620     
33621     layoutItems : function( isInstant )
33622     {
33623         //var items = this._getItemsForLayout( this.items );
33624         // original code supports filtering layout items.. we just ignore it..
33625         
33626         this._layoutItems( this.bricks , isInstant );
33627       
33628         this._postLayout();
33629     },
33630     _layoutItems : function ( items , isInstant)
33631     {
33632        //this.fireEvent( 'layout', this, items );
33633     
33634
33635         if ( !items || !items.elements.length ) {
33636           // no items, emit event with empty array
33637             return;
33638         }
33639
33640         var queue = [];
33641         items.each(function(item) {
33642             Roo.log("layout item");
33643             Roo.log(item);
33644             // get x/y object from method
33645             var position = this._getItemLayoutPosition( item );
33646             // enqueue
33647             position.item = item;
33648             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
33649             queue.push( position );
33650         }, this);
33651       
33652         this._processLayoutQueue( queue );
33653     },
33654     /** Sets position of item in DOM
33655     * @param {Element} item
33656     * @param {Number} x - horizontal position
33657     * @param {Number} y - vertical position
33658     * @param {Boolean} isInstant - disables transitions
33659     */
33660     _processLayoutQueue : function( queue )
33661     {
33662         for ( var i=0, len = queue.length; i < len; i++ ) {
33663             var obj = queue[i];
33664             obj.item.position('absolute');
33665             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
33666         }
33667     },
33668       
33669     
33670     /**
33671     * Any logic you want to do after each layout,
33672     * i.e. size the container
33673     */
33674     _postLayout : function()
33675     {
33676         this.resizeContainer();
33677     },
33678     
33679     resizeContainer : function()
33680     {
33681         if ( !this.isResizingContainer ) {
33682             return;
33683         }
33684         var size = this._getContainerSize();
33685         if ( size ) {
33686             this.el.setSize(size.width,size.height);
33687             this.boxesEl.setSize(size.width,size.height);
33688         }
33689     },
33690     
33691     
33692     
33693     _resetLayout : function()
33694     {
33695         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
33696         this.colWidth = this.el.getWidth();
33697         //this.gutter = this.el.getWidth(); 
33698         
33699         this.measureColumns();
33700
33701         // reset column Y
33702         var i = this.cols;
33703         this.colYs = [];
33704         while (i--) {
33705             this.colYs.push( 0 );
33706         }
33707     
33708         this.maxY = 0;
33709     },
33710
33711     measureColumns : function()
33712     {
33713         this.getContainerWidth();
33714       // if columnWidth is 0, default to outerWidth of first item
33715         if ( !this.columnWidth ) {
33716             var firstItem = this.bricks.first();
33717             Roo.log(firstItem);
33718             this.columnWidth  = this.containerWidth;
33719             if (firstItem && firstItem.attr('originalwidth') ) {
33720                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
33721             }
33722             // columnWidth fall back to item of first element
33723             Roo.log("set column width?");
33724                         this.initialColumnWidth = this.columnWidth  ;
33725
33726             // if first elem has no width, default to size of container
33727             
33728         }
33729         
33730         
33731         if (this.initialColumnWidth) {
33732             this.columnWidth = this.initialColumnWidth;
33733         }
33734         
33735         
33736             
33737         // column width is fixed at the top - however if container width get's smaller we should
33738         // reduce it...
33739         
33740         // this bit calcs how man columns..
33741             
33742         var columnWidth = this.columnWidth += this.gutter;
33743       
33744         // calculate columns
33745         var containerWidth = this.containerWidth + this.gutter;
33746         
33747         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
33748         // fix rounding errors, typically with gutters
33749         var excess = columnWidth - containerWidth % columnWidth;
33750         
33751         
33752         // if overshoot is less than a pixel, round up, otherwise floor it
33753         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
33754         cols = Math[ mathMethod ]( cols );
33755         this.cols = Math.max( cols, 1 );
33756         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33757         
33758          // padding positioning..
33759         var totalColWidth = this.cols * this.columnWidth;
33760         var padavail = this.containerWidth - totalColWidth;
33761         // so for 2 columns - we need 3 'pads'
33762         
33763         var padNeeded = (1+this.cols) * this.padWidth;
33764         
33765         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
33766         
33767         this.columnWidth += padExtra
33768         //this.padWidth = Math.floor(padavail /  ( this.cols));
33769         
33770         // adjust colum width so that padding is fixed??
33771         
33772         // we have 3 columns ... total = width * 3
33773         // we have X left over... that should be used by 
33774         
33775         //if (this.expandC) {
33776             
33777         //}
33778         
33779         
33780         
33781     },
33782     
33783     getContainerWidth : function()
33784     {
33785        /* // container is parent if fit width
33786         var container = this.isFitWidth ? this.element.parentNode : this.element;
33787         // check that this.size and size are there
33788         // IE8 triggers resize on body size change, so they might not be
33789         
33790         var size = getSize( container );  //FIXME
33791         this.containerWidth = size && size.innerWidth; //FIXME
33792         */
33793          
33794         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33795         
33796     },
33797     
33798     _getItemLayoutPosition : function( item )  // what is item?
33799     {
33800         // we resize the item to our columnWidth..
33801       
33802         item.setWidth(this.columnWidth);
33803         item.autoBoxAdjust  = false;
33804         
33805         var sz = item.getSize();
33806  
33807         // how many columns does this brick span
33808         var remainder = this.containerWidth % this.columnWidth;
33809         
33810         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
33811         // round if off by 1 pixel, otherwise use ceil
33812         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
33813         colSpan = Math.min( colSpan, this.cols );
33814         
33815         // normally this should be '1' as we dont' currently allow multi width columns..
33816         
33817         var colGroup = this._getColGroup( colSpan );
33818         // get the minimum Y value from the columns
33819         var minimumY = Math.min.apply( Math, colGroup );
33820         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
33821         
33822         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
33823          
33824         // position the brick
33825         var position = {
33826             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
33827             y: this.currentSize.y + minimumY + this.padHeight
33828         };
33829         
33830         Roo.log(position);
33831         // apply setHeight to necessary columns
33832         var setHeight = minimumY + sz.height + this.padHeight;
33833         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
33834         
33835         var setSpan = this.cols + 1 - colGroup.length;
33836         for ( var i = 0; i < setSpan; i++ ) {
33837           this.colYs[ shortColIndex + i ] = setHeight ;
33838         }
33839       
33840         return position;
33841     },
33842     
33843     /**
33844      * @param {Number} colSpan - number of columns the element spans
33845      * @returns {Array} colGroup
33846      */
33847     _getColGroup : function( colSpan )
33848     {
33849         if ( colSpan < 2 ) {
33850           // if brick spans only one column, use all the column Ys
33851           return this.colYs;
33852         }
33853       
33854         var colGroup = [];
33855         // how many different places could this brick fit horizontally
33856         var groupCount = this.cols + 1 - colSpan;
33857         // for each group potential horizontal position
33858         for ( var i = 0; i < groupCount; i++ ) {
33859           // make an array of colY values for that one group
33860           var groupColYs = this.colYs.slice( i, i + colSpan );
33861           // and get the max value of the array
33862           colGroup[i] = Math.max.apply( Math, groupColYs );
33863         }
33864         return colGroup;
33865     },
33866     /*
33867     _manageStamp : function( stamp )
33868     {
33869         var stampSize =  stamp.getSize();
33870         var offset = stamp.getBox();
33871         // get the columns that this stamp affects
33872         var firstX = this.isOriginLeft ? offset.x : offset.right;
33873         var lastX = firstX + stampSize.width;
33874         var firstCol = Math.floor( firstX / this.columnWidth );
33875         firstCol = Math.max( 0, firstCol );
33876         
33877         var lastCol = Math.floor( lastX / this.columnWidth );
33878         // lastCol should not go over if multiple of columnWidth #425
33879         lastCol -= lastX % this.columnWidth ? 0 : 1;
33880         lastCol = Math.min( this.cols - 1, lastCol );
33881         
33882         // set colYs to bottom of the stamp
33883         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
33884             stampSize.height;
33885             
33886         for ( var i = firstCol; i <= lastCol; i++ ) {
33887           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
33888         }
33889     },
33890     */
33891     
33892     _getContainerSize : function()
33893     {
33894         this.maxY = Math.max.apply( Math, this.colYs );
33895         var size = {
33896             height: this.maxY
33897         };
33898       
33899         if ( this.isFitWidth ) {
33900             size.width = this._getContainerFitWidth();
33901         }
33902       
33903         return size;
33904     },
33905     
33906     _getContainerFitWidth : function()
33907     {
33908         var unusedCols = 0;
33909         // count unused columns
33910         var i = this.cols;
33911         while ( --i ) {
33912           if ( this.colYs[i] !== 0 ) {
33913             break;
33914           }
33915           unusedCols++;
33916         }
33917         // fit container to columns that have been used
33918         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
33919     },
33920     
33921     needsResizeLayout : function()
33922     {
33923         var previousWidth = this.containerWidth;
33924         this.getContainerWidth();
33925         return previousWidth !== this.containerWidth;
33926     }
33927  
33928 });
33929
33930  
33931
33932  /*
33933  * - LGPL
33934  *
33935  * element
33936  * 
33937  */
33938
33939 /**
33940  * @class Roo.bootstrap.MasonryBrick
33941  * @extends Roo.bootstrap.Component
33942  * Bootstrap MasonryBrick class
33943  * 
33944  * @constructor
33945  * Create a new MasonryBrick
33946  * @param {Object} config The config object
33947  */
33948
33949 Roo.bootstrap.MasonryBrick = function(config){
33950     
33951     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
33952     
33953     Roo.bootstrap.MasonryBrick.register(this);
33954     
33955     this.addEvents({
33956         // raw events
33957         /**
33958          * @event click
33959          * When a MasonryBrick is clcik
33960          * @param {Roo.bootstrap.MasonryBrick} this
33961          * @param {Roo.EventObject} e
33962          */
33963         "click" : true
33964     });
33965 };
33966
33967 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
33968     
33969     /**
33970      * @cfg {String} title
33971      */   
33972     title : '',
33973     /**
33974      * @cfg {String} html
33975      */   
33976     html : '',
33977     /**
33978      * @cfg {String} bgimage
33979      */   
33980     bgimage : '',
33981     /**
33982      * @cfg {String} videourl
33983      */   
33984     videourl : '',
33985     /**
33986      * @cfg {String} cls
33987      */   
33988     cls : '',
33989     /**
33990      * @cfg {String} href
33991      */   
33992     href : '',
33993     /**
33994      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
33995      */   
33996     size : 'xs',
33997     
33998     /**
33999      * @cfg {String} placetitle (center|bottom)
34000      */   
34001     placetitle : '',
34002     
34003     /**
34004      * @cfg {Boolean} isFitContainer defalut true
34005      */   
34006     isFitContainer : true, 
34007     
34008     /**
34009      * @cfg {Boolean} preventDefault defalut false
34010      */   
34011     preventDefault : false, 
34012     
34013     /**
34014      * @cfg {Boolean} inverse defalut false
34015      */   
34016     maskInverse : false, 
34017     
34018     getAutoCreate : function()
34019     {
34020         if(!this.isFitContainer){
34021             return this.getSplitAutoCreate();
34022         }
34023         
34024         var cls = 'masonry-brick masonry-brick-full';
34025         
34026         if(this.href.length){
34027             cls += ' masonry-brick-link';
34028         }
34029         
34030         if(this.bgimage.length){
34031             cls += ' masonry-brick-image';
34032         }
34033         
34034         if(this.maskInverse){
34035             cls += ' mask-inverse';
34036         }
34037         
34038         if(!this.html.length && !this.maskInverse && !this.videourl.length){
34039             cls += ' enable-mask';
34040         }
34041         
34042         if(this.size){
34043             cls += ' masonry-' + this.size + '-brick';
34044         }
34045         
34046         if(this.placetitle.length){
34047             
34048             switch (this.placetitle) {
34049                 case 'center' :
34050                     cls += ' masonry-center-title';
34051                     break;
34052                 case 'bottom' :
34053                     cls += ' masonry-bottom-title';
34054                     break;
34055                 default:
34056                     break;
34057             }
34058             
34059         } else {
34060             if(!this.html.length && !this.bgimage.length){
34061                 cls += ' masonry-center-title';
34062             }
34063
34064             if(!this.html.length && this.bgimage.length){
34065                 cls += ' masonry-bottom-title';
34066             }
34067         }
34068         
34069         if(this.cls){
34070             cls += ' ' + this.cls;
34071         }
34072         
34073         var cfg = {
34074             tag: (this.href.length) ? 'a' : 'div',
34075             cls: cls,
34076             cn: [
34077                 {
34078                     tag: 'div',
34079                     cls: 'masonry-brick-mask'
34080                 },
34081                 {
34082                     tag: 'div',
34083                     cls: 'masonry-brick-paragraph',
34084                     cn: []
34085                 }
34086             ]
34087         };
34088         
34089         if(this.href.length){
34090             cfg.href = this.href;
34091         }
34092         
34093         var cn = cfg.cn[1].cn;
34094         
34095         if(this.title.length){
34096             cn.push({
34097                 tag: 'h4',
34098                 cls: 'masonry-brick-title',
34099                 html: this.title
34100             });
34101         }
34102         
34103         if(this.html.length){
34104             cn.push({
34105                 tag: 'p',
34106                 cls: 'masonry-brick-text',
34107                 html: this.html
34108             });
34109         }
34110         
34111         if (!this.title.length && !this.html.length) {
34112             cfg.cn[1].cls += ' hide';
34113         }
34114         
34115         if(this.bgimage.length){
34116             cfg.cn.push({
34117                 tag: 'img',
34118                 cls: 'masonry-brick-image-view',
34119                 src: this.bgimage
34120             });
34121         }
34122         
34123         if(this.videourl.length){
34124             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
34125             // youtube support only?
34126             cfg.cn.push({
34127                 tag: 'iframe',
34128                 cls: 'masonry-brick-image-view',
34129                 src: vurl,
34130                 frameborder : 0,
34131                 allowfullscreen : true
34132             });
34133         }
34134         
34135         return cfg;
34136         
34137     },
34138     
34139     getSplitAutoCreate : function()
34140     {
34141         var cls = 'masonry-brick masonry-brick-split';
34142         
34143         if(this.href.length){
34144             cls += ' masonry-brick-link';
34145         }
34146         
34147         if(this.bgimage.length){
34148             cls += ' masonry-brick-image';
34149         }
34150         
34151         if(this.size){
34152             cls += ' masonry-' + this.size + '-brick';
34153         }
34154         
34155         switch (this.placetitle) {
34156             case 'center' :
34157                 cls += ' masonry-center-title';
34158                 break;
34159             case 'bottom' :
34160                 cls += ' masonry-bottom-title';
34161                 break;
34162             default:
34163                 if(!this.bgimage.length){
34164                     cls += ' masonry-center-title';
34165                 }
34166
34167                 if(this.bgimage.length){
34168                     cls += ' masonry-bottom-title';
34169                 }
34170                 break;
34171         }
34172         
34173         if(this.cls){
34174             cls += ' ' + this.cls;
34175         }
34176         
34177         var cfg = {
34178             tag: (this.href.length) ? 'a' : 'div',
34179             cls: cls,
34180             cn: [
34181                 {
34182                     tag: 'div',
34183                     cls: 'masonry-brick-split-head',
34184                     cn: [
34185                         {
34186                             tag: 'div',
34187                             cls: 'masonry-brick-paragraph',
34188                             cn: []
34189                         }
34190                     ]
34191                 },
34192                 {
34193                     tag: 'div',
34194                     cls: 'masonry-brick-split-body',
34195                     cn: []
34196                 }
34197             ]
34198         };
34199         
34200         if(this.href.length){
34201             cfg.href = this.href;
34202         }
34203         
34204         if(this.title.length){
34205             cfg.cn[0].cn[0].cn.push({
34206                 tag: 'h4',
34207                 cls: 'masonry-brick-title',
34208                 html: this.title
34209             });
34210         }
34211         
34212         if(this.html.length){
34213             cfg.cn[1].cn.push({
34214                 tag: 'p',
34215                 cls: 'masonry-brick-text',
34216                 html: this.html
34217             });
34218         }
34219
34220         if(this.bgimage.length){
34221             cfg.cn[0].cn.push({
34222                 tag: 'img',
34223                 cls: 'masonry-brick-image-view',
34224                 src: this.bgimage
34225             });
34226         }
34227         
34228         if(this.videourl.length){
34229             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
34230             // youtube support only?
34231             cfg.cn[0].cn.cn.push({
34232                 tag: 'iframe',
34233                 cls: 'masonry-brick-image-view',
34234                 src: vurl,
34235                 frameborder : 0,
34236                 allowfullscreen : true
34237             });
34238         }
34239         
34240         return cfg;
34241     },
34242     
34243     initEvents: function() 
34244     {
34245         switch (this.size) {
34246             case 'xs' :
34247                 this.x = 1;
34248                 this.y = 1;
34249                 break;
34250             case 'sm' :
34251                 this.x = 2;
34252                 this.y = 2;
34253                 break;
34254             case 'md' :
34255             case 'md-left' :
34256             case 'md-right' :
34257                 this.x = 3;
34258                 this.y = 3;
34259                 break;
34260             case 'tall' :
34261                 this.x = 2;
34262                 this.y = 3;
34263                 break;
34264             case 'wide' :
34265                 this.x = 3;
34266                 this.y = 2;
34267                 break;
34268             case 'wide-thin' :
34269                 this.x = 3;
34270                 this.y = 1;
34271                 break;
34272                         
34273             default :
34274                 break;
34275         }
34276         
34277         if(Roo.isTouch){
34278             this.el.on('touchstart', this.onTouchStart, this);
34279             this.el.on('touchmove', this.onTouchMove, this);
34280             this.el.on('touchend', this.onTouchEnd, this);
34281             this.el.on('contextmenu', this.onContextMenu, this);
34282         } else {
34283             this.el.on('mouseenter'  ,this.enter, this);
34284             this.el.on('mouseleave', this.leave, this);
34285             this.el.on('click', this.onClick, this);
34286         }
34287         
34288         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
34289             this.parent().bricks.push(this);   
34290         }
34291         
34292     },
34293     
34294     onClick: function(e, el)
34295     {
34296         var time = this.endTimer - this.startTimer;
34297         // Roo.log(e.preventDefault());
34298         if(Roo.isTouch){
34299             if(time > 1000){
34300                 e.preventDefault();
34301                 return;
34302             }
34303         }
34304         
34305         if(!this.preventDefault){
34306             return;
34307         }
34308         
34309         e.preventDefault();
34310         
34311         if (this.activeClass != '') {
34312             this.selectBrick();
34313         }
34314         
34315         this.fireEvent('click', this, e);
34316     },
34317     
34318     enter: function(e, el)
34319     {
34320         e.preventDefault();
34321         
34322         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
34323             return;
34324         }
34325         
34326         if(this.bgimage.length && this.html.length){
34327             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
34328         }
34329     },
34330     
34331     leave: function(e, el)
34332     {
34333         e.preventDefault();
34334         
34335         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
34336             return;
34337         }
34338         
34339         if(this.bgimage.length && this.html.length){
34340             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
34341         }
34342     },
34343     
34344     onTouchStart: function(e, el)
34345     {
34346 //        e.preventDefault();
34347         
34348         this.touchmoved = false;
34349         
34350         if(!this.isFitContainer){
34351             return;
34352         }
34353         
34354         if(!this.bgimage.length || !this.html.length){
34355             return;
34356         }
34357         
34358         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
34359         
34360         this.timer = new Date().getTime();
34361         
34362     },
34363     
34364     onTouchMove: function(e, el)
34365     {
34366         this.touchmoved = true;
34367     },
34368     
34369     onContextMenu : function(e,el)
34370     {
34371         e.preventDefault();
34372         e.stopPropagation();
34373         return false;
34374     },
34375     
34376     onTouchEnd: function(e, el)
34377     {
34378 //        e.preventDefault();
34379         
34380         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
34381         
34382             this.leave(e,el);
34383             
34384             return;
34385         }
34386         
34387         if(!this.bgimage.length || !this.html.length){
34388             
34389             if(this.href.length){
34390                 window.location.href = this.href;
34391             }
34392             
34393             return;
34394         }
34395         
34396         if(!this.isFitContainer){
34397             return;
34398         }
34399         
34400         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
34401         
34402         window.location.href = this.href;
34403     },
34404     
34405     //selection on single brick only
34406     selectBrick : function() {
34407         
34408         if (!this.parentId) {
34409             return;
34410         }
34411         
34412         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
34413         var index = m.selectedBrick.indexOf(this.id);
34414         
34415         if ( index > -1) {
34416             m.selectedBrick.splice(index,1);
34417             this.el.removeClass(this.activeClass);
34418             return;
34419         }
34420         
34421         for(var i = 0; i < m.selectedBrick.length; i++) {
34422             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
34423             b.el.removeClass(b.activeClass);
34424         }
34425         
34426         m.selectedBrick = [];
34427         
34428         m.selectedBrick.push(this.id);
34429         this.el.addClass(this.activeClass);
34430         return;
34431     },
34432     
34433     isSelected : function(){
34434         return this.el.hasClass(this.activeClass);
34435         
34436     }
34437 });
34438
34439 Roo.apply(Roo.bootstrap.MasonryBrick, {
34440     
34441     //groups: {},
34442     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
34443      /**
34444     * register a Masonry Brick
34445     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34446     */
34447     
34448     register : function(brick)
34449     {
34450         //this.groups[brick.id] = brick;
34451         this.groups.add(brick.id, brick);
34452     },
34453     /**
34454     * fetch a  masonry brick based on the masonry brick ID
34455     * @param {string} the masonry brick to add
34456     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
34457     */
34458     
34459     get: function(brick_id) 
34460     {
34461         // if (typeof(this.groups[brick_id]) == 'undefined') {
34462         //     return false;
34463         // }
34464         // return this.groups[brick_id] ;
34465         
34466         if(this.groups.key(brick_id)) {
34467             return this.groups.key(brick_id);
34468         }
34469         
34470         return false;
34471     }
34472     
34473     
34474     
34475 });
34476
34477  /*
34478  * - LGPL
34479  *
34480  * element
34481  * 
34482  */
34483
34484 /**
34485  * @class Roo.bootstrap.Brick
34486  * @extends Roo.bootstrap.Component
34487  * Bootstrap Brick class
34488  * 
34489  * @constructor
34490  * Create a new Brick
34491  * @param {Object} config The config object
34492  */
34493
34494 Roo.bootstrap.Brick = function(config){
34495     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
34496     
34497     this.addEvents({
34498         // raw events
34499         /**
34500          * @event click
34501          * When a Brick is click
34502          * @param {Roo.bootstrap.Brick} this
34503          * @param {Roo.EventObject} e
34504          */
34505         "click" : true
34506     });
34507 };
34508
34509 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
34510     
34511     /**
34512      * @cfg {String} title
34513      */   
34514     title : '',
34515     /**
34516      * @cfg {String} html
34517      */   
34518     html : '',
34519     /**
34520      * @cfg {String} bgimage
34521      */   
34522     bgimage : '',
34523     /**
34524      * @cfg {String} cls
34525      */   
34526     cls : '',
34527     /**
34528      * @cfg {String} href
34529      */   
34530     href : '',
34531     /**
34532      * @cfg {String} video
34533      */   
34534     video : '',
34535     /**
34536      * @cfg {Boolean} square
34537      */   
34538     square : true,
34539     
34540     getAutoCreate : function()
34541     {
34542         var cls = 'roo-brick';
34543         
34544         if(this.href.length){
34545             cls += ' roo-brick-link';
34546         }
34547         
34548         if(this.bgimage.length){
34549             cls += ' roo-brick-image';
34550         }
34551         
34552         if(!this.html.length && !this.bgimage.length){
34553             cls += ' roo-brick-center-title';
34554         }
34555         
34556         if(!this.html.length && this.bgimage.length){
34557             cls += ' roo-brick-bottom-title';
34558         }
34559         
34560         if(this.cls){
34561             cls += ' ' + this.cls;
34562         }
34563         
34564         var cfg = {
34565             tag: (this.href.length) ? 'a' : 'div',
34566             cls: cls,
34567             cn: [
34568                 {
34569                     tag: 'div',
34570                     cls: 'roo-brick-paragraph',
34571                     cn: []
34572                 }
34573             ]
34574         };
34575         
34576         if(this.href.length){
34577             cfg.href = this.href;
34578         }
34579         
34580         var cn = cfg.cn[0].cn;
34581         
34582         if(this.title.length){
34583             cn.push({
34584                 tag: 'h4',
34585                 cls: 'roo-brick-title',
34586                 html: this.title
34587             });
34588         }
34589         
34590         if(this.html.length){
34591             cn.push({
34592                 tag: 'p',
34593                 cls: 'roo-brick-text',
34594                 html: this.html
34595             });
34596         } else {
34597             cn.cls += ' hide';
34598         }
34599         
34600         if(this.bgimage.length){
34601             cfg.cn.push({
34602                 tag: 'img',
34603                 cls: 'roo-brick-image-view',
34604                 src: this.bgimage
34605             });
34606         }
34607         
34608         return cfg;
34609     },
34610     
34611     initEvents: function() 
34612     {
34613         if(this.title.length || this.html.length){
34614             this.el.on('mouseenter'  ,this.enter, this);
34615             this.el.on('mouseleave', this.leave, this);
34616         }
34617         
34618         Roo.EventManager.onWindowResize(this.resize, this); 
34619         
34620         if(this.bgimage.length){
34621             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
34622             this.imageEl.on('load', this.onImageLoad, this);
34623             return;
34624         }
34625         
34626         this.resize();
34627     },
34628     
34629     onImageLoad : function()
34630     {
34631         this.resize();
34632     },
34633     
34634     resize : function()
34635     {
34636         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
34637         
34638         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
34639         
34640         if(this.bgimage.length){
34641             var image = this.el.select('.roo-brick-image-view', true).first();
34642             
34643             image.setWidth(paragraph.getWidth());
34644             
34645             if(this.square){
34646                 image.setHeight(paragraph.getWidth());
34647             }
34648             
34649             this.el.setHeight(image.getHeight());
34650             paragraph.setHeight(image.getHeight());
34651             
34652         }
34653         
34654     },
34655     
34656     enter: function(e, el)
34657     {
34658         e.preventDefault();
34659         
34660         if(this.bgimage.length){
34661             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
34662             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
34663         }
34664     },
34665     
34666     leave: function(e, el)
34667     {
34668         e.preventDefault();
34669         
34670         if(this.bgimage.length){
34671             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
34672             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
34673         }
34674     }
34675     
34676 });
34677
34678  
34679
34680  /*
34681  * - LGPL
34682  *
34683  * Number field 
34684  */
34685
34686 /**
34687  * @class Roo.bootstrap.NumberField
34688  * @extends Roo.bootstrap.Input
34689  * Bootstrap NumberField class
34690  * 
34691  * 
34692  * 
34693  * 
34694  * @constructor
34695  * Create a new NumberField
34696  * @param {Object} config The config object
34697  */
34698
34699 Roo.bootstrap.NumberField = function(config){
34700     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
34701 };
34702
34703 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
34704     
34705     /**
34706      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
34707      */
34708     allowDecimals : true,
34709     /**
34710      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
34711      */
34712     decimalSeparator : ".",
34713     /**
34714      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
34715      */
34716     decimalPrecision : 2,
34717     /**
34718      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
34719      */
34720     allowNegative : true,
34721     
34722     /**
34723      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
34724      */
34725     allowZero: true,
34726     /**
34727      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
34728      */
34729     minValue : Number.NEGATIVE_INFINITY,
34730     /**
34731      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
34732      */
34733     maxValue : Number.MAX_VALUE,
34734     /**
34735      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
34736      */
34737     minText : "The minimum value for this field is {0}",
34738     /**
34739      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
34740      */
34741     maxText : "The maximum value for this field is {0}",
34742     /**
34743      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
34744      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
34745      */
34746     nanText : "{0} is not a valid number",
34747     /**
34748      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
34749      */
34750     thousandsDelimiter : false,
34751     /**
34752      * @cfg {String} valueAlign alignment of value
34753      */
34754     valueAlign : "left",
34755
34756     getAutoCreate : function()
34757     {
34758         var hiddenInput = {
34759             tag: 'input',
34760             type: 'hidden',
34761             id: Roo.id(),
34762             cls: 'hidden-number-input'
34763         };
34764         
34765         if (this.name) {
34766             hiddenInput.name = this.name;
34767         }
34768         
34769         this.name = '';
34770         
34771         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
34772         
34773         this.name = hiddenInput.name;
34774         
34775         if(cfg.cn.length > 0) {
34776             cfg.cn.push(hiddenInput);
34777         }
34778         
34779         return cfg;
34780     },
34781
34782     // private
34783     initEvents : function()
34784     {   
34785         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
34786         
34787         var allowed = "0123456789";
34788         
34789         if(this.allowDecimals){
34790             allowed += this.decimalSeparator;
34791         }
34792         
34793         if(this.allowNegative){
34794             allowed += "-";
34795         }
34796         
34797         if(this.thousandsDelimiter) {
34798             allowed += ",";
34799         }
34800         
34801         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
34802         
34803         var keyPress = function(e){
34804             
34805             var k = e.getKey();
34806             
34807             var c = e.getCharCode();
34808             
34809             if(
34810                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
34811                     allowed.indexOf(String.fromCharCode(c)) === -1
34812             ){
34813                 e.stopEvent();
34814                 return;
34815             }
34816             
34817             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
34818                 return;
34819             }
34820             
34821             if(allowed.indexOf(String.fromCharCode(c)) === -1){
34822                 e.stopEvent();
34823             }
34824         };
34825         
34826         this.el.on("keypress", keyPress, this);
34827     },
34828     
34829     validateValue : function(value)
34830     {
34831         
34832         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
34833             return false;
34834         }
34835         
34836         var num = this.parseValue(value);
34837         
34838         if(isNaN(num)){
34839             this.markInvalid(String.format(this.nanText, value));
34840             return false;
34841         }
34842         
34843         if(num < this.minValue){
34844             this.markInvalid(String.format(this.minText, this.minValue));
34845             return false;
34846         }
34847         
34848         if(num > this.maxValue){
34849             this.markInvalid(String.format(this.maxText, this.maxValue));
34850             return false;
34851         }
34852         
34853         return true;
34854     },
34855
34856     getValue : function()
34857     {
34858         var v = this.hiddenEl().getValue();
34859         
34860         return this.fixPrecision(this.parseValue(v));
34861     },
34862
34863     parseValue : function(value)
34864     {
34865         if(this.thousandsDelimiter) {
34866             value += "";
34867             r = new RegExp(",", "g");
34868             value = value.replace(r, "");
34869         }
34870         
34871         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
34872         return isNaN(value) ? '' : value;
34873     },
34874
34875     fixPrecision : function(value)
34876     {
34877         if(this.thousandsDelimiter) {
34878             value += "";
34879             r = new RegExp(",", "g");
34880             value = value.replace(r, "");
34881         }
34882         
34883         var nan = isNaN(value);
34884         
34885         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
34886             return nan ? '' : value;
34887         }
34888         return parseFloat(value).toFixed(this.decimalPrecision);
34889     },
34890
34891     setValue : function(v)
34892     {
34893         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
34894         
34895         this.value = v;
34896         
34897         if(this.rendered){
34898             
34899             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
34900             
34901             this.inputEl().dom.value = (v == '') ? '' :
34902                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
34903             
34904             if(!this.allowZero && v === '0') {
34905                 this.hiddenEl().dom.value = '';
34906                 this.inputEl().dom.value = '';
34907             }
34908             
34909             this.validate();
34910         }
34911     },
34912
34913     decimalPrecisionFcn : function(v)
34914     {
34915         return Math.floor(v);
34916     },
34917
34918     beforeBlur : function()
34919     {
34920         var v = this.parseValue(this.getRawValue());
34921         
34922         if(v || v === 0 || v === ''){
34923             this.setValue(v);
34924         }
34925     },
34926     
34927     hiddenEl : function()
34928     {
34929         return this.el.select('input.hidden-number-input',true).first();
34930     }
34931     
34932 });
34933
34934  
34935
34936 /*
34937 * Licence: LGPL
34938 */
34939
34940 /**
34941  * @class Roo.bootstrap.DocumentSlider
34942  * @extends Roo.bootstrap.Component
34943  * Bootstrap DocumentSlider class
34944  * 
34945  * @constructor
34946  * Create a new DocumentViewer
34947  * @param {Object} config The config object
34948  */
34949
34950 Roo.bootstrap.DocumentSlider = function(config){
34951     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
34952     
34953     this.files = [];
34954     
34955     this.addEvents({
34956         /**
34957          * @event initial
34958          * Fire after initEvent
34959          * @param {Roo.bootstrap.DocumentSlider} this
34960          */
34961         "initial" : true,
34962         /**
34963          * @event update
34964          * Fire after update
34965          * @param {Roo.bootstrap.DocumentSlider} this
34966          */
34967         "update" : true,
34968         /**
34969          * @event click
34970          * Fire after click
34971          * @param {Roo.bootstrap.DocumentSlider} this
34972          */
34973         "click" : true
34974     });
34975 };
34976
34977 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
34978     
34979     files : false,
34980     
34981     indicator : 0,
34982     
34983     getAutoCreate : function()
34984     {
34985         var cfg = {
34986             tag : 'div',
34987             cls : 'roo-document-slider',
34988             cn : [
34989                 {
34990                     tag : 'div',
34991                     cls : 'roo-document-slider-header',
34992                     cn : [
34993                         {
34994                             tag : 'div',
34995                             cls : 'roo-document-slider-header-title'
34996                         }
34997                     ]
34998                 },
34999                 {
35000                     tag : 'div',
35001                     cls : 'roo-document-slider-body',
35002                     cn : [
35003                         {
35004                             tag : 'div',
35005                             cls : 'roo-document-slider-prev',
35006                             cn : [
35007                                 {
35008                                     tag : 'i',
35009                                     cls : 'fa fa-chevron-left'
35010                                 }
35011                             ]
35012                         },
35013                         {
35014                             tag : 'div',
35015                             cls : 'roo-document-slider-thumb',
35016                             cn : [
35017                                 {
35018                                     tag : 'img',
35019                                     cls : 'roo-document-slider-image'
35020                                 }
35021                             ]
35022                         },
35023                         {
35024                             tag : 'div',
35025                             cls : 'roo-document-slider-next',
35026                             cn : [
35027                                 {
35028                                     tag : 'i',
35029                                     cls : 'fa fa-chevron-right'
35030                                 }
35031                             ]
35032                         }
35033                     ]
35034                 }
35035             ]
35036         };
35037         
35038         return cfg;
35039     },
35040     
35041     initEvents : function()
35042     {
35043         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
35044         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
35045         
35046         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
35047         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
35048         
35049         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
35050         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
35051         
35052         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
35053         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
35054         
35055         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
35056         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
35057         
35058         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
35059         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
35060         
35061         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
35062         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
35063         
35064         this.thumbEl.on('click', this.onClick, this);
35065         
35066         this.prevIndicator.on('click', this.prev, this);
35067         
35068         this.nextIndicator.on('click', this.next, this);
35069         
35070     },
35071     
35072     initial : function()
35073     {
35074         if(this.files.length){
35075             this.indicator = 1;
35076             this.update()
35077         }
35078         
35079         this.fireEvent('initial', this);
35080     },
35081     
35082     update : function()
35083     {
35084         this.imageEl.attr('src', this.files[this.indicator - 1]);
35085         
35086         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
35087         
35088         this.prevIndicator.show();
35089         
35090         if(this.indicator == 1){
35091             this.prevIndicator.hide();
35092         }
35093         
35094         this.nextIndicator.show();
35095         
35096         if(this.indicator == this.files.length){
35097             this.nextIndicator.hide();
35098         }
35099         
35100         this.thumbEl.scrollTo('top');
35101         
35102         this.fireEvent('update', this);
35103     },
35104     
35105     onClick : function(e)
35106     {
35107         e.preventDefault();
35108         
35109         this.fireEvent('click', this);
35110     },
35111     
35112     prev : function(e)
35113     {
35114         e.preventDefault();
35115         
35116         this.indicator = Math.max(1, this.indicator - 1);
35117         
35118         this.update();
35119     },
35120     
35121     next : function(e)
35122     {
35123         e.preventDefault();
35124         
35125         this.indicator = Math.min(this.files.length, this.indicator + 1);
35126         
35127         this.update();
35128     }
35129 });
35130 /*
35131  * - LGPL
35132  *
35133  * RadioSet
35134  *
35135  *
35136  */
35137
35138 /**
35139  * @class Roo.bootstrap.RadioSet
35140  * @extends Roo.bootstrap.Input
35141  * Bootstrap RadioSet class
35142  * @cfg {String} indicatorpos (left|right) default left
35143  * @cfg {Boolean} inline (true|false) inline the element (default true)
35144  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
35145  * @constructor
35146  * Create a new RadioSet
35147  * @param {Object} config The config object
35148  */
35149
35150 Roo.bootstrap.RadioSet = function(config){
35151     
35152     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
35153     
35154     this.radioes = [];
35155     
35156     Roo.bootstrap.RadioSet.register(this);
35157     
35158     this.addEvents({
35159         /**
35160         * @event check
35161         * Fires when the element is checked or unchecked.
35162         * @param {Roo.bootstrap.RadioSet} this This radio
35163         * @param {Roo.bootstrap.Radio} item The checked item
35164         */
35165        check : true,
35166        /**
35167         * @event click
35168         * Fires when the element is click.
35169         * @param {Roo.bootstrap.RadioSet} this This radio set
35170         * @param {Roo.bootstrap.Radio} item The checked item
35171         * @param {Roo.EventObject} e The event object
35172         */
35173        click : true
35174     });
35175     
35176 };
35177
35178 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
35179
35180     radioes : false,
35181     
35182     inline : true,
35183     
35184     weight : '',
35185     
35186     indicatorpos : 'left',
35187     
35188     getAutoCreate : function()
35189     {
35190         var label = {
35191             tag : 'label',
35192             cls : 'roo-radio-set-label',
35193             cn : [
35194                 {
35195                     tag : 'span',
35196                     html : this.fieldLabel
35197                 }
35198             ]
35199         };
35200         if (Roo.bootstrap.version == 3) {
35201             
35202             
35203             if(this.indicatorpos == 'left'){
35204                 label.cn.unshift({
35205                     tag : 'i',
35206                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
35207                     tooltip : 'This field is required'
35208                 });
35209             } else {
35210                 label.cn.push({
35211                     tag : 'i',
35212                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
35213                     tooltip : 'This field is required'
35214                 });
35215             }
35216         }
35217         var items = {
35218             tag : 'div',
35219             cls : 'roo-radio-set-items'
35220         };
35221         
35222         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
35223         
35224         if (align === 'left' && this.fieldLabel.length) {
35225             
35226             items = {
35227                 cls : "roo-radio-set-right", 
35228                 cn: [
35229                     items
35230                 ]
35231             };
35232             
35233             if(this.labelWidth > 12){
35234                 label.style = "width: " + this.labelWidth + 'px';
35235             }
35236             
35237             if(this.labelWidth < 13 && this.labelmd == 0){
35238                 this.labelmd = this.labelWidth;
35239             }
35240             
35241             if(this.labellg > 0){
35242                 label.cls += ' col-lg-' + this.labellg;
35243                 items.cls += ' col-lg-' + (12 - this.labellg);
35244             }
35245             
35246             if(this.labelmd > 0){
35247                 label.cls += ' col-md-' + this.labelmd;
35248                 items.cls += ' col-md-' + (12 - this.labelmd);
35249             }
35250             
35251             if(this.labelsm > 0){
35252                 label.cls += ' col-sm-' + this.labelsm;
35253                 items.cls += ' col-sm-' + (12 - this.labelsm);
35254             }
35255             
35256             if(this.labelxs > 0){
35257                 label.cls += ' col-xs-' + this.labelxs;
35258                 items.cls += ' col-xs-' + (12 - this.labelxs);
35259             }
35260         }
35261         
35262         var cfg = {
35263             tag : 'div',
35264             cls : 'roo-radio-set',
35265             cn : [
35266                 {
35267                     tag : 'input',
35268                     cls : 'roo-radio-set-input',
35269                     type : 'hidden',
35270                     name : this.name,
35271                     value : this.value ? this.value :  ''
35272                 },
35273                 label,
35274                 items
35275             ]
35276         };
35277         
35278         if(this.weight.length){
35279             cfg.cls += ' roo-radio-' + this.weight;
35280         }
35281         
35282         if(this.inline) {
35283             cfg.cls += ' roo-radio-set-inline';
35284         }
35285         
35286         var settings=this;
35287         ['xs','sm','md','lg'].map(function(size){
35288             if (settings[size]) {
35289                 cfg.cls += ' col-' + size + '-' + settings[size];
35290             }
35291         });
35292         
35293         return cfg;
35294         
35295     },
35296
35297     initEvents : function()
35298     {
35299         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
35300         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
35301         
35302         if(!this.fieldLabel.length){
35303             this.labelEl.hide();
35304         }
35305         
35306         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
35307         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
35308         
35309         this.indicator = this.indicatorEl();
35310         
35311         if(this.indicator){
35312             this.indicator.addClass('invisible');
35313         }
35314         
35315         this.originalValue = this.getValue();
35316         
35317     },
35318     
35319     inputEl: function ()
35320     {
35321         return this.el.select('.roo-radio-set-input', true).first();
35322     },
35323     
35324     getChildContainer : function()
35325     {
35326         return this.itemsEl;
35327     },
35328     
35329     register : function(item)
35330     {
35331         this.radioes.push(item);
35332         
35333     },
35334     
35335     validate : function()
35336     {   
35337         if(this.getVisibilityEl().hasClass('hidden')){
35338             return true;
35339         }
35340         
35341         var valid = false;
35342         
35343         Roo.each(this.radioes, function(i){
35344             if(!i.checked){
35345                 return;
35346             }
35347             
35348             valid = true;
35349             return false;
35350         });
35351         
35352         if(this.allowBlank) {
35353             return true;
35354         }
35355         
35356         if(this.disabled || valid){
35357             this.markValid();
35358             return true;
35359         }
35360         
35361         this.markInvalid();
35362         return false;
35363         
35364     },
35365     
35366     markValid : function()
35367     {
35368         if(this.labelEl.isVisible(true) && this.indicatorEl()){
35369             this.indicatorEl().removeClass('visible');
35370             this.indicatorEl().addClass('invisible');
35371         }
35372         
35373         
35374         if (Roo.bootstrap.version == 3) {
35375             this.el.removeClass([this.invalidClass, this.validClass]);
35376             this.el.addClass(this.validClass);
35377         } else {
35378             this.el.removeClass(['is-invalid','is-valid']);
35379             this.el.addClass(['is-valid']);
35380         }
35381         this.fireEvent('valid', this);
35382     },
35383     
35384     markInvalid : function(msg)
35385     {
35386         if(this.allowBlank || this.disabled){
35387             return;
35388         }
35389         
35390         if(this.labelEl.isVisible(true) && this.indicatorEl()){
35391             this.indicatorEl().removeClass('invisible');
35392             this.indicatorEl().addClass('visible');
35393         }
35394         if (Roo.bootstrap.version == 3) {
35395             this.el.removeClass([this.invalidClass, this.validClass]);
35396             this.el.addClass(this.invalidClass);
35397         } else {
35398             this.el.removeClass(['is-invalid','is-valid']);
35399             this.el.addClass(['is-invalid']);
35400         }
35401         
35402         this.fireEvent('invalid', this, msg);
35403         
35404     },
35405     
35406     setValue : function(v, suppressEvent)
35407     {   
35408         if(this.value === v){
35409             return;
35410         }
35411         
35412         this.value = v;
35413         
35414         if(this.rendered){
35415             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
35416         }
35417         
35418         Roo.each(this.radioes, function(i){
35419             i.checked = false;
35420             i.el.removeClass('checked');
35421         });
35422         
35423         Roo.each(this.radioes, function(i){
35424             
35425             if(i.value === v || i.value.toString() === v.toString()){
35426                 i.checked = true;
35427                 i.el.addClass('checked');
35428                 
35429                 if(suppressEvent !== true){
35430                     this.fireEvent('check', this, i);
35431                 }
35432                 
35433                 return false;
35434             }
35435             
35436         }, this);
35437         
35438         this.validate();
35439     },
35440     
35441     clearInvalid : function(){
35442         
35443         if(!this.el || this.preventMark){
35444             return;
35445         }
35446         
35447         this.el.removeClass([this.invalidClass]);
35448         
35449         this.fireEvent('valid', this);
35450     }
35451     
35452 });
35453
35454 Roo.apply(Roo.bootstrap.RadioSet, {
35455     
35456     groups: {},
35457     
35458     register : function(set)
35459     {
35460         this.groups[set.name] = set;
35461     },
35462     
35463     get: function(name) 
35464     {
35465         if (typeof(this.groups[name]) == 'undefined') {
35466             return false;
35467         }
35468         
35469         return this.groups[name] ;
35470     }
35471     
35472 });
35473 /*
35474  * Based on:
35475  * Ext JS Library 1.1.1
35476  * Copyright(c) 2006-2007, Ext JS, LLC.
35477  *
35478  * Originally Released Under LGPL - original licence link has changed is not relivant.
35479  *
35480  * Fork - LGPL
35481  * <script type="text/javascript">
35482  */
35483
35484
35485 /**
35486  * @class Roo.bootstrap.SplitBar
35487  * @extends Roo.util.Observable
35488  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
35489  * <br><br>
35490  * Usage:
35491  * <pre><code>
35492 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
35493                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
35494 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
35495 split.minSize = 100;
35496 split.maxSize = 600;
35497 split.animate = true;
35498 split.on('moved', splitterMoved);
35499 </code></pre>
35500  * @constructor
35501  * Create a new SplitBar
35502  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
35503  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
35504  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
35505  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
35506                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
35507                         position of the SplitBar).
35508  */
35509 Roo.bootstrap.SplitBar = function(cfg){
35510     
35511     /** @private */
35512     
35513     //{
35514     //  dragElement : elm
35515     //  resizingElement: el,
35516         // optional..
35517     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
35518     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
35519         // existingProxy ???
35520     //}
35521     
35522     this.el = Roo.get(cfg.dragElement, true);
35523     this.el.dom.unselectable = "on";
35524     /** @private */
35525     this.resizingEl = Roo.get(cfg.resizingElement, true);
35526
35527     /**
35528      * @private
35529      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
35530      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
35531      * @type Number
35532      */
35533     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
35534     
35535     /**
35536      * The minimum size of the resizing element. (Defaults to 0)
35537      * @type Number
35538      */
35539     this.minSize = 0;
35540     
35541     /**
35542      * The maximum size of the resizing element. (Defaults to 2000)
35543      * @type Number
35544      */
35545     this.maxSize = 2000;
35546     
35547     /**
35548      * Whether to animate the transition to the new size
35549      * @type Boolean
35550      */
35551     this.animate = false;
35552     
35553     /**
35554      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
35555      * @type Boolean
35556      */
35557     this.useShim = false;
35558     
35559     /** @private */
35560     this.shim = null;
35561     
35562     if(!cfg.existingProxy){
35563         /** @private */
35564         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
35565     }else{
35566         this.proxy = Roo.get(cfg.existingProxy).dom;
35567     }
35568     /** @private */
35569     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
35570     
35571     /** @private */
35572     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
35573     
35574     /** @private */
35575     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
35576     
35577     /** @private */
35578     this.dragSpecs = {};
35579     
35580     /**
35581      * @private The adapter to use to positon and resize elements
35582      */
35583     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
35584     this.adapter.init(this);
35585     
35586     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35587         /** @private */
35588         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
35589         this.el.addClass("roo-splitbar-h");
35590     }else{
35591         /** @private */
35592         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
35593         this.el.addClass("roo-splitbar-v");
35594     }
35595     
35596     this.addEvents({
35597         /**
35598          * @event resize
35599          * Fires when the splitter is moved (alias for {@link #event-moved})
35600          * @param {Roo.bootstrap.SplitBar} this
35601          * @param {Number} newSize the new width or height
35602          */
35603         "resize" : true,
35604         /**
35605          * @event moved
35606          * Fires when the splitter is moved
35607          * @param {Roo.bootstrap.SplitBar} this
35608          * @param {Number} newSize the new width or height
35609          */
35610         "moved" : true,
35611         /**
35612          * @event beforeresize
35613          * Fires before the splitter is dragged
35614          * @param {Roo.bootstrap.SplitBar} this
35615          */
35616         "beforeresize" : true,
35617
35618         "beforeapply" : true
35619     });
35620
35621     Roo.util.Observable.call(this);
35622 };
35623
35624 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
35625     onStartProxyDrag : function(x, y){
35626         this.fireEvent("beforeresize", this);
35627         if(!this.overlay){
35628             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
35629             o.unselectable();
35630             o.enableDisplayMode("block");
35631             // all splitbars share the same overlay
35632             Roo.bootstrap.SplitBar.prototype.overlay = o;
35633         }
35634         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
35635         this.overlay.show();
35636         Roo.get(this.proxy).setDisplayed("block");
35637         var size = this.adapter.getElementSize(this);
35638         this.activeMinSize = this.getMinimumSize();;
35639         this.activeMaxSize = this.getMaximumSize();;
35640         var c1 = size - this.activeMinSize;
35641         var c2 = Math.max(this.activeMaxSize - size, 0);
35642         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35643             this.dd.resetConstraints();
35644             this.dd.setXConstraint(
35645                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
35646                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
35647             );
35648             this.dd.setYConstraint(0, 0);
35649         }else{
35650             this.dd.resetConstraints();
35651             this.dd.setXConstraint(0, 0);
35652             this.dd.setYConstraint(
35653                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
35654                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
35655             );
35656          }
35657         this.dragSpecs.startSize = size;
35658         this.dragSpecs.startPoint = [x, y];
35659         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
35660     },
35661     
35662     /** 
35663      * @private Called after the drag operation by the DDProxy
35664      */
35665     onEndProxyDrag : function(e){
35666         Roo.get(this.proxy).setDisplayed(false);
35667         var endPoint = Roo.lib.Event.getXY(e);
35668         if(this.overlay){
35669             this.overlay.hide();
35670         }
35671         var newSize;
35672         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35673             newSize = this.dragSpecs.startSize + 
35674                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
35675                     endPoint[0] - this.dragSpecs.startPoint[0] :
35676                     this.dragSpecs.startPoint[0] - endPoint[0]
35677                 );
35678         }else{
35679             newSize = this.dragSpecs.startSize + 
35680                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
35681                     endPoint[1] - this.dragSpecs.startPoint[1] :
35682                     this.dragSpecs.startPoint[1] - endPoint[1]
35683                 );
35684         }
35685         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
35686         if(newSize != this.dragSpecs.startSize){
35687             if(this.fireEvent('beforeapply', this, newSize) !== false){
35688                 this.adapter.setElementSize(this, newSize);
35689                 this.fireEvent("moved", this, newSize);
35690                 this.fireEvent("resize", this, newSize);
35691             }
35692         }
35693     },
35694     
35695     /**
35696      * Get the adapter this SplitBar uses
35697      * @return The adapter object
35698      */
35699     getAdapter : function(){
35700         return this.adapter;
35701     },
35702     
35703     /**
35704      * Set the adapter this SplitBar uses
35705      * @param {Object} adapter A SplitBar adapter object
35706      */
35707     setAdapter : function(adapter){
35708         this.adapter = adapter;
35709         this.adapter.init(this);
35710     },
35711     
35712     /**
35713      * Gets the minimum size for the resizing element
35714      * @return {Number} The minimum size
35715      */
35716     getMinimumSize : function(){
35717         return this.minSize;
35718     },
35719     
35720     /**
35721      * Sets the minimum size for the resizing element
35722      * @param {Number} minSize The minimum size
35723      */
35724     setMinimumSize : function(minSize){
35725         this.minSize = minSize;
35726     },
35727     
35728     /**
35729      * Gets the maximum size for the resizing element
35730      * @return {Number} The maximum size
35731      */
35732     getMaximumSize : function(){
35733         return this.maxSize;
35734     },
35735     
35736     /**
35737      * Sets the maximum size for the resizing element
35738      * @param {Number} maxSize The maximum size
35739      */
35740     setMaximumSize : function(maxSize){
35741         this.maxSize = maxSize;
35742     },
35743     
35744     /**
35745      * Sets the initialize size for the resizing element
35746      * @param {Number} size The initial size
35747      */
35748     setCurrentSize : function(size){
35749         var oldAnimate = this.animate;
35750         this.animate = false;
35751         this.adapter.setElementSize(this, size);
35752         this.animate = oldAnimate;
35753     },
35754     
35755     /**
35756      * Destroy this splitbar. 
35757      * @param {Boolean} removeEl True to remove the element
35758      */
35759     destroy : function(removeEl){
35760         if(this.shim){
35761             this.shim.remove();
35762         }
35763         this.dd.unreg();
35764         this.proxy.parentNode.removeChild(this.proxy);
35765         if(removeEl){
35766             this.el.remove();
35767         }
35768     }
35769 });
35770
35771 /**
35772  * @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.
35773  */
35774 Roo.bootstrap.SplitBar.createProxy = function(dir){
35775     var proxy = new Roo.Element(document.createElement("div"));
35776     proxy.unselectable();
35777     var cls = 'roo-splitbar-proxy';
35778     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
35779     document.body.appendChild(proxy.dom);
35780     return proxy.dom;
35781 };
35782
35783 /** 
35784  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
35785  * Default Adapter. It assumes the splitter and resizing element are not positioned
35786  * elements and only gets/sets the width of the element. Generally used for table based layouts.
35787  */
35788 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
35789 };
35790
35791 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
35792     // do nothing for now
35793     init : function(s){
35794     
35795     },
35796     /**
35797      * Called before drag operations to get the current size of the resizing element. 
35798      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
35799      */
35800      getElementSize : function(s){
35801         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35802             return s.resizingEl.getWidth();
35803         }else{
35804             return s.resizingEl.getHeight();
35805         }
35806     },
35807     
35808     /**
35809      * Called after drag operations to set the size of the resizing element.
35810      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
35811      * @param {Number} newSize The new size to set
35812      * @param {Function} onComplete A function to be invoked when resizing is complete
35813      */
35814     setElementSize : function(s, newSize, onComplete){
35815         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35816             if(!s.animate){
35817                 s.resizingEl.setWidth(newSize);
35818                 if(onComplete){
35819                     onComplete(s, newSize);
35820                 }
35821             }else{
35822                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
35823             }
35824         }else{
35825             
35826             if(!s.animate){
35827                 s.resizingEl.setHeight(newSize);
35828                 if(onComplete){
35829                     onComplete(s, newSize);
35830                 }
35831             }else{
35832                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
35833             }
35834         }
35835     }
35836 };
35837
35838 /** 
35839  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
35840  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
35841  * Adapter that  moves the splitter element to align with the resized sizing element. 
35842  * Used with an absolute positioned SplitBar.
35843  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
35844  * document.body, make sure you assign an id to the body element.
35845  */
35846 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
35847     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
35848     this.container = Roo.get(container);
35849 };
35850
35851 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
35852     init : function(s){
35853         this.basic.init(s);
35854     },
35855     
35856     getElementSize : function(s){
35857         return this.basic.getElementSize(s);
35858     },
35859     
35860     setElementSize : function(s, newSize, onComplete){
35861         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
35862     },
35863     
35864     moveSplitter : function(s){
35865         var yes = Roo.bootstrap.SplitBar;
35866         switch(s.placement){
35867             case yes.LEFT:
35868                 s.el.setX(s.resizingEl.getRight());
35869                 break;
35870             case yes.RIGHT:
35871                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
35872                 break;
35873             case yes.TOP:
35874                 s.el.setY(s.resizingEl.getBottom());
35875                 break;
35876             case yes.BOTTOM:
35877                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
35878                 break;
35879         }
35880     }
35881 };
35882
35883 /**
35884  * Orientation constant - Create a vertical SplitBar
35885  * @static
35886  * @type Number
35887  */
35888 Roo.bootstrap.SplitBar.VERTICAL = 1;
35889
35890 /**
35891  * Orientation constant - Create a horizontal SplitBar
35892  * @static
35893  * @type Number
35894  */
35895 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
35896
35897 /**
35898  * Placement constant - The resizing element is to the left of the splitter element
35899  * @static
35900  * @type Number
35901  */
35902 Roo.bootstrap.SplitBar.LEFT = 1;
35903
35904 /**
35905  * Placement constant - The resizing element is to the right of the splitter element
35906  * @static
35907  * @type Number
35908  */
35909 Roo.bootstrap.SplitBar.RIGHT = 2;
35910
35911 /**
35912  * Placement constant - The resizing element is positioned above the splitter element
35913  * @static
35914  * @type Number
35915  */
35916 Roo.bootstrap.SplitBar.TOP = 3;
35917
35918 /**
35919  * Placement constant - The resizing element is positioned under splitter element
35920  * @static
35921  * @type Number
35922  */
35923 Roo.bootstrap.SplitBar.BOTTOM = 4;
35924 Roo.namespace("Roo.bootstrap.layout");/*
35925  * Based on:
35926  * Ext JS Library 1.1.1
35927  * Copyright(c) 2006-2007, Ext JS, LLC.
35928  *
35929  * Originally Released Under LGPL - original licence link has changed is not relivant.
35930  *
35931  * Fork - LGPL
35932  * <script type="text/javascript">
35933  */
35934
35935 /**
35936  * @class Roo.bootstrap.layout.Manager
35937  * @extends Roo.bootstrap.Component
35938  * Base class for layout managers.
35939  */
35940 Roo.bootstrap.layout.Manager = function(config)
35941 {
35942     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
35943
35944
35945
35946
35947
35948     /** false to disable window resize monitoring @type Boolean */
35949     this.monitorWindowResize = true;
35950     this.regions = {};
35951     this.addEvents({
35952         /**
35953          * @event layout
35954          * Fires when a layout is performed.
35955          * @param {Roo.LayoutManager} this
35956          */
35957         "layout" : true,
35958         /**
35959          * @event regionresized
35960          * Fires when the user resizes a region.
35961          * @param {Roo.LayoutRegion} region The resized region
35962          * @param {Number} newSize The new size (width for east/west, height for north/south)
35963          */
35964         "regionresized" : true,
35965         /**
35966          * @event regioncollapsed
35967          * Fires when a region is collapsed.
35968          * @param {Roo.LayoutRegion} region The collapsed region
35969          */
35970         "regioncollapsed" : true,
35971         /**
35972          * @event regionexpanded
35973          * Fires when a region is expanded.
35974          * @param {Roo.LayoutRegion} region The expanded region
35975          */
35976         "regionexpanded" : true
35977     });
35978     this.updating = false;
35979
35980     if (config.el) {
35981         this.el = Roo.get(config.el);
35982         this.initEvents();
35983     }
35984
35985 };
35986
35987 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
35988
35989
35990     regions : null,
35991
35992     monitorWindowResize : true,
35993
35994
35995     updating : false,
35996
35997
35998     onRender : function(ct, position)
35999     {
36000         if(!this.el){
36001             this.el = Roo.get(ct);
36002             this.initEvents();
36003         }
36004         //this.fireEvent('render',this);
36005     },
36006
36007
36008     initEvents: function()
36009     {
36010
36011
36012         // ie scrollbar fix
36013         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
36014             document.body.scroll = "no";
36015         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
36016             this.el.position('relative');
36017         }
36018         this.id = this.el.id;
36019         this.el.addClass("roo-layout-container");
36020         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
36021         if(this.el.dom != document.body ) {
36022             this.el.on('resize', this.layout,this);
36023             this.el.on('show', this.layout,this);
36024         }
36025
36026     },
36027
36028     /**
36029      * Returns true if this layout is currently being updated
36030      * @return {Boolean}
36031      */
36032     isUpdating : function(){
36033         return this.updating;
36034     },
36035
36036     /**
36037      * Suspend the LayoutManager from doing auto-layouts while
36038      * making multiple add or remove calls
36039      */
36040     beginUpdate : function(){
36041         this.updating = true;
36042     },
36043
36044     /**
36045      * Restore auto-layouts and optionally disable the manager from performing a layout
36046      * @param {Boolean} noLayout true to disable a layout update
36047      */
36048     endUpdate : function(noLayout){
36049         this.updating = false;
36050         if(!noLayout){
36051             this.layout();
36052         }
36053     },
36054
36055     layout: function(){
36056         // abstract...
36057     },
36058
36059     onRegionResized : function(region, newSize){
36060         this.fireEvent("regionresized", region, newSize);
36061         this.layout();
36062     },
36063
36064     onRegionCollapsed : function(region){
36065         this.fireEvent("regioncollapsed", region);
36066     },
36067
36068     onRegionExpanded : function(region){
36069         this.fireEvent("regionexpanded", region);
36070     },
36071
36072     /**
36073      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
36074      * performs box-model adjustments.
36075      * @return {Object} The size as an object {width: (the width), height: (the height)}
36076      */
36077     getViewSize : function()
36078     {
36079         var size;
36080         if(this.el.dom != document.body){
36081             size = this.el.getSize();
36082         }else{
36083             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
36084         }
36085         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
36086         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
36087         return size;
36088     },
36089
36090     /**
36091      * Returns the Element this layout is bound to.
36092      * @return {Roo.Element}
36093      */
36094     getEl : function(){
36095         return this.el;
36096     },
36097
36098     /**
36099      * Returns the specified region.
36100      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
36101      * @return {Roo.LayoutRegion}
36102      */
36103     getRegion : function(target){
36104         return this.regions[target.toLowerCase()];
36105     },
36106
36107     onWindowResize : function(){
36108         if(this.monitorWindowResize){
36109             this.layout();
36110         }
36111     }
36112 });
36113 /*
36114  * Based on:
36115  * Ext JS Library 1.1.1
36116  * Copyright(c) 2006-2007, Ext JS, LLC.
36117  *
36118  * Originally Released Under LGPL - original licence link has changed is not relivant.
36119  *
36120  * Fork - LGPL
36121  * <script type="text/javascript">
36122  */
36123 /**
36124  * @class Roo.bootstrap.layout.Border
36125  * @extends Roo.bootstrap.layout.Manager
36126  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
36127  * please see: examples/bootstrap/nested.html<br><br>
36128  
36129 <b>The container the layout is rendered into can be either the body element or any other element.
36130 If it is not the body element, the container needs to either be an absolute positioned element,
36131 or you will need to add "position:relative" to the css of the container.  You will also need to specify
36132 the container size if it is not the body element.</b>
36133
36134 * @constructor
36135 * Create a new Border
36136 * @param {Object} config Configuration options
36137  */
36138 Roo.bootstrap.layout.Border = function(config){
36139     config = config || {};
36140     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
36141     
36142     
36143     
36144     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
36145         if(config[region]){
36146             config[region].region = region;
36147             this.addRegion(config[region]);
36148         }
36149     },this);
36150     
36151 };
36152
36153 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
36154
36155 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
36156     
36157     parent : false, // this might point to a 'nest' or a ???
36158     
36159     /**
36160      * Creates and adds a new region if it doesn't already exist.
36161      * @param {String} target The target region key (north, south, east, west or center).
36162      * @param {Object} config The regions config object
36163      * @return {BorderLayoutRegion} The new region
36164      */
36165     addRegion : function(config)
36166     {
36167         if(!this.regions[config.region]){
36168             var r = this.factory(config);
36169             this.bindRegion(r);
36170         }
36171         return this.regions[config.region];
36172     },
36173
36174     // private (kinda)
36175     bindRegion : function(r){
36176         this.regions[r.config.region] = r;
36177         
36178         r.on("visibilitychange",    this.layout, this);
36179         r.on("paneladded",          this.layout, this);
36180         r.on("panelremoved",        this.layout, this);
36181         r.on("invalidated",         this.layout, this);
36182         r.on("resized",             this.onRegionResized, this);
36183         r.on("collapsed",           this.onRegionCollapsed, this);
36184         r.on("expanded",            this.onRegionExpanded, this);
36185     },
36186
36187     /**
36188      * Performs a layout update.
36189      */
36190     layout : function()
36191     {
36192         if(this.updating) {
36193             return;
36194         }
36195         
36196         // render all the rebions if they have not been done alreayd?
36197         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
36198             if(this.regions[region] && !this.regions[region].bodyEl){
36199                 this.regions[region].onRender(this.el)
36200             }
36201         },this);
36202         
36203         var size = this.getViewSize();
36204         var w = size.width;
36205         var h = size.height;
36206         var centerW = w;
36207         var centerH = h;
36208         var centerY = 0;
36209         var centerX = 0;
36210         //var x = 0, y = 0;
36211
36212         var rs = this.regions;
36213         var north = rs["north"];
36214         var south = rs["south"]; 
36215         var west = rs["west"];
36216         var east = rs["east"];
36217         var center = rs["center"];
36218         //if(this.hideOnLayout){ // not supported anymore
36219             //c.el.setStyle("display", "none");
36220         //}
36221         if(north && north.isVisible()){
36222             var b = north.getBox();
36223             var m = north.getMargins();
36224             b.width = w - (m.left+m.right);
36225             b.x = m.left;
36226             b.y = m.top;
36227             centerY = b.height + b.y + m.bottom;
36228             centerH -= centerY;
36229             north.updateBox(this.safeBox(b));
36230         }
36231         if(south && south.isVisible()){
36232             var b = south.getBox();
36233             var m = south.getMargins();
36234             b.width = w - (m.left+m.right);
36235             b.x = m.left;
36236             var totalHeight = (b.height + m.top + m.bottom);
36237             b.y = h - totalHeight + m.top;
36238             centerH -= totalHeight;
36239             south.updateBox(this.safeBox(b));
36240         }
36241         if(west && west.isVisible()){
36242             var b = west.getBox();
36243             var m = west.getMargins();
36244             b.height = centerH - (m.top+m.bottom);
36245             b.x = m.left;
36246             b.y = centerY + m.top;
36247             var totalWidth = (b.width + m.left + m.right);
36248             centerX += totalWidth;
36249             centerW -= totalWidth;
36250             west.updateBox(this.safeBox(b));
36251         }
36252         if(east && east.isVisible()){
36253             var b = east.getBox();
36254             var m = east.getMargins();
36255             b.height = centerH - (m.top+m.bottom);
36256             var totalWidth = (b.width + m.left + m.right);
36257             b.x = w - totalWidth + m.left;
36258             b.y = centerY + m.top;
36259             centerW -= totalWidth;
36260             east.updateBox(this.safeBox(b));
36261         }
36262         if(center){
36263             var m = center.getMargins();
36264             var centerBox = {
36265                 x: centerX + m.left,
36266                 y: centerY + m.top,
36267                 width: centerW - (m.left+m.right),
36268                 height: centerH - (m.top+m.bottom)
36269             };
36270             //if(this.hideOnLayout){
36271                 //center.el.setStyle("display", "block");
36272             //}
36273             center.updateBox(this.safeBox(centerBox));
36274         }
36275         this.el.repaint();
36276         this.fireEvent("layout", this);
36277     },
36278
36279     // private
36280     safeBox : function(box){
36281         box.width = Math.max(0, box.width);
36282         box.height = Math.max(0, box.height);
36283         return box;
36284     },
36285
36286     /**
36287      * Adds a ContentPanel (or subclass) to this layout.
36288      * @param {String} target The target region key (north, south, east, west or center).
36289      * @param {Roo.ContentPanel} panel The panel to add
36290      * @return {Roo.ContentPanel} The added panel
36291      */
36292     add : function(target, panel){
36293          
36294         target = target.toLowerCase();
36295         return this.regions[target].add(panel);
36296     },
36297
36298     /**
36299      * Remove a ContentPanel (or subclass) to this layout.
36300      * @param {String} target The target region key (north, south, east, west or center).
36301      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
36302      * @return {Roo.ContentPanel} The removed panel
36303      */
36304     remove : function(target, panel){
36305         target = target.toLowerCase();
36306         return this.regions[target].remove(panel);
36307     },
36308
36309     /**
36310      * Searches all regions for a panel with the specified id
36311      * @param {String} panelId
36312      * @return {Roo.ContentPanel} The panel or null if it wasn't found
36313      */
36314     findPanel : function(panelId){
36315         var rs = this.regions;
36316         for(var target in rs){
36317             if(typeof rs[target] != "function"){
36318                 var p = rs[target].getPanel(panelId);
36319                 if(p){
36320                     return p;
36321                 }
36322             }
36323         }
36324         return null;
36325     },
36326
36327     /**
36328      * Searches all regions for a panel with the specified id and activates (shows) it.
36329      * @param {String/ContentPanel} panelId The panels id or the panel itself
36330      * @return {Roo.ContentPanel} The shown panel or null
36331      */
36332     showPanel : function(panelId) {
36333       var rs = this.regions;
36334       for(var target in rs){
36335          var r = rs[target];
36336          if(typeof r != "function"){
36337             if(r.hasPanel(panelId)){
36338                return r.showPanel(panelId);
36339             }
36340          }
36341       }
36342       return null;
36343    },
36344
36345    /**
36346      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
36347      * @param {Roo.state.Provider} provider (optional) An alternate state provider
36348      */
36349    /*
36350     restoreState : function(provider){
36351         if(!provider){
36352             provider = Roo.state.Manager;
36353         }
36354         var sm = new Roo.LayoutStateManager();
36355         sm.init(this, provider);
36356     },
36357 */
36358  
36359  
36360     /**
36361      * Adds a xtype elements to the layout.
36362      * <pre><code>
36363
36364 layout.addxtype({
36365        xtype : 'ContentPanel',
36366        region: 'west',
36367        items: [ .... ]
36368    }
36369 );
36370
36371 layout.addxtype({
36372         xtype : 'NestedLayoutPanel',
36373         region: 'west',
36374         layout: {
36375            center: { },
36376            west: { }   
36377         },
36378         items : [ ... list of content panels or nested layout panels.. ]
36379    }
36380 );
36381 </code></pre>
36382      * @param {Object} cfg Xtype definition of item to add.
36383      */
36384     addxtype : function(cfg)
36385     {
36386         // basically accepts a pannel...
36387         // can accept a layout region..!?!?
36388         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
36389         
36390         
36391         // theory?  children can only be panels??
36392         
36393         //if (!cfg.xtype.match(/Panel$/)) {
36394         //    return false;
36395         //}
36396         var ret = false;
36397         
36398         if (typeof(cfg.region) == 'undefined') {
36399             Roo.log("Failed to add Panel, region was not set");
36400             Roo.log(cfg);
36401             return false;
36402         }
36403         var region = cfg.region;
36404         delete cfg.region;
36405         
36406           
36407         var xitems = [];
36408         if (cfg.items) {
36409             xitems = cfg.items;
36410             delete cfg.items;
36411         }
36412         var nb = false;
36413         
36414         if ( region == 'center') {
36415             Roo.log("Center: " + cfg.title);
36416         }
36417         
36418         
36419         switch(cfg.xtype) 
36420         {
36421             case 'Content':  // ContentPanel (el, cfg)
36422             case 'Scroll':  // ContentPanel (el, cfg)
36423             case 'View': 
36424                 cfg.autoCreate = cfg.autoCreate || true;
36425                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
36426                 //} else {
36427                 //    var el = this.el.createChild();
36428                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
36429                 //}
36430                 
36431                 this.add(region, ret);
36432                 break;
36433             
36434             /*
36435             case 'TreePanel': // our new panel!
36436                 cfg.el = this.el.createChild();
36437                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
36438                 this.add(region, ret);
36439                 break;
36440             */
36441             
36442             case 'Nest': 
36443                 // create a new Layout (which is  a Border Layout...
36444                 
36445                 var clayout = cfg.layout;
36446                 clayout.el  = this.el.createChild();
36447                 clayout.items   = clayout.items  || [];
36448                 
36449                 delete cfg.layout;
36450                 
36451                 // replace this exitems with the clayout ones..
36452                 xitems = clayout.items;
36453                  
36454                 // force background off if it's in center...
36455                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
36456                     cfg.background = false;
36457                 }
36458                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
36459                 
36460                 
36461                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
36462                 //console.log('adding nested layout panel '  + cfg.toSource());
36463                 this.add(region, ret);
36464                 nb = {}; /// find first...
36465                 break;
36466             
36467             case 'Grid':
36468                 
36469                 // needs grid and region
36470                 
36471                 //var el = this.getRegion(region).el.createChild();
36472                 /*
36473                  *var el = this.el.createChild();
36474                 // create the grid first...
36475                 cfg.grid.container = el;
36476                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
36477                 */
36478                 
36479                 if (region == 'center' && this.active ) {
36480                     cfg.background = false;
36481                 }
36482                 
36483                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
36484                 
36485                 this.add(region, ret);
36486                 /*
36487                 if (cfg.background) {
36488                     // render grid on panel activation (if panel background)
36489                     ret.on('activate', function(gp) {
36490                         if (!gp.grid.rendered) {
36491                     //        gp.grid.render(el);
36492                         }
36493                     });
36494                 } else {
36495                   //  cfg.grid.render(el);
36496                 }
36497                 */
36498                 break;
36499            
36500            
36501             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
36502                 // it was the old xcomponent building that caused this before.
36503                 // espeically if border is the top element in the tree.
36504                 ret = this;
36505                 break; 
36506                 
36507                     
36508                 
36509                 
36510                 
36511             default:
36512                 /*
36513                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
36514                     
36515                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
36516                     this.add(region, ret);
36517                 } else {
36518                 */
36519                     Roo.log(cfg);
36520                     throw "Can not add '" + cfg.xtype + "' to Border";
36521                     return null;
36522              
36523                                 
36524              
36525         }
36526         this.beginUpdate();
36527         // add children..
36528         var region = '';
36529         var abn = {};
36530         Roo.each(xitems, function(i)  {
36531             region = nb && i.region ? i.region : false;
36532             
36533             var add = ret.addxtype(i);
36534            
36535             if (region) {
36536                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
36537                 if (!i.background) {
36538                     abn[region] = nb[region] ;
36539                 }
36540             }
36541             
36542         });
36543         this.endUpdate();
36544
36545         // make the last non-background panel active..
36546         //if (nb) { Roo.log(abn); }
36547         if (nb) {
36548             
36549             for(var r in abn) {
36550                 region = this.getRegion(r);
36551                 if (region) {
36552                     // tried using nb[r], but it does not work..
36553                      
36554                     region.showPanel(abn[r]);
36555                    
36556                 }
36557             }
36558         }
36559         return ret;
36560         
36561     },
36562     
36563     
36564 // private
36565     factory : function(cfg)
36566     {
36567         
36568         var validRegions = Roo.bootstrap.layout.Border.regions;
36569
36570         var target = cfg.region;
36571         cfg.mgr = this;
36572         
36573         var r = Roo.bootstrap.layout;
36574         Roo.log(target);
36575         switch(target){
36576             case "north":
36577                 return new r.North(cfg);
36578             case "south":
36579                 return new r.South(cfg);
36580             case "east":
36581                 return new r.East(cfg);
36582             case "west":
36583                 return new r.West(cfg);
36584             case "center":
36585                 return new r.Center(cfg);
36586         }
36587         throw 'Layout region "'+target+'" not supported.';
36588     }
36589     
36590     
36591 });
36592  /*
36593  * Based on:
36594  * Ext JS Library 1.1.1
36595  * Copyright(c) 2006-2007, Ext JS, LLC.
36596  *
36597  * Originally Released Under LGPL - original licence link has changed is not relivant.
36598  *
36599  * Fork - LGPL
36600  * <script type="text/javascript">
36601  */
36602  
36603 /**
36604  * @class Roo.bootstrap.layout.Basic
36605  * @extends Roo.util.Observable
36606  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
36607  * and does not have a titlebar, tabs or any other features. All it does is size and position 
36608  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
36609  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
36610  * @cfg {string}   region  the region that it inhabits..
36611  * @cfg {bool}   skipConfig skip config?
36612  * 
36613
36614  */
36615 Roo.bootstrap.layout.Basic = function(config){
36616     
36617     this.mgr = config.mgr;
36618     
36619     this.position = config.region;
36620     
36621     var skipConfig = config.skipConfig;
36622     
36623     this.events = {
36624         /**
36625          * @scope Roo.BasicLayoutRegion
36626          */
36627         
36628         /**
36629          * @event beforeremove
36630          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
36631          * @param {Roo.LayoutRegion} this
36632          * @param {Roo.ContentPanel} panel The panel
36633          * @param {Object} e The cancel event object
36634          */
36635         "beforeremove" : true,
36636         /**
36637          * @event invalidated
36638          * Fires when the layout for this region is changed.
36639          * @param {Roo.LayoutRegion} this
36640          */
36641         "invalidated" : true,
36642         /**
36643          * @event visibilitychange
36644          * Fires when this region is shown or hidden 
36645          * @param {Roo.LayoutRegion} this
36646          * @param {Boolean} visibility true or false
36647          */
36648         "visibilitychange" : true,
36649         /**
36650          * @event paneladded
36651          * Fires when a panel is added. 
36652          * @param {Roo.LayoutRegion} this
36653          * @param {Roo.ContentPanel} panel The panel
36654          */
36655         "paneladded" : true,
36656         /**
36657          * @event panelremoved
36658          * Fires when a panel is removed. 
36659          * @param {Roo.LayoutRegion} this
36660          * @param {Roo.ContentPanel} panel The panel
36661          */
36662         "panelremoved" : true,
36663         /**
36664          * @event beforecollapse
36665          * Fires when this region before collapse.
36666          * @param {Roo.LayoutRegion} this
36667          */
36668         "beforecollapse" : true,
36669         /**
36670          * @event collapsed
36671          * Fires when this region is collapsed.
36672          * @param {Roo.LayoutRegion} this
36673          */
36674         "collapsed" : true,
36675         /**
36676          * @event expanded
36677          * Fires when this region is expanded.
36678          * @param {Roo.LayoutRegion} this
36679          */
36680         "expanded" : true,
36681         /**
36682          * @event slideshow
36683          * Fires when this region is slid into view.
36684          * @param {Roo.LayoutRegion} this
36685          */
36686         "slideshow" : true,
36687         /**
36688          * @event slidehide
36689          * Fires when this region slides out of view. 
36690          * @param {Roo.LayoutRegion} this
36691          */
36692         "slidehide" : true,
36693         /**
36694          * @event panelactivated
36695          * Fires when a panel is activated. 
36696          * @param {Roo.LayoutRegion} this
36697          * @param {Roo.ContentPanel} panel The activated panel
36698          */
36699         "panelactivated" : true,
36700         /**
36701          * @event resized
36702          * Fires when the user resizes this region. 
36703          * @param {Roo.LayoutRegion} this
36704          * @param {Number} newSize The new size (width for east/west, height for north/south)
36705          */
36706         "resized" : true
36707     };
36708     /** A collection of panels in this region. @type Roo.util.MixedCollection */
36709     this.panels = new Roo.util.MixedCollection();
36710     this.panels.getKey = this.getPanelId.createDelegate(this);
36711     this.box = null;
36712     this.activePanel = null;
36713     // ensure listeners are added...
36714     
36715     if (config.listeners || config.events) {
36716         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
36717             listeners : config.listeners || {},
36718             events : config.events || {}
36719         });
36720     }
36721     
36722     if(skipConfig !== true){
36723         this.applyConfig(config);
36724     }
36725 };
36726
36727 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
36728 {
36729     getPanelId : function(p){
36730         return p.getId();
36731     },
36732     
36733     applyConfig : function(config){
36734         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36735         this.config = config;
36736         
36737     },
36738     
36739     /**
36740      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
36741      * the width, for horizontal (north, south) the height.
36742      * @param {Number} newSize The new width or height
36743      */
36744     resizeTo : function(newSize){
36745         var el = this.el ? this.el :
36746                  (this.activePanel ? this.activePanel.getEl() : null);
36747         if(el){
36748             switch(this.position){
36749                 case "east":
36750                 case "west":
36751                     el.setWidth(newSize);
36752                     this.fireEvent("resized", this, newSize);
36753                 break;
36754                 case "north":
36755                 case "south":
36756                     el.setHeight(newSize);
36757                     this.fireEvent("resized", this, newSize);
36758                 break;                
36759             }
36760         }
36761     },
36762     
36763     getBox : function(){
36764         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
36765     },
36766     
36767     getMargins : function(){
36768         return this.margins;
36769     },
36770     
36771     updateBox : function(box){
36772         this.box = box;
36773         var el = this.activePanel.getEl();
36774         el.dom.style.left = box.x + "px";
36775         el.dom.style.top = box.y + "px";
36776         this.activePanel.setSize(box.width, box.height);
36777     },
36778     
36779     /**
36780      * Returns the container element for this region.
36781      * @return {Roo.Element}
36782      */
36783     getEl : function(){
36784         return this.activePanel;
36785     },
36786     
36787     /**
36788      * Returns true if this region is currently visible.
36789      * @return {Boolean}
36790      */
36791     isVisible : function(){
36792         return this.activePanel ? true : false;
36793     },
36794     
36795     setActivePanel : function(panel){
36796         panel = this.getPanel(panel);
36797         if(this.activePanel && this.activePanel != panel){
36798             this.activePanel.setActiveState(false);
36799             this.activePanel.getEl().setLeftTop(-10000,-10000);
36800         }
36801         this.activePanel = panel;
36802         panel.setActiveState(true);
36803         if(this.box){
36804             panel.setSize(this.box.width, this.box.height);
36805         }
36806         this.fireEvent("panelactivated", this, panel);
36807         this.fireEvent("invalidated");
36808     },
36809     
36810     /**
36811      * Show the specified panel.
36812      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
36813      * @return {Roo.ContentPanel} The shown panel or null
36814      */
36815     showPanel : function(panel){
36816         panel = this.getPanel(panel);
36817         if(panel){
36818             this.setActivePanel(panel);
36819         }
36820         return panel;
36821     },
36822     
36823     /**
36824      * Get the active panel for this region.
36825      * @return {Roo.ContentPanel} The active panel or null
36826      */
36827     getActivePanel : function(){
36828         return this.activePanel;
36829     },
36830     
36831     /**
36832      * Add the passed ContentPanel(s)
36833      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36834      * @return {Roo.ContentPanel} The panel added (if only one was added)
36835      */
36836     add : function(panel){
36837         if(arguments.length > 1){
36838             for(var i = 0, len = arguments.length; i < len; i++) {
36839                 this.add(arguments[i]);
36840             }
36841             return null;
36842         }
36843         if(this.hasPanel(panel)){
36844             this.showPanel(panel);
36845             return panel;
36846         }
36847         var el = panel.getEl();
36848         if(el.dom.parentNode != this.mgr.el.dom){
36849             this.mgr.el.dom.appendChild(el.dom);
36850         }
36851         if(panel.setRegion){
36852             panel.setRegion(this);
36853         }
36854         this.panels.add(panel);
36855         el.setStyle("position", "absolute");
36856         if(!panel.background){
36857             this.setActivePanel(panel);
36858             if(this.config.initialSize && this.panels.getCount()==1){
36859                 this.resizeTo(this.config.initialSize);
36860             }
36861         }
36862         this.fireEvent("paneladded", this, panel);
36863         return panel;
36864     },
36865     
36866     /**
36867      * Returns true if the panel is in this region.
36868      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36869      * @return {Boolean}
36870      */
36871     hasPanel : function(panel){
36872         if(typeof panel == "object"){ // must be panel obj
36873             panel = panel.getId();
36874         }
36875         return this.getPanel(panel) ? true : false;
36876     },
36877     
36878     /**
36879      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36880      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36881      * @param {Boolean} preservePanel Overrides the config preservePanel option
36882      * @return {Roo.ContentPanel} The panel that was removed
36883      */
36884     remove : function(panel, preservePanel){
36885         panel = this.getPanel(panel);
36886         if(!panel){
36887             return null;
36888         }
36889         var e = {};
36890         this.fireEvent("beforeremove", this, panel, e);
36891         if(e.cancel === true){
36892             return null;
36893         }
36894         var panelId = panel.getId();
36895         this.panels.removeKey(panelId);
36896         return panel;
36897     },
36898     
36899     /**
36900      * Returns the panel specified or null if it's not in this region.
36901      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36902      * @return {Roo.ContentPanel}
36903      */
36904     getPanel : function(id){
36905         if(typeof id == "object"){ // must be panel obj
36906             return id;
36907         }
36908         return this.panels.get(id);
36909     },
36910     
36911     /**
36912      * Returns this regions position (north/south/east/west/center).
36913      * @return {String} 
36914      */
36915     getPosition: function(){
36916         return this.position;    
36917     }
36918 });/*
36919  * Based on:
36920  * Ext JS Library 1.1.1
36921  * Copyright(c) 2006-2007, Ext JS, LLC.
36922  *
36923  * Originally Released Under LGPL - original licence link has changed is not relivant.
36924  *
36925  * Fork - LGPL
36926  * <script type="text/javascript">
36927  */
36928  
36929 /**
36930  * @class Roo.bootstrap.layout.Region
36931  * @extends Roo.bootstrap.layout.Basic
36932  * This class represents a region in a layout manager.
36933  
36934  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
36935  * @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})
36936  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
36937  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
36938  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
36939  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
36940  * @cfg {String}    title           The title for the region (overrides panel titles)
36941  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
36942  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
36943  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
36944  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
36945  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
36946  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
36947  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
36948  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
36949  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
36950  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
36951
36952  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
36953  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
36954  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
36955  * @cfg {Number}    width           For East/West panels
36956  * @cfg {Number}    height          For North/South panels
36957  * @cfg {Boolean}   split           To show the splitter
36958  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
36959  * 
36960  * @cfg {string}   cls             Extra CSS classes to add to region
36961  * 
36962  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
36963  * @cfg {string}   region  the region that it inhabits..
36964  *
36965
36966  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
36967  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
36968
36969  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
36970  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
36971  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
36972  */
36973 Roo.bootstrap.layout.Region = function(config)
36974 {
36975     this.applyConfig(config);
36976
36977     var mgr = config.mgr;
36978     var pos = config.region;
36979     config.skipConfig = true;
36980     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
36981     
36982     if (mgr.el) {
36983         this.onRender(mgr.el);   
36984     }
36985      
36986     this.visible = true;
36987     this.collapsed = false;
36988     this.unrendered_panels = [];
36989 };
36990
36991 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
36992
36993     position: '', // set by wrapper (eg. north/south etc..)
36994     unrendered_panels : null,  // unrendered panels.
36995     
36996     tabPosition : false,
36997     
36998     mgr: false, // points to 'Border'
36999     
37000     
37001     createBody : function(){
37002         /** This region's body element 
37003         * @type Roo.Element */
37004         this.bodyEl = this.el.createChild({
37005                 tag: "div",
37006                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
37007         });
37008     },
37009
37010     onRender: function(ctr, pos)
37011     {
37012         var dh = Roo.DomHelper;
37013         /** This region's container element 
37014         * @type Roo.Element */
37015         this.el = dh.append(ctr.dom, {
37016                 tag: "div",
37017                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
37018             }, true);
37019         /** This region's title element 
37020         * @type Roo.Element */
37021     
37022         this.titleEl = dh.append(this.el.dom,  {
37023                 tag: "div",
37024                 unselectable: "on",
37025                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
37026                 children:[
37027                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
37028                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
37029                 ]
37030             }, true);
37031         
37032         this.titleEl.enableDisplayMode();
37033         /** This region's title text element 
37034         * @type HTMLElement */
37035         this.titleTextEl = this.titleEl.dom.firstChild;
37036         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
37037         /*
37038         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
37039         this.closeBtn.enableDisplayMode();
37040         this.closeBtn.on("click", this.closeClicked, this);
37041         this.closeBtn.hide();
37042     */
37043         this.createBody(this.config);
37044         if(this.config.hideWhenEmpty){
37045             this.hide();
37046             this.on("paneladded", this.validateVisibility, this);
37047             this.on("panelremoved", this.validateVisibility, this);
37048         }
37049         if(this.autoScroll){
37050             this.bodyEl.setStyle("overflow", "auto");
37051         }else{
37052             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
37053         }
37054         //if(c.titlebar !== false){
37055             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
37056                 this.titleEl.hide();
37057             }else{
37058                 this.titleEl.show();
37059                 if(this.config.title){
37060                     this.titleTextEl.innerHTML = this.config.title;
37061                 }
37062             }
37063         //}
37064         if(this.config.collapsed){
37065             this.collapse(true);
37066         }
37067         if(this.config.hidden){
37068             this.hide();
37069         }
37070         
37071         if (this.unrendered_panels && this.unrendered_panels.length) {
37072             for (var i =0;i< this.unrendered_panels.length; i++) {
37073                 this.add(this.unrendered_panels[i]);
37074             }
37075             this.unrendered_panels = null;
37076             
37077         }
37078         
37079     },
37080     
37081     applyConfig : function(c)
37082     {
37083         /*
37084          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
37085             var dh = Roo.DomHelper;
37086             if(c.titlebar !== false){
37087                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
37088                 this.collapseBtn.on("click", this.collapse, this);
37089                 this.collapseBtn.enableDisplayMode();
37090                 /*
37091                 if(c.showPin === true || this.showPin){
37092                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
37093                     this.stickBtn.enableDisplayMode();
37094                     this.stickBtn.on("click", this.expand, this);
37095                     this.stickBtn.hide();
37096                 }
37097                 
37098             }
37099             */
37100             /** This region's collapsed element
37101             * @type Roo.Element */
37102             /*
37103              *
37104             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
37105                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
37106             ]}, true);
37107             
37108             if(c.floatable !== false){
37109                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
37110                this.collapsedEl.on("click", this.collapseClick, this);
37111             }
37112
37113             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
37114                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
37115                    id: "message", unselectable: "on", style:{"float":"left"}});
37116                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
37117              }
37118             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
37119             this.expandBtn.on("click", this.expand, this);
37120             
37121         }
37122         
37123         if(this.collapseBtn){
37124             this.collapseBtn.setVisible(c.collapsible == true);
37125         }
37126         
37127         this.cmargins = c.cmargins || this.cmargins ||
37128                          (this.position == "west" || this.position == "east" ?
37129                              {top: 0, left: 2, right:2, bottom: 0} :
37130                              {top: 2, left: 0, right:0, bottom: 2});
37131         */
37132         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37133         
37134         
37135         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
37136         
37137         this.autoScroll = c.autoScroll || false;
37138         
37139         
37140        
37141         
37142         this.duration = c.duration || .30;
37143         this.slideDuration = c.slideDuration || .45;
37144         this.config = c;
37145        
37146     },
37147     /**
37148      * Returns true if this region is currently visible.
37149      * @return {Boolean}
37150      */
37151     isVisible : function(){
37152         return this.visible;
37153     },
37154
37155     /**
37156      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
37157      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
37158      */
37159     //setCollapsedTitle : function(title){
37160     //    title = title || "&#160;";
37161      //   if(this.collapsedTitleTextEl){
37162       //      this.collapsedTitleTextEl.innerHTML = title;
37163        // }
37164     //},
37165
37166     getBox : function(){
37167         var b;
37168       //  if(!this.collapsed){
37169             b = this.el.getBox(false, true);
37170        // }else{
37171           //  b = this.collapsedEl.getBox(false, true);
37172         //}
37173         return b;
37174     },
37175
37176     getMargins : function(){
37177         return this.margins;
37178         //return this.collapsed ? this.cmargins : this.margins;
37179     },
37180 /*
37181     highlight : function(){
37182         this.el.addClass("x-layout-panel-dragover");
37183     },
37184
37185     unhighlight : function(){
37186         this.el.removeClass("x-layout-panel-dragover");
37187     },
37188 */
37189     updateBox : function(box)
37190     {
37191         if (!this.bodyEl) {
37192             return; // not rendered yet..
37193         }
37194         
37195         this.box = box;
37196         if(!this.collapsed){
37197             this.el.dom.style.left = box.x + "px";
37198             this.el.dom.style.top = box.y + "px";
37199             this.updateBody(box.width, box.height);
37200         }else{
37201             this.collapsedEl.dom.style.left = box.x + "px";
37202             this.collapsedEl.dom.style.top = box.y + "px";
37203             this.collapsedEl.setSize(box.width, box.height);
37204         }
37205         if(this.tabs){
37206             this.tabs.autoSizeTabs();
37207         }
37208     },
37209
37210     updateBody : function(w, h)
37211     {
37212         if(w !== null){
37213             this.el.setWidth(w);
37214             w -= this.el.getBorderWidth("rl");
37215             if(this.config.adjustments){
37216                 w += this.config.adjustments[0];
37217             }
37218         }
37219         if(h !== null && h > 0){
37220             this.el.setHeight(h);
37221             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
37222             h -= this.el.getBorderWidth("tb");
37223             if(this.config.adjustments){
37224                 h += this.config.adjustments[1];
37225             }
37226             this.bodyEl.setHeight(h);
37227             if(this.tabs){
37228                 h = this.tabs.syncHeight(h);
37229             }
37230         }
37231         if(this.panelSize){
37232             w = w !== null ? w : this.panelSize.width;
37233             h = h !== null ? h : this.panelSize.height;
37234         }
37235         if(this.activePanel){
37236             var el = this.activePanel.getEl();
37237             w = w !== null ? w : el.getWidth();
37238             h = h !== null ? h : el.getHeight();
37239             this.panelSize = {width: w, height: h};
37240             this.activePanel.setSize(w, h);
37241         }
37242         if(Roo.isIE && this.tabs){
37243             this.tabs.el.repaint();
37244         }
37245     },
37246
37247     /**
37248      * Returns the container element for this region.
37249      * @return {Roo.Element}
37250      */
37251     getEl : function(){
37252         return this.el;
37253     },
37254
37255     /**
37256      * Hides this region.
37257      */
37258     hide : function(){
37259         //if(!this.collapsed){
37260             this.el.dom.style.left = "-2000px";
37261             this.el.hide();
37262         //}else{
37263          //   this.collapsedEl.dom.style.left = "-2000px";
37264          //   this.collapsedEl.hide();
37265        // }
37266         this.visible = false;
37267         this.fireEvent("visibilitychange", this, false);
37268     },
37269
37270     /**
37271      * Shows this region if it was previously hidden.
37272      */
37273     show : function(){
37274         //if(!this.collapsed){
37275             this.el.show();
37276         //}else{
37277         //    this.collapsedEl.show();
37278        // }
37279         this.visible = true;
37280         this.fireEvent("visibilitychange", this, true);
37281     },
37282 /*
37283     closeClicked : function(){
37284         if(this.activePanel){
37285             this.remove(this.activePanel);
37286         }
37287     },
37288
37289     collapseClick : function(e){
37290         if(this.isSlid){
37291            e.stopPropagation();
37292            this.slideIn();
37293         }else{
37294            e.stopPropagation();
37295            this.slideOut();
37296         }
37297     },
37298 */
37299     /**
37300      * Collapses this region.
37301      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
37302      */
37303     /*
37304     collapse : function(skipAnim, skipCheck = false){
37305         if(this.collapsed) {
37306             return;
37307         }
37308         
37309         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
37310             
37311             this.collapsed = true;
37312             if(this.split){
37313                 this.split.el.hide();
37314             }
37315             if(this.config.animate && skipAnim !== true){
37316                 this.fireEvent("invalidated", this);
37317                 this.animateCollapse();
37318             }else{
37319                 this.el.setLocation(-20000,-20000);
37320                 this.el.hide();
37321                 this.collapsedEl.show();
37322                 this.fireEvent("collapsed", this);
37323                 this.fireEvent("invalidated", this);
37324             }
37325         }
37326         
37327     },
37328 */
37329     animateCollapse : function(){
37330         // overridden
37331     },
37332
37333     /**
37334      * Expands this region if it was previously collapsed.
37335      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
37336      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
37337      */
37338     /*
37339     expand : function(e, skipAnim){
37340         if(e) {
37341             e.stopPropagation();
37342         }
37343         if(!this.collapsed || this.el.hasActiveFx()) {
37344             return;
37345         }
37346         if(this.isSlid){
37347             this.afterSlideIn();
37348             skipAnim = true;
37349         }
37350         this.collapsed = false;
37351         if(this.config.animate && skipAnim !== true){
37352             this.animateExpand();
37353         }else{
37354             this.el.show();
37355             if(this.split){
37356                 this.split.el.show();
37357             }
37358             this.collapsedEl.setLocation(-2000,-2000);
37359             this.collapsedEl.hide();
37360             this.fireEvent("invalidated", this);
37361             this.fireEvent("expanded", this);
37362         }
37363     },
37364 */
37365     animateExpand : function(){
37366         // overridden
37367     },
37368
37369     initTabs : function()
37370     {
37371         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
37372         
37373         var ts = new Roo.bootstrap.panel.Tabs({
37374             el: this.bodyEl.dom,
37375             region : this,
37376             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
37377             disableTooltips: this.config.disableTabTips,
37378             toolbar : this.config.toolbar
37379         });
37380         
37381         if(this.config.hideTabs){
37382             ts.stripWrap.setDisplayed(false);
37383         }
37384         this.tabs = ts;
37385         ts.resizeTabs = this.config.resizeTabs === true;
37386         ts.minTabWidth = this.config.minTabWidth || 40;
37387         ts.maxTabWidth = this.config.maxTabWidth || 250;
37388         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
37389         ts.monitorResize = false;
37390         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
37391         ts.bodyEl.addClass('roo-layout-tabs-body');
37392         this.panels.each(this.initPanelAsTab, this);
37393     },
37394
37395     initPanelAsTab : function(panel){
37396         var ti = this.tabs.addTab(
37397             panel.getEl().id,
37398             panel.getTitle(),
37399             null,
37400             this.config.closeOnTab && panel.isClosable(),
37401             panel.tpl
37402         );
37403         if(panel.tabTip !== undefined){
37404             ti.setTooltip(panel.tabTip);
37405         }
37406         ti.on("activate", function(){
37407               this.setActivePanel(panel);
37408         }, this);
37409         
37410         if(this.config.closeOnTab){
37411             ti.on("beforeclose", function(t, e){
37412                 e.cancel = true;
37413                 this.remove(panel);
37414             }, this);
37415         }
37416         
37417         panel.tabItem = ti;
37418         
37419         return ti;
37420     },
37421
37422     updatePanelTitle : function(panel, title)
37423     {
37424         if(this.activePanel == panel){
37425             this.updateTitle(title);
37426         }
37427         if(this.tabs){
37428             var ti = this.tabs.getTab(panel.getEl().id);
37429             ti.setText(title);
37430             if(panel.tabTip !== undefined){
37431                 ti.setTooltip(panel.tabTip);
37432             }
37433         }
37434     },
37435
37436     updateTitle : function(title){
37437         if(this.titleTextEl && !this.config.title){
37438             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
37439         }
37440     },
37441
37442     setActivePanel : function(panel)
37443     {
37444         panel = this.getPanel(panel);
37445         if(this.activePanel && this.activePanel != panel){
37446             if(this.activePanel.setActiveState(false) === false){
37447                 return;
37448             }
37449         }
37450         this.activePanel = panel;
37451         panel.setActiveState(true);
37452         if(this.panelSize){
37453             panel.setSize(this.panelSize.width, this.panelSize.height);
37454         }
37455         if(this.closeBtn){
37456             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
37457         }
37458         this.updateTitle(panel.getTitle());
37459         if(this.tabs){
37460             this.fireEvent("invalidated", this);
37461         }
37462         this.fireEvent("panelactivated", this, panel);
37463     },
37464
37465     /**
37466      * Shows the specified panel.
37467      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
37468      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
37469      */
37470     showPanel : function(panel)
37471     {
37472         panel = this.getPanel(panel);
37473         if(panel){
37474             if(this.tabs){
37475                 var tab = this.tabs.getTab(panel.getEl().id);
37476                 if(tab.isHidden()){
37477                     this.tabs.unhideTab(tab.id);
37478                 }
37479                 tab.activate();
37480             }else{
37481                 this.setActivePanel(panel);
37482             }
37483         }
37484         return panel;
37485     },
37486
37487     /**
37488      * Get the active panel for this region.
37489      * @return {Roo.ContentPanel} The active panel or null
37490      */
37491     getActivePanel : function(){
37492         return this.activePanel;
37493     },
37494
37495     validateVisibility : function(){
37496         if(this.panels.getCount() < 1){
37497             this.updateTitle("&#160;");
37498             this.closeBtn.hide();
37499             this.hide();
37500         }else{
37501             if(!this.isVisible()){
37502                 this.show();
37503             }
37504         }
37505     },
37506
37507     /**
37508      * Adds the passed ContentPanel(s) to this region.
37509      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
37510      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
37511      */
37512     add : function(panel)
37513     {
37514         if(arguments.length > 1){
37515             for(var i = 0, len = arguments.length; i < len; i++) {
37516                 this.add(arguments[i]);
37517             }
37518             return null;
37519         }
37520         
37521         // if we have not been rendered yet, then we can not really do much of this..
37522         if (!this.bodyEl) {
37523             this.unrendered_panels.push(panel);
37524             return panel;
37525         }
37526         
37527         
37528         
37529         
37530         if(this.hasPanel(panel)){
37531             this.showPanel(panel);
37532             return panel;
37533         }
37534         panel.setRegion(this);
37535         this.panels.add(panel);
37536        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
37537             // sinle panel - no tab...?? would it not be better to render it with the tabs,
37538             // and hide them... ???
37539             this.bodyEl.dom.appendChild(panel.getEl().dom);
37540             if(panel.background !== true){
37541                 this.setActivePanel(panel);
37542             }
37543             this.fireEvent("paneladded", this, panel);
37544             return panel;
37545         }
37546         */
37547         if(!this.tabs){
37548             this.initTabs();
37549         }else{
37550             this.initPanelAsTab(panel);
37551         }
37552         
37553         
37554         if(panel.background !== true){
37555             this.tabs.activate(panel.getEl().id);
37556         }
37557         this.fireEvent("paneladded", this, panel);
37558         return panel;
37559     },
37560
37561     /**
37562      * Hides the tab for the specified panel.
37563      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
37564      */
37565     hidePanel : function(panel){
37566         if(this.tabs && (panel = this.getPanel(panel))){
37567             this.tabs.hideTab(panel.getEl().id);
37568         }
37569     },
37570
37571     /**
37572      * Unhides the tab for a previously hidden panel.
37573      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
37574      */
37575     unhidePanel : function(panel){
37576         if(this.tabs && (panel = this.getPanel(panel))){
37577             this.tabs.unhideTab(panel.getEl().id);
37578         }
37579     },
37580
37581     clearPanels : function(){
37582         while(this.panels.getCount() > 0){
37583              this.remove(this.panels.first());
37584         }
37585     },
37586
37587     /**
37588      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
37589      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
37590      * @param {Boolean} preservePanel Overrides the config preservePanel option
37591      * @return {Roo.ContentPanel} The panel that was removed
37592      */
37593     remove : function(panel, preservePanel)
37594     {
37595         panel = this.getPanel(panel);
37596         if(!panel){
37597             return null;
37598         }
37599         var e = {};
37600         this.fireEvent("beforeremove", this, panel, e);
37601         if(e.cancel === true){
37602             return null;
37603         }
37604         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
37605         var panelId = panel.getId();
37606         this.panels.removeKey(panelId);
37607         if(preservePanel){
37608             document.body.appendChild(panel.getEl().dom);
37609         }
37610         if(this.tabs){
37611             this.tabs.removeTab(panel.getEl().id);
37612         }else if (!preservePanel){
37613             this.bodyEl.dom.removeChild(panel.getEl().dom);
37614         }
37615         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
37616             var p = this.panels.first();
37617             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
37618             tempEl.appendChild(p.getEl().dom);
37619             this.bodyEl.update("");
37620             this.bodyEl.dom.appendChild(p.getEl().dom);
37621             tempEl = null;
37622             this.updateTitle(p.getTitle());
37623             this.tabs = null;
37624             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
37625             this.setActivePanel(p);
37626         }
37627         panel.setRegion(null);
37628         if(this.activePanel == panel){
37629             this.activePanel = null;
37630         }
37631         if(this.config.autoDestroy !== false && preservePanel !== true){
37632             try{panel.destroy();}catch(e){}
37633         }
37634         this.fireEvent("panelremoved", this, panel);
37635         return panel;
37636     },
37637
37638     /**
37639      * Returns the TabPanel component used by this region
37640      * @return {Roo.TabPanel}
37641      */
37642     getTabs : function(){
37643         return this.tabs;
37644     },
37645
37646     createTool : function(parentEl, className){
37647         var btn = Roo.DomHelper.append(parentEl, {
37648             tag: "div",
37649             cls: "x-layout-tools-button",
37650             children: [ {
37651                 tag: "div",
37652                 cls: "roo-layout-tools-button-inner " + className,
37653                 html: "&#160;"
37654             }]
37655         }, true);
37656         btn.addClassOnOver("roo-layout-tools-button-over");
37657         return btn;
37658     }
37659 });/*
37660  * Based on:
37661  * Ext JS Library 1.1.1
37662  * Copyright(c) 2006-2007, Ext JS, LLC.
37663  *
37664  * Originally Released Under LGPL - original licence link has changed is not relivant.
37665  *
37666  * Fork - LGPL
37667  * <script type="text/javascript">
37668  */
37669  
37670
37671
37672 /**
37673  * @class Roo.SplitLayoutRegion
37674  * @extends Roo.LayoutRegion
37675  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
37676  */
37677 Roo.bootstrap.layout.Split = function(config){
37678     this.cursor = config.cursor;
37679     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
37680 };
37681
37682 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
37683 {
37684     splitTip : "Drag to resize.",
37685     collapsibleSplitTip : "Drag to resize. Double click to hide.",
37686     useSplitTips : false,
37687
37688     applyConfig : function(config){
37689         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
37690     },
37691     
37692     onRender : function(ctr,pos) {
37693         
37694         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
37695         if(!this.config.split){
37696             return;
37697         }
37698         if(!this.split){
37699             
37700             var splitEl = Roo.DomHelper.append(ctr.dom,  {
37701                             tag: "div",
37702                             id: this.el.id + "-split",
37703                             cls: "roo-layout-split roo-layout-split-"+this.position,
37704                             html: "&#160;"
37705             });
37706             /** The SplitBar for this region 
37707             * @type Roo.SplitBar */
37708             // does not exist yet...
37709             Roo.log([this.position, this.orientation]);
37710             
37711             this.split = new Roo.bootstrap.SplitBar({
37712                 dragElement : splitEl,
37713                 resizingElement: this.el,
37714                 orientation : this.orientation
37715             });
37716             
37717             this.split.on("moved", this.onSplitMove, this);
37718             this.split.useShim = this.config.useShim === true;
37719             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
37720             if(this.useSplitTips){
37721                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
37722             }
37723             //if(config.collapsible){
37724             //    this.split.el.on("dblclick", this.collapse,  this);
37725             //}
37726         }
37727         if(typeof this.config.minSize != "undefined"){
37728             this.split.minSize = this.config.minSize;
37729         }
37730         if(typeof this.config.maxSize != "undefined"){
37731             this.split.maxSize = this.config.maxSize;
37732         }
37733         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
37734             this.hideSplitter();
37735         }
37736         
37737     },
37738
37739     getHMaxSize : function(){
37740          var cmax = this.config.maxSize || 10000;
37741          var center = this.mgr.getRegion("center");
37742          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
37743     },
37744
37745     getVMaxSize : function(){
37746          var cmax = this.config.maxSize || 10000;
37747          var center = this.mgr.getRegion("center");
37748          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
37749     },
37750
37751     onSplitMove : function(split, newSize){
37752         this.fireEvent("resized", this, newSize);
37753     },
37754     
37755     /** 
37756      * Returns the {@link Roo.SplitBar} for this region.
37757      * @return {Roo.SplitBar}
37758      */
37759     getSplitBar : function(){
37760         return this.split;
37761     },
37762     
37763     hide : function(){
37764         this.hideSplitter();
37765         Roo.bootstrap.layout.Split.superclass.hide.call(this);
37766     },
37767
37768     hideSplitter : function(){
37769         if(this.split){
37770             this.split.el.setLocation(-2000,-2000);
37771             this.split.el.hide();
37772         }
37773     },
37774
37775     show : function(){
37776         if(this.split){
37777             this.split.el.show();
37778         }
37779         Roo.bootstrap.layout.Split.superclass.show.call(this);
37780     },
37781     
37782     beforeSlide: function(){
37783         if(Roo.isGecko){// firefox overflow auto bug workaround
37784             this.bodyEl.clip();
37785             if(this.tabs) {
37786                 this.tabs.bodyEl.clip();
37787             }
37788             if(this.activePanel){
37789                 this.activePanel.getEl().clip();
37790                 
37791                 if(this.activePanel.beforeSlide){
37792                     this.activePanel.beforeSlide();
37793                 }
37794             }
37795         }
37796     },
37797     
37798     afterSlide : function(){
37799         if(Roo.isGecko){// firefox overflow auto bug workaround
37800             this.bodyEl.unclip();
37801             if(this.tabs) {
37802                 this.tabs.bodyEl.unclip();
37803             }
37804             if(this.activePanel){
37805                 this.activePanel.getEl().unclip();
37806                 if(this.activePanel.afterSlide){
37807                     this.activePanel.afterSlide();
37808                 }
37809             }
37810         }
37811     },
37812
37813     initAutoHide : function(){
37814         if(this.autoHide !== false){
37815             if(!this.autoHideHd){
37816                 var st = new Roo.util.DelayedTask(this.slideIn, this);
37817                 this.autoHideHd = {
37818                     "mouseout": function(e){
37819                         if(!e.within(this.el, true)){
37820                             st.delay(500);
37821                         }
37822                     },
37823                     "mouseover" : function(e){
37824                         st.cancel();
37825                     },
37826                     scope : this
37827                 };
37828             }
37829             this.el.on(this.autoHideHd);
37830         }
37831     },
37832
37833     clearAutoHide : function(){
37834         if(this.autoHide !== false){
37835             this.el.un("mouseout", this.autoHideHd.mouseout);
37836             this.el.un("mouseover", this.autoHideHd.mouseover);
37837         }
37838     },
37839
37840     clearMonitor : function(){
37841         Roo.get(document).un("click", this.slideInIf, this);
37842     },
37843
37844     // these names are backwards but not changed for compat
37845     slideOut : function(){
37846         if(this.isSlid || this.el.hasActiveFx()){
37847             return;
37848         }
37849         this.isSlid = true;
37850         if(this.collapseBtn){
37851             this.collapseBtn.hide();
37852         }
37853         this.closeBtnState = this.closeBtn.getStyle('display');
37854         this.closeBtn.hide();
37855         if(this.stickBtn){
37856             this.stickBtn.show();
37857         }
37858         this.el.show();
37859         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
37860         this.beforeSlide();
37861         this.el.setStyle("z-index", 10001);
37862         this.el.slideIn(this.getSlideAnchor(), {
37863             callback: function(){
37864                 this.afterSlide();
37865                 this.initAutoHide();
37866                 Roo.get(document).on("click", this.slideInIf, this);
37867                 this.fireEvent("slideshow", this);
37868             },
37869             scope: this,
37870             block: true
37871         });
37872     },
37873
37874     afterSlideIn : function(){
37875         this.clearAutoHide();
37876         this.isSlid = false;
37877         this.clearMonitor();
37878         this.el.setStyle("z-index", "");
37879         if(this.collapseBtn){
37880             this.collapseBtn.show();
37881         }
37882         this.closeBtn.setStyle('display', this.closeBtnState);
37883         if(this.stickBtn){
37884             this.stickBtn.hide();
37885         }
37886         this.fireEvent("slidehide", this);
37887     },
37888
37889     slideIn : function(cb){
37890         if(!this.isSlid || this.el.hasActiveFx()){
37891             Roo.callback(cb);
37892             return;
37893         }
37894         this.isSlid = false;
37895         this.beforeSlide();
37896         this.el.slideOut(this.getSlideAnchor(), {
37897             callback: function(){
37898                 this.el.setLeftTop(-10000, -10000);
37899                 this.afterSlide();
37900                 this.afterSlideIn();
37901                 Roo.callback(cb);
37902             },
37903             scope: this,
37904             block: true
37905         });
37906     },
37907     
37908     slideInIf : function(e){
37909         if(!e.within(this.el)){
37910             this.slideIn();
37911         }
37912     },
37913
37914     animateCollapse : function(){
37915         this.beforeSlide();
37916         this.el.setStyle("z-index", 20000);
37917         var anchor = this.getSlideAnchor();
37918         this.el.slideOut(anchor, {
37919             callback : function(){
37920                 this.el.setStyle("z-index", "");
37921                 this.collapsedEl.slideIn(anchor, {duration:.3});
37922                 this.afterSlide();
37923                 this.el.setLocation(-10000,-10000);
37924                 this.el.hide();
37925                 this.fireEvent("collapsed", this);
37926             },
37927             scope: this,
37928             block: true
37929         });
37930     },
37931
37932     animateExpand : function(){
37933         this.beforeSlide();
37934         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
37935         this.el.setStyle("z-index", 20000);
37936         this.collapsedEl.hide({
37937             duration:.1
37938         });
37939         this.el.slideIn(this.getSlideAnchor(), {
37940             callback : function(){
37941                 this.el.setStyle("z-index", "");
37942                 this.afterSlide();
37943                 if(this.split){
37944                     this.split.el.show();
37945                 }
37946                 this.fireEvent("invalidated", this);
37947                 this.fireEvent("expanded", this);
37948             },
37949             scope: this,
37950             block: true
37951         });
37952     },
37953
37954     anchors : {
37955         "west" : "left",
37956         "east" : "right",
37957         "north" : "top",
37958         "south" : "bottom"
37959     },
37960
37961     sanchors : {
37962         "west" : "l",
37963         "east" : "r",
37964         "north" : "t",
37965         "south" : "b"
37966     },
37967
37968     canchors : {
37969         "west" : "tl-tr",
37970         "east" : "tr-tl",
37971         "north" : "tl-bl",
37972         "south" : "bl-tl"
37973     },
37974
37975     getAnchor : function(){
37976         return this.anchors[this.position];
37977     },
37978
37979     getCollapseAnchor : function(){
37980         return this.canchors[this.position];
37981     },
37982
37983     getSlideAnchor : function(){
37984         return this.sanchors[this.position];
37985     },
37986
37987     getAlignAdj : function(){
37988         var cm = this.cmargins;
37989         switch(this.position){
37990             case "west":
37991                 return [0, 0];
37992             break;
37993             case "east":
37994                 return [0, 0];
37995             break;
37996             case "north":
37997                 return [0, 0];
37998             break;
37999             case "south":
38000                 return [0, 0];
38001             break;
38002         }
38003     },
38004
38005     getExpandAdj : function(){
38006         var c = this.collapsedEl, cm = this.cmargins;
38007         switch(this.position){
38008             case "west":
38009                 return [-(cm.right+c.getWidth()+cm.left), 0];
38010             break;
38011             case "east":
38012                 return [cm.right+c.getWidth()+cm.left, 0];
38013             break;
38014             case "north":
38015                 return [0, -(cm.top+cm.bottom+c.getHeight())];
38016             break;
38017             case "south":
38018                 return [0, cm.top+cm.bottom+c.getHeight()];
38019             break;
38020         }
38021     }
38022 });/*
38023  * Based on:
38024  * Ext JS Library 1.1.1
38025  * Copyright(c) 2006-2007, Ext JS, LLC.
38026  *
38027  * Originally Released Under LGPL - original licence link has changed is not relivant.
38028  *
38029  * Fork - LGPL
38030  * <script type="text/javascript">
38031  */
38032 /*
38033  * These classes are private internal classes
38034  */
38035 Roo.bootstrap.layout.Center = function(config){
38036     config.region = "center";
38037     Roo.bootstrap.layout.Region.call(this, config);
38038     this.visible = true;
38039     this.minWidth = config.minWidth || 20;
38040     this.minHeight = config.minHeight || 20;
38041 };
38042
38043 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
38044     hide : function(){
38045         // center panel can't be hidden
38046     },
38047     
38048     show : function(){
38049         // center panel can't be hidden
38050     },
38051     
38052     getMinWidth: function(){
38053         return this.minWidth;
38054     },
38055     
38056     getMinHeight: function(){
38057         return this.minHeight;
38058     }
38059 });
38060
38061
38062
38063
38064  
38065
38066
38067
38068
38069
38070
38071 Roo.bootstrap.layout.North = function(config)
38072 {
38073     config.region = 'north';
38074     config.cursor = 'n-resize';
38075     
38076     Roo.bootstrap.layout.Split.call(this, config);
38077     
38078     
38079     if(this.split){
38080         this.split.placement = Roo.bootstrap.SplitBar.TOP;
38081         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
38082         this.split.el.addClass("roo-layout-split-v");
38083     }
38084     var size = config.initialSize || config.height;
38085     if(typeof size != "undefined"){
38086         this.el.setHeight(size);
38087     }
38088 };
38089 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
38090 {
38091     orientation: Roo.bootstrap.SplitBar.VERTICAL,
38092     
38093     
38094     
38095     getBox : function(){
38096         if(this.collapsed){
38097             return this.collapsedEl.getBox();
38098         }
38099         var box = this.el.getBox();
38100         if(this.split){
38101             box.height += this.split.el.getHeight();
38102         }
38103         return box;
38104     },
38105     
38106     updateBox : function(box){
38107         if(this.split && !this.collapsed){
38108             box.height -= this.split.el.getHeight();
38109             this.split.el.setLeft(box.x);
38110             this.split.el.setTop(box.y+box.height);
38111             this.split.el.setWidth(box.width);
38112         }
38113         if(this.collapsed){
38114             this.updateBody(box.width, null);
38115         }
38116         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38117     }
38118 });
38119
38120
38121
38122
38123
38124 Roo.bootstrap.layout.South = function(config){
38125     config.region = 'south';
38126     config.cursor = 's-resize';
38127     Roo.bootstrap.layout.Split.call(this, config);
38128     if(this.split){
38129         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
38130         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
38131         this.split.el.addClass("roo-layout-split-v");
38132     }
38133     var size = config.initialSize || config.height;
38134     if(typeof size != "undefined"){
38135         this.el.setHeight(size);
38136     }
38137 };
38138
38139 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
38140     orientation: Roo.bootstrap.SplitBar.VERTICAL,
38141     getBox : function(){
38142         if(this.collapsed){
38143             return this.collapsedEl.getBox();
38144         }
38145         var box = this.el.getBox();
38146         if(this.split){
38147             var sh = this.split.el.getHeight();
38148             box.height += sh;
38149             box.y -= sh;
38150         }
38151         return box;
38152     },
38153     
38154     updateBox : function(box){
38155         if(this.split && !this.collapsed){
38156             var sh = this.split.el.getHeight();
38157             box.height -= sh;
38158             box.y += sh;
38159             this.split.el.setLeft(box.x);
38160             this.split.el.setTop(box.y-sh);
38161             this.split.el.setWidth(box.width);
38162         }
38163         if(this.collapsed){
38164             this.updateBody(box.width, null);
38165         }
38166         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38167     }
38168 });
38169
38170 Roo.bootstrap.layout.East = function(config){
38171     config.region = "east";
38172     config.cursor = "e-resize";
38173     Roo.bootstrap.layout.Split.call(this, config);
38174     if(this.split){
38175         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
38176         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
38177         this.split.el.addClass("roo-layout-split-h");
38178     }
38179     var size = config.initialSize || config.width;
38180     if(typeof size != "undefined"){
38181         this.el.setWidth(size);
38182     }
38183 };
38184 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
38185     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
38186     getBox : function(){
38187         if(this.collapsed){
38188             return this.collapsedEl.getBox();
38189         }
38190         var box = this.el.getBox();
38191         if(this.split){
38192             var sw = this.split.el.getWidth();
38193             box.width += sw;
38194             box.x -= sw;
38195         }
38196         return box;
38197     },
38198
38199     updateBox : function(box){
38200         if(this.split && !this.collapsed){
38201             var sw = this.split.el.getWidth();
38202             box.width -= sw;
38203             this.split.el.setLeft(box.x);
38204             this.split.el.setTop(box.y);
38205             this.split.el.setHeight(box.height);
38206             box.x += sw;
38207         }
38208         if(this.collapsed){
38209             this.updateBody(null, box.height);
38210         }
38211         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38212     }
38213 });
38214
38215 Roo.bootstrap.layout.West = function(config){
38216     config.region = "west";
38217     config.cursor = "w-resize";
38218     
38219     Roo.bootstrap.layout.Split.call(this, config);
38220     if(this.split){
38221         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
38222         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
38223         this.split.el.addClass("roo-layout-split-h");
38224     }
38225     
38226 };
38227 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
38228     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
38229     
38230     onRender: function(ctr, pos)
38231     {
38232         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
38233         var size = this.config.initialSize || this.config.width;
38234         if(typeof size != "undefined"){
38235             this.el.setWidth(size);
38236         }
38237     },
38238     
38239     getBox : function(){
38240         if(this.collapsed){
38241             return this.collapsedEl.getBox();
38242         }
38243         var box = this.el.getBox();
38244         if(this.split){
38245             box.width += this.split.el.getWidth();
38246         }
38247         return box;
38248     },
38249     
38250     updateBox : function(box){
38251         if(this.split && !this.collapsed){
38252             var sw = this.split.el.getWidth();
38253             box.width -= sw;
38254             this.split.el.setLeft(box.x+box.width);
38255             this.split.el.setTop(box.y);
38256             this.split.el.setHeight(box.height);
38257         }
38258         if(this.collapsed){
38259             this.updateBody(null, box.height);
38260         }
38261         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38262     }
38263 });Roo.namespace("Roo.bootstrap.panel");/*
38264  * Based on:
38265  * Ext JS Library 1.1.1
38266  * Copyright(c) 2006-2007, Ext JS, LLC.
38267  *
38268  * Originally Released Under LGPL - original licence link has changed is not relivant.
38269  *
38270  * Fork - LGPL
38271  * <script type="text/javascript">
38272  */
38273 /**
38274  * @class Roo.ContentPanel
38275  * @extends Roo.util.Observable
38276  * A basic ContentPanel element.
38277  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
38278  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
38279  * @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
38280  * @cfg {Boolean}   closable      True if the panel can be closed/removed
38281  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
38282  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
38283  * @cfg {Toolbar}   toolbar       A toolbar for this panel
38284  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
38285  * @cfg {String} title          The title for this panel
38286  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
38287  * @cfg {String} url            Calls {@link #setUrl} with this value
38288  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
38289  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
38290  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
38291  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
38292  * @cfg {Boolean} badges render the badges
38293
38294  * @constructor
38295  * Create a new ContentPanel.
38296  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
38297  * @param {String/Object} config A string to set only the title or a config object
38298  * @param {String} content (optional) Set the HTML content for this panel
38299  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
38300  */
38301 Roo.bootstrap.panel.Content = function( config){
38302     
38303     this.tpl = config.tpl || false;
38304     
38305     var el = config.el;
38306     var content = config.content;
38307
38308     if(config.autoCreate){ // xtype is available if this is called from factory
38309         el = Roo.id();
38310     }
38311     this.el = Roo.get(el);
38312     if(!this.el && config && config.autoCreate){
38313         if(typeof config.autoCreate == "object"){
38314             if(!config.autoCreate.id){
38315                 config.autoCreate.id = config.id||el;
38316             }
38317             this.el = Roo.DomHelper.append(document.body,
38318                         config.autoCreate, true);
38319         }else{
38320             var elcfg =  {   tag: "div",
38321                             cls: "roo-layout-inactive-content",
38322                             id: config.id||el
38323                             };
38324             if (config.html) {
38325                 elcfg.html = config.html;
38326                 
38327             }
38328                         
38329             this.el = Roo.DomHelper.append(document.body, elcfg , true);
38330         }
38331     } 
38332     this.closable = false;
38333     this.loaded = false;
38334     this.active = false;
38335    
38336       
38337     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
38338         
38339         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
38340         
38341         this.wrapEl = this.el; //this.el.wrap();
38342         var ti = [];
38343         if (config.toolbar.items) {
38344             ti = config.toolbar.items ;
38345             delete config.toolbar.items ;
38346         }
38347         
38348         var nitems = [];
38349         this.toolbar.render(this.wrapEl, 'before');
38350         for(var i =0;i < ti.length;i++) {
38351           //  Roo.log(['add child', items[i]]);
38352             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
38353         }
38354         this.toolbar.items = nitems;
38355         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
38356         delete config.toolbar;
38357         
38358     }
38359     /*
38360     // xtype created footer. - not sure if will work as we normally have to render first..
38361     if (this.footer && !this.footer.el && this.footer.xtype) {
38362         if (!this.wrapEl) {
38363             this.wrapEl = this.el.wrap();
38364         }
38365     
38366         this.footer.container = this.wrapEl.createChild();
38367          
38368         this.footer = Roo.factory(this.footer, Roo);
38369         
38370     }
38371     */
38372     
38373      if(typeof config == "string"){
38374         this.title = config;
38375     }else{
38376         Roo.apply(this, config);
38377     }
38378     
38379     if(this.resizeEl){
38380         this.resizeEl = Roo.get(this.resizeEl, true);
38381     }else{
38382         this.resizeEl = this.el;
38383     }
38384     // handle view.xtype
38385     
38386  
38387     
38388     
38389     this.addEvents({
38390         /**
38391          * @event activate
38392          * Fires when this panel is activated. 
38393          * @param {Roo.ContentPanel} this
38394          */
38395         "activate" : true,
38396         /**
38397          * @event deactivate
38398          * Fires when this panel is activated. 
38399          * @param {Roo.ContentPanel} this
38400          */
38401         "deactivate" : true,
38402
38403         /**
38404          * @event resize
38405          * Fires when this panel is resized if fitToFrame is true.
38406          * @param {Roo.ContentPanel} this
38407          * @param {Number} width The width after any component adjustments
38408          * @param {Number} height The height after any component adjustments
38409          */
38410         "resize" : true,
38411         
38412          /**
38413          * @event render
38414          * Fires when this tab is created
38415          * @param {Roo.ContentPanel} this
38416          */
38417         "render" : true
38418         
38419         
38420         
38421     });
38422     
38423
38424     
38425     
38426     if(this.autoScroll){
38427         this.resizeEl.setStyle("overflow", "auto");
38428     } else {
38429         // fix randome scrolling
38430         //this.el.on('scroll', function() {
38431         //    Roo.log('fix random scolling');
38432         //    this.scrollTo('top',0); 
38433         //});
38434     }
38435     content = content || this.content;
38436     if(content){
38437         this.setContent(content);
38438     }
38439     if(config && config.url){
38440         this.setUrl(this.url, this.params, this.loadOnce);
38441     }
38442     
38443     
38444     
38445     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
38446     
38447     if (this.view && typeof(this.view.xtype) != 'undefined') {
38448         this.view.el = this.el.appendChild(document.createElement("div"));
38449         this.view = Roo.factory(this.view); 
38450         this.view.render  &&  this.view.render(false, '');  
38451     }
38452     
38453     
38454     this.fireEvent('render', this);
38455 };
38456
38457 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
38458     
38459     tabTip : '',
38460     
38461     setRegion : function(region){
38462         this.region = region;
38463         this.setActiveClass(region && !this.background);
38464     },
38465     
38466     
38467     setActiveClass: function(state)
38468     {
38469         if(state){
38470            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
38471            this.el.setStyle('position','relative');
38472         }else{
38473            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
38474            this.el.setStyle('position', 'absolute');
38475         } 
38476     },
38477     
38478     /**
38479      * Returns the toolbar for this Panel if one was configured. 
38480      * @return {Roo.Toolbar} 
38481      */
38482     getToolbar : function(){
38483         return this.toolbar;
38484     },
38485     
38486     setActiveState : function(active)
38487     {
38488         this.active = active;
38489         this.setActiveClass(active);
38490         if(!active){
38491             if(this.fireEvent("deactivate", this) === false){
38492                 return false;
38493             }
38494             return true;
38495         }
38496         this.fireEvent("activate", this);
38497         return true;
38498     },
38499     /**
38500      * Updates this panel's element
38501      * @param {String} content The new content
38502      * @param {Boolean} loadScripts (optional) true to look for and process scripts
38503     */
38504     setContent : function(content, loadScripts){
38505         this.el.update(content, loadScripts);
38506     },
38507
38508     ignoreResize : function(w, h){
38509         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
38510             return true;
38511         }else{
38512             this.lastSize = {width: w, height: h};
38513             return false;
38514         }
38515     },
38516     /**
38517      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
38518      * @return {Roo.UpdateManager} The UpdateManager
38519      */
38520     getUpdateManager : function(){
38521         return this.el.getUpdateManager();
38522     },
38523      /**
38524      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
38525      * @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:
38526 <pre><code>
38527 panel.load({
38528     url: "your-url.php",
38529     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
38530     callback: yourFunction,
38531     scope: yourObject, //(optional scope)
38532     discardUrl: false,
38533     nocache: false,
38534     text: "Loading...",
38535     timeout: 30,
38536     scripts: false
38537 });
38538 </code></pre>
38539      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
38540      * 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.
38541      * @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}
38542      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
38543      * @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.
38544      * @return {Roo.ContentPanel} this
38545      */
38546     load : function(){
38547         var um = this.el.getUpdateManager();
38548         um.update.apply(um, arguments);
38549         return this;
38550     },
38551
38552
38553     /**
38554      * 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.
38555      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
38556      * @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)
38557      * @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)
38558      * @return {Roo.UpdateManager} The UpdateManager
38559      */
38560     setUrl : function(url, params, loadOnce){
38561         if(this.refreshDelegate){
38562             this.removeListener("activate", this.refreshDelegate);
38563         }
38564         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38565         this.on("activate", this.refreshDelegate);
38566         return this.el.getUpdateManager();
38567     },
38568     
38569     _handleRefresh : function(url, params, loadOnce){
38570         if(!loadOnce || !this.loaded){
38571             var updater = this.el.getUpdateManager();
38572             updater.update(url, params, this._setLoaded.createDelegate(this));
38573         }
38574     },
38575     
38576     _setLoaded : function(){
38577         this.loaded = true;
38578     }, 
38579     
38580     /**
38581      * Returns this panel's id
38582      * @return {String} 
38583      */
38584     getId : function(){
38585         return this.el.id;
38586     },
38587     
38588     /** 
38589      * Returns this panel's element - used by regiosn to add.
38590      * @return {Roo.Element} 
38591      */
38592     getEl : function(){
38593         return this.wrapEl || this.el;
38594     },
38595     
38596    
38597     
38598     adjustForComponents : function(width, height)
38599     {
38600         //Roo.log('adjustForComponents ');
38601         if(this.resizeEl != this.el){
38602             width -= this.el.getFrameWidth('lr');
38603             height -= this.el.getFrameWidth('tb');
38604         }
38605         if(this.toolbar){
38606             var te = this.toolbar.getEl();
38607             te.setWidth(width);
38608             height -= te.getHeight();
38609         }
38610         if(this.footer){
38611             var te = this.footer.getEl();
38612             te.setWidth(width);
38613             height -= te.getHeight();
38614         }
38615         
38616         
38617         if(this.adjustments){
38618             width += this.adjustments[0];
38619             height += this.adjustments[1];
38620         }
38621         return {"width": width, "height": height};
38622     },
38623     
38624     setSize : function(width, height){
38625         if(this.fitToFrame && !this.ignoreResize(width, height)){
38626             if(this.fitContainer && this.resizeEl != this.el){
38627                 this.el.setSize(width, height);
38628             }
38629             var size = this.adjustForComponents(width, height);
38630             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
38631             this.fireEvent('resize', this, size.width, size.height);
38632         }
38633     },
38634     
38635     /**
38636      * Returns this panel's title
38637      * @return {String} 
38638      */
38639     getTitle : function(){
38640         
38641         if (typeof(this.title) != 'object') {
38642             return this.title;
38643         }
38644         
38645         var t = '';
38646         for (var k in this.title) {
38647             if (!this.title.hasOwnProperty(k)) {
38648                 continue;
38649             }
38650             
38651             if (k.indexOf('-') >= 0) {
38652                 var s = k.split('-');
38653                 for (var i = 0; i<s.length; i++) {
38654                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
38655                 }
38656             } else {
38657                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
38658             }
38659         }
38660         return t;
38661     },
38662     
38663     /**
38664      * Set this panel's title
38665      * @param {String} title
38666      */
38667     setTitle : function(title){
38668         this.title = title;
38669         if(this.region){
38670             this.region.updatePanelTitle(this, title);
38671         }
38672     },
38673     
38674     /**
38675      * Returns true is this panel was configured to be closable
38676      * @return {Boolean} 
38677      */
38678     isClosable : function(){
38679         return this.closable;
38680     },
38681     
38682     beforeSlide : function(){
38683         this.el.clip();
38684         this.resizeEl.clip();
38685     },
38686     
38687     afterSlide : function(){
38688         this.el.unclip();
38689         this.resizeEl.unclip();
38690     },
38691     
38692     /**
38693      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
38694      *   Will fail silently if the {@link #setUrl} method has not been called.
38695      *   This does not activate the panel, just updates its content.
38696      */
38697     refresh : function(){
38698         if(this.refreshDelegate){
38699            this.loaded = false;
38700            this.refreshDelegate();
38701         }
38702     },
38703     
38704     /**
38705      * Destroys this panel
38706      */
38707     destroy : function(){
38708         this.el.removeAllListeners();
38709         var tempEl = document.createElement("span");
38710         tempEl.appendChild(this.el.dom);
38711         tempEl.innerHTML = "";
38712         this.el.remove();
38713         this.el = null;
38714     },
38715     
38716     /**
38717      * form - if the content panel contains a form - this is a reference to it.
38718      * @type {Roo.form.Form}
38719      */
38720     form : false,
38721     /**
38722      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
38723      *    This contains a reference to it.
38724      * @type {Roo.View}
38725      */
38726     view : false,
38727     
38728       /**
38729      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
38730      * <pre><code>
38731
38732 layout.addxtype({
38733        xtype : 'Form',
38734        items: [ .... ]
38735    }
38736 );
38737
38738 </code></pre>
38739      * @param {Object} cfg Xtype definition of item to add.
38740      */
38741     
38742     
38743     getChildContainer: function () {
38744         return this.getEl();
38745     }
38746     
38747     
38748     /*
38749         var  ret = new Roo.factory(cfg);
38750         return ret;
38751         
38752         
38753         // add form..
38754         if (cfg.xtype.match(/^Form$/)) {
38755             
38756             var el;
38757             //if (this.footer) {
38758             //    el = this.footer.container.insertSibling(false, 'before');
38759             //} else {
38760                 el = this.el.createChild();
38761             //}
38762
38763             this.form = new  Roo.form.Form(cfg);
38764             
38765             
38766             if ( this.form.allItems.length) {
38767                 this.form.render(el.dom);
38768             }
38769             return this.form;
38770         }
38771         // should only have one of theses..
38772         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
38773             // views.. should not be just added - used named prop 'view''
38774             
38775             cfg.el = this.el.appendChild(document.createElement("div"));
38776             // factory?
38777             
38778             var ret = new Roo.factory(cfg);
38779              
38780              ret.render && ret.render(false, ''); // render blank..
38781             this.view = ret;
38782             return ret;
38783         }
38784         return false;
38785     }
38786     \*/
38787 });
38788  
38789 /**
38790  * @class Roo.bootstrap.panel.Grid
38791  * @extends Roo.bootstrap.panel.Content
38792  * @constructor
38793  * Create a new GridPanel.
38794  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
38795  * @param {Object} config A the config object
38796   
38797  */
38798
38799
38800
38801 Roo.bootstrap.panel.Grid = function(config)
38802 {
38803     
38804       
38805     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
38806         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
38807
38808     config.el = this.wrapper;
38809     //this.el = this.wrapper;
38810     
38811       if (config.container) {
38812         // ctor'ed from a Border/panel.grid
38813         
38814         
38815         this.wrapper.setStyle("overflow", "hidden");
38816         this.wrapper.addClass('roo-grid-container');
38817
38818     }
38819     
38820     
38821     if(config.toolbar){
38822         var tool_el = this.wrapper.createChild();    
38823         this.toolbar = Roo.factory(config.toolbar);
38824         var ti = [];
38825         if (config.toolbar.items) {
38826             ti = config.toolbar.items ;
38827             delete config.toolbar.items ;
38828         }
38829         
38830         var nitems = [];
38831         this.toolbar.render(tool_el);
38832         for(var i =0;i < ti.length;i++) {
38833           //  Roo.log(['add child', items[i]]);
38834             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
38835         }
38836         this.toolbar.items = nitems;
38837         
38838         delete config.toolbar;
38839     }
38840     
38841     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
38842     config.grid.scrollBody = true;;
38843     config.grid.monitorWindowResize = false; // turn off autosizing
38844     config.grid.autoHeight = false;
38845     config.grid.autoWidth = false;
38846     
38847     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
38848     
38849     if (config.background) {
38850         // render grid on panel activation (if panel background)
38851         this.on('activate', function(gp) {
38852             if (!gp.grid.rendered) {
38853                 gp.grid.render(this.wrapper);
38854                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
38855             }
38856         });
38857             
38858     } else {
38859         this.grid.render(this.wrapper);
38860         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
38861
38862     }
38863     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
38864     // ??? needed ??? config.el = this.wrapper;
38865     
38866     
38867     
38868   
38869     // xtype created footer. - not sure if will work as we normally have to render first..
38870     if (this.footer && !this.footer.el && this.footer.xtype) {
38871         
38872         var ctr = this.grid.getView().getFooterPanel(true);
38873         this.footer.dataSource = this.grid.dataSource;
38874         this.footer = Roo.factory(this.footer, Roo);
38875         this.footer.render(ctr);
38876         
38877     }
38878     
38879     
38880     
38881     
38882      
38883 };
38884
38885 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
38886     getId : function(){
38887         return this.grid.id;
38888     },
38889     
38890     /**
38891      * Returns the grid for this panel
38892      * @return {Roo.bootstrap.Table} 
38893      */
38894     getGrid : function(){
38895         return this.grid;    
38896     },
38897     
38898     setSize : function(width, height){
38899         if(!this.ignoreResize(width, height)){
38900             var grid = this.grid;
38901             var size = this.adjustForComponents(width, height);
38902             var gridel = grid.getGridEl();
38903             gridel.setSize(size.width, size.height);
38904             /*
38905             var thd = grid.getGridEl().select('thead',true).first();
38906             var tbd = grid.getGridEl().select('tbody', true).first();
38907             if (tbd) {
38908                 tbd.setSize(width, height - thd.getHeight());
38909             }
38910             */
38911             grid.autoSize();
38912         }
38913     },
38914      
38915     
38916     
38917     beforeSlide : function(){
38918         this.grid.getView().scroller.clip();
38919     },
38920     
38921     afterSlide : function(){
38922         this.grid.getView().scroller.unclip();
38923     },
38924     
38925     destroy : function(){
38926         this.grid.destroy();
38927         delete this.grid;
38928         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
38929     }
38930 });
38931
38932 /**
38933  * @class Roo.bootstrap.panel.Nest
38934  * @extends Roo.bootstrap.panel.Content
38935  * @constructor
38936  * Create a new Panel, that can contain a layout.Border.
38937  * 
38938  * 
38939  * @param {Roo.BorderLayout} layout The layout for this panel
38940  * @param {String/Object} config A string to set only the title or a config object
38941  */
38942 Roo.bootstrap.panel.Nest = function(config)
38943 {
38944     // construct with only one argument..
38945     /* FIXME - implement nicer consturctors
38946     if (layout.layout) {
38947         config = layout;
38948         layout = config.layout;
38949         delete config.layout;
38950     }
38951     if (layout.xtype && !layout.getEl) {
38952         // then layout needs constructing..
38953         layout = Roo.factory(layout, Roo);
38954     }
38955     */
38956     
38957     config.el =  config.layout.getEl();
38958     
38959     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
38960     
38961     config.layout.monitorWindowResize = false; // turn off autosizing
38962     this.layout = config.layout;
38963     this.layout.getEl().addClass("roo-layout-nested-layout");
38964     this.layout.parent = this;
38965     
38966     
38967     
38968     
38969 };
38970
38971 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
38972
38973     setSize : function(width, height){
38974         if(!this.ignoreResize(width, height)){
38975             var size = this.adjustForComponents(width, height);
38976             var el = this.layout.getEl();
38977             if (size.height < 1) {
38978                 el.setWidth(size.width);   
38979             } else {
38980                 el.setSize(size.width, size.height);
38981             }
38982             var touch = el.dom.offsetWidth;
38983             this.layout.layout();
38984             // ie requires a double layout on the first pass
38985             if(Roo.isIE && !this.initialized){
38986                 this.initialized = true;
38987                 this.layout.layout();
38988             }
38989         }
38990     },
38991     
38992     // activate all subpanels if not currently active..
38993     
38994     setActiveState : function(active){
38995         this.active = active;
38996         this.setActiveClass(active);
38997         
38998         if(!active){
38999             this.fireEvent("deactivate", this);
39000             return;
39001         }
39002         
39003         this.fireEvent("activate", this);
39004         // not sure if this should happen before or after..
39005         if (!this.layout) {
39006             return; // should not happen..
39007         }
39008         var reg = false;
39009         for (var r in this.layout.regions) {
39010             reg = this.layout.getRegion(r);
39011             if (reg.getActivePanel()) {
39012                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
39013                 reg.setActivePanel(reg.getActivePanel());
39014                 continue;
39015             }
39016             if (!reg.panels.length) {
39017                 continue;
39018             }
39019             reg.showPanel(reg.getPanel(0));
39020         }
39021         
39022         
39023         
39024         
39025     },
39026     
39027     /**
39028      * Returns the nested BorderLayout for this panel
39029      * @return {Roo.BorderLayout} 
39030      */
39031     getLayout : function(){
39032         return this.layout;
39033     },
39034     
39035      /**
39036      * Adds a xtype elements to the layout of the nested panel
39037      * <pre><code>
39038
39039 panel.addxtype({
39040        xtype : 'ContentPanel',
39041        region: 'west',
39042        items: [ .... ]
39043    }
39044 );
39045
39046 panel.addxtype({
39047         xtype : 'NestedLayoutPanel',
39048         region: 'west',
39049         layout: {
39050            center: { },
39051            west: { }   
39052         },
39053         items : [ ... list of content panels or nested layout panels.. ]
39054    }
39055 );
39056 </code></pre>
39057      * @param {Object} cfg Xtype definition of item to add.
39058      */
39059     addxtype : function(cfg) {
39060         return this.layout.addxtype(cfg);
39061     
39062     }
39063 });/*
39064  * Based on:
39065  * Ext JS Library 1.1.1
39066  * Copyright(c) 2006-2007, Ext JS, LLC.
39067  *
39068  * Originally Released Under LGPL - original licence link has changed is not relivant.
39069  *
39070  * Fork - LGPL
39071  * <script type="text/javascript">
39072  */
39073 /**
39074  * @class Roo.TabPanel
39075  * @extends Roo.util.Observable
39076  * A lightweight tab container.
39077  * <br><br>
39078  * Usage:
39079  * <pre><code>
39080 // basic tabs 1, built from existing content
39081 var tabs = new Roo.TabPanel("tabs1");
39082 tabs.addTab("script", "View Script");
39083 tabs.addTab("markup", "View Markup");
39084 tabs.activate("script");
39085
39086 // more advanced tabs, built from javascript
39087 var jtabs = new Roo.TabPanel("jtabs");
39088 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
39089
39090 // set up the UpdateManager
39091 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
39092 var updater = tab2.getUpdateManager();
39093 updater.setDefaultUrl("ajax1.htm");
39094 tab2.on('activate', updater.refresh, updater, true);
39095
39096 // Use setUrl for Ajax loading
39097 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
39098 tab3.setUrl("ajax2.htm", null, true);
39099
39100 // Disabled tab
39101 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
39102 tab4.disable();
39103
39104 jtabs.activate("jtabs-1");
39105  * </code></pre>
39106  * @constructor
39107  * Create a new TabPanel.
39108  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
39109  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
39110  */
39111 Roo.bootstrap.panel.Tabs = function(config){
39112     /**
39113     * The container element for this TabPanel.
39114     * @type Roo.Element
39115     */
39116     this.el = Roo.get(config.el);
39117     delete config.el;
39118     if(config){
39119         if(typeof config == "boolean"){
39120             this.tabPosition = config ? "bottom" : "top";
39121         }else{
39122             Roo.apply(this, config);
39123         }
39124     }
39125     
39126     if(this.tabPosition == "bottom"){
39127         // if tabs are at the bottom = create the body first.
39128         this.bodyEl = Roo.get(this.createBody(this.el.dom));
39129         this.el.addClass("roo-tabs-bottom");
39130     }
39131     // next create the tabs holders
39132     
39133     if (this.tabPosition == "west"){
39134         
39135         var reg = this.region; // fake it..
39136         while (reg) {
39137             if (!reg.mgr.parent) {
39138                 break;
39139             }
39140             reg = reg.mgr.parent.region;
39141         }
39142         Roo.log("got nest?");
39143         Roo.log(reg);
39144         if (reg.mgr.getRegion('west')) {
39145             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
39146             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
39147             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
39148             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
39149             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
39150         
39151             
39152         }
39153         
39154         
39155     } else {
39156      
39157         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
39158         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
39159         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
39160         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
39161     }
39162     
39163     
39164     if(Roo.isIE){
39165         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
39166     }
39167     
39168     // finally - if tabs are at the top, then create the body last..
39169     if(this.tabPosition != "bottom"){
39170         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
39171          * @type Roo.Element
39172          */
39173         this.bodyEl = Roo.get(this.createBody(this.el.dom));
39174         this.el.addClass("roo-tabs-top");
39175     }
39176     this.items = [];
39177
39178     this.bodyEl.setStyle("position", "relative");
39179
39180     this.active = null;
39181     this.activateDelegate = this.activate.createDelegate(this);
39182
39183     this.addEvents({
39184         /**
39185          * @event tabchange
39186          * Fires when the active tab changes
39187          * @param {Roo.TabPanel} this
39188          * @param {Roo.TabPanelItem} activePanel The new active tab
39189          */
39190         "tabchange": true,
39191         /**
39192          * @event beforetabchange
39193          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
39194          * @param {Roo.TabPanel} this
39195          * @param {Object} e Set cancel to true on this object to cancel the tab change
39196          * @param {Roo.TabPanelItem} tab The tab being changed to
39197          */
39198         "beforetabchange" : true
39199     });
39200
39201     Roo.EventManager.onWindowResize(this.onResize, this);
39202     this.cpad = this.el.getPadding("lr");
39203     this.hiddenCount = 0;
39204
39205
39206     // toolbar on the tabbar support...
39207     if (this.toolbar) {
39208         alert("no toolbar support yet");
39209         this.toolbar  = false;
39210         /*
39211         var tcfg = this.toolbar;
39212         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
39213         this.toolbar = new Roo.Toolbar(tcfg);
39214         if (Roo.isSafari) {
39215             var tbl = tcfg.container.child('table', true);
39216             tbl.setAttribute('width', '100%');
39217         }
39218         */
39219         
39220     }
39221    
39222
39223
39224     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
39225 };
39226
39227 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
39228     /*
39229      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
39230      */
39231     tabPosition : "top",
39232     /*
39233      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
39234      */
39235     currentTabWidth : 0,
39236     /*
39237      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
39238      */
39239     minTabWidth : 40,
39240     /*
39241      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
39242      */
39243     maxTabWidth : 250,
39244     /*
39245      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
39246      */
39247     preferredTabWidth : 175,
39248     /*
39249      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
39250      */
39251     resizeTabs : false,
39252     /*
39253      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
39254      */
39255     monitorResize : true,
39256     /*
39257      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
39258      */
39259     toolbar : false,  // set by caller..
39260     
39261     region : false, /// set by caller
39262     
39263     disableTooltips : true, // not used yet...
39264
39265     /**
39266      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
39267      * @param {String} id The id of the div to use <b>or create</b>
39268      * @param {String} text The text for the tab
39269      * @param {String} content (optional) Content to put in the TabPanelItem body
39270      * @param {Boolean} closable (optional) True to create a close icon on the tab
39271      * @return {Roo.TabPanelItem} The created TabPanelItem
39272      */
39273     addTab : function(id, text, content, closable, tpl)
39274     {
39275         var item = new Roo.bootstrap.panel.TabItem({
39276             panel: this,
39277             id : id,
39278             text : text,
39279             closable : closable,
39280             tpl : tpl
39281         });
39282         this.addTabItem(item);
39283         if(content){
39284             item.setContent(content);
39285         }
39286         return item;
39287     },
39288
39289     /**
39290      * Returns the {@link Roo.TabPanelItem} with the specified id/index
39291      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
39292      * @return {Roo.TabPanelItem}
39293      */
39294     getTab : function(id){
39295         return this.items[id];
39296     },
39297
39298     /**
39299      * Hides the {@link Roo.TabPanelItem} with the specified id/index
39300      * @param {String/Number} id The id or index of the TabPanelItem to hide.
39301      */
39302     hideTab : function(id){
39303         var t = this.items[id];
39304         if(!t.isHidden()){
39305            t.setHidden(true);
39306            this.hiddenCount++;
39307            this.autoSizeTabs();
39308         }
39309     },
39310
39311     /**
39312      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
39313      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
39314      */
39315     unhideTab : function(id){
39316         var t = this.items[id];
39317         if(t.isHidden()){
39318            t.setHidden(false);
39319            this.hiddenCount--;
39320            this.autoSizeTabs();
39321         }
39322     },
39323
39324     /**
39325      * Adds an existing {@link Roo.TabPanelItem}.
39326      * @param {Roo.TabPanelItem} item The TabPanelItem to add
39327      */
39328     addTabItem : function(item)
39329     {
39330         this.items[item.id] = item;
39331         this.items.push(item);
39332         this.autoSizeTabs();
39333       //  if(this.resizeTabs){
39334     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
39335   //         this.autoSizeTabs();
39336 //        }else{
39337 //            item.autoSize();
39338        // }
39339     },
39340
39341     /**
39342      * Removes a {@link Roo.TabPanelItem}.
39343      * @param {String/Number} id The id or index of the TabPanelItem to remove.
39344      */
39345     removeTab : function(id){
39346         var items = this.items;
39347         var tab = items[id];
39348         if(!tab) { return; }
39349         var index = items.indexOf(tab);
39350         if(this.active == tab && items.length > 1){
39351             var newTab = this.getNextAvailable(index);
39352             if(newTab) {
39353                 newTab.activate();
39354             }
39355         }
39356         this.stripEl.dom.removeChild(tab.pnode.dom);
39357         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
39358             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
39359         }
39360         items.splice(index, 1);
39361         delete this.items[tab.id];
39362         tab.fireEvent("close", tab);
39363         tab.purgeListeners();
39364         this.autoSizeTabs();
39365     },
39366
39367     getNextAvailable : function(start){
39368         var items = this.items;
39369         var index = start;
39370         // look for a next tab that will slide over to
39371         // replace the one being removed
39372         while(index < items.length){
39373             var item = items[++index];
39374             if(item && !item.isHidden()){
39375                 return item;
39376             }
39377         }
39378         // if one isn't found select the previous tab (on the left)
39379         index = start;
39380         while(index >= 0){
39381             var item = items[--index];
39382             if(item && !item.isHidden()){
39383                 return item;
39384             }
39385         }
39386         return null;
39387     },
39388
39389     /**
39390      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
39391      * @param {String/Number} id The id or index of the TabPanelItem to disable.
39392      */
39393     disableTab : function(id){
39394         var tab = this.items[id];
39395         if(tab && this.active != tab){
39396             tab.disable();
39397         }
39398     },
39399
39400     /**
39401      * Enables a {@link Roo.TabPanelItem} that is disabled.
39402      * @param {String/Number} id The id or index of the TabPanelItem to enable.
39403      */
39404     enableTab : function(id){
39405         var tab = this.items[id];
39406         tab.enable();
39407     },
39408
39409     /**
39410      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
39411      * @param {String/Number} id The id or index of the TabPanelItem to activate.
39412      * @return {Roo.TabPanelItem} The TabPanelItem.
39413      */
39414     activate : function(id)
39415     {
39416         //Roo.log('activite:'  + id);
39417         
39418         var tab = this.items[id];
39419         if(!tab){
39420             return null;
39421         }
39422         if(tab == this.active || tab.disabled){
39423             return tab;
39424         }
39425         var e = {};
39426         this.fireEvent("beforetabchange", this, e, tab);
39427         if(e.cancel !== true && !tab.disabled){
39428             if(this.active){
39429                 this.active.hide();
39430             }
39431             this.active = this.items[id];
39432             this.active.show();
39433             this.fireEvent("tabchange", this, this.active);
39434         }
39435         return tab;
39436     },
39437
39438     /**
39439      * Gets the active {@link Roo.TabPanelItem}.
39440      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
39441      */
39442     getActiveTab : function(){
39443         return this.active;
39444     },
39445
39446     /**
39447      * Updates the tab body element to fit the height of the container element
39448      * for overflow scrolling
39449      * @param {Number} targetHeight (optional) Override the starting height from the elements height
39450      */
39451     syncHeight : function(targetHeight){
39452         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
39453         var bm = this.bodyEl.getMargins();
39454         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
39455         this.bodyEl.setHeight(newHeight);
39456         return newHeight;
39457     },
39458
39459     onResize : function(){
39460         if(this.monitorResize){
39461             this.autoSizeTabs();
39462         }
39463     },
39464
39465     /**
39466      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
39467      */
39468     beginUpdate : function(){
39469         this.updating = true;
39470     },
39471
39472     /**
39473      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
39474      */
39475     endUpdate : function(){
39476         this.updating = false;
39477         this.autoSizeTabs();
39478     },
39479
39480     /**
39481      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
39482      */
39483     autoSizeTabs : function()
39484     {
39485         var count = this.items.length;
39486         var vcount = count - this.hiddenCount;
39487         
39488         if (vcount < 2) {
39489             this.stripEl.hide();
39490         } else {
39491             this.stripEl.show();
39492         }
39493         
39494         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
39495             return;
39496         }
39497         
39498         
39499         var w = Math.max(this.el.getWidth() - this.cpad, 10);
39500         var availWidth = Math.floor(w / vcount);
39501         var b = this.stripBody;
39502         if(b.getWidth() > w){
39503             var tabs = this.items;
39504             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
39505             if(availWidth < this.minTabWidth){
39506                 /*if(!this.sleft){    // incomplete scrolling code
39507                     this.createScrollButtons();
39508                 }
39509                 this.showScroll();
39510                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
39511             }
39512         }else{
39513             if(this.currentTabWidth < this.preferredTabWidth){
39514                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
39515             }
39516         }
39517     },
39518
39519     /**
39520      * Returns the number of tabs in this TabPanel.
39521      * @return {Number}
39522      */
39523      getCount : function(){
39524          return this.items.length;
39525      },
39526
39527     /**
39528      * Resizes all the tabs to the passed width
39529      * @param {Number} The new width
39530      */
39531     setTabWidth : function(width){
39532         this.currentTabWidth = width;
39533         for(var i = 0, len = this.items.length; i < len; i++) {
39534                 if(!this.items[i].isHidden()) {
39535                 this.items[i].setWidth(width);
39536             }
39537         }
39538     },
39539
39540     /**
39541      * Destroys this TabPanel
39542      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
39543      */
39544     destroy : function(removeEl){
39545         Roo.EventManager.removeResizeListener(this.onResize, this);
39546         for(var i = 0, len = this.items.length; i < len; i++){
39547             this.items[i].purgeListeners();
39548         }
39549         if(removeEl === true){
39550             this.el.update("");
39551             this.el.remove();
39552         }
39553     },
39554     
39555     createStrip : function(container)
39556     {
39557         var strip = document.createElement("nav");
39558         strip.className = Roo.bootstrap.version == 4 ?
39559             "navbar-light bg-light" : 
39560             "navbar navbar-default"; //"x-tabs-wrap";
39561         container.appendChild(strip);
39562         return strip;
39563     },
39564     
39565     createStripList : function(strip)
39566     {
39567         // div wrapper for retard IE
39568         // returns the "tr" element.
39569         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
39570         //'<div class="x-tabs-strip-wrap">'+
39571           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
39572           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
39573         return strip.firstChild; //.firstChild.firstChild.firstChild;
39574     },
39575     createBody : function(container)
39576     {
39577         var body = document.createElement("div");
39578         Roo.id(body, "tab-body");
39579         //Roo.fly(body).addClass("x-tabs-body");
39580         Roo.fly(body).addClass("tab-content");
39581         container.appendChild(body);
39582         return body;
39583     },
39584     createItemBody :function(bodyEl, id){
39585         var body = Roo.getDom(id);
39586         if(!body){
39587             body = document.createElement("div");
39588             body.id = id;
39589         }
39590         //Roo.fly(body).addClass("x-tabs-item-body");
39591         Roo.fly(body).addClass("tab-pane");
39592          bodyEl.insertBefore(body, bodyEl.firstChild);
39593         return body;
39594     },
39595     /** @private */
39596     createStripElements :  function(stripEl, text, closable, tpl)
39597     {
39598         var td = document.createElement("li"); // was td..
39599         td.className = 'nav-item';
39600         
39601         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
39602         
39603         
39604         stripEl.appendChild(td);
39605         /*if(closable){
39606             td.className = "x-tabs-closable";
39607             if(!this.closeTpl){
39608                 this.closeTpl = new Roo.Template(
39609                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
39610                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
39611                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
39612                 );
39613             }
39614             var el = this.closeTpl.overwrite(td, {"text": text});
39615             var close = el.getElementsByTagName("div")[0];
39616             var inner = el.getElementsByTagName("em")[0];
39617             return {"el": el, "close": close, "inner": inner};
39618         } else {
39619         */
39620         // not sure what this is..
39621 //            if(!this.tabTpl){
39622                 //this.tabTpl = new Roo.Template(
39623                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
39624                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
39625                 //);
39626 //                this.tabTpl = new Roo.Template(
39627 //                   '<a href="#">' +
39628 //                   '<span unselectable="on"' +
39629 //                            (this.disableTooltips ? '' : ' title="{text}"') +
39630 //                            ' >{text}</span></a>'
39631 //                );
39632 //                
39633 //            }
39634
39635
39636             var template = tpl || this.tabTpl || false;
39637             
39638             if(!template){
39639                 template =  new Roo.Template(
39640                         Roo.bootstrap.version == 4 ? 
39641                             (
39642                                 '<a class="nav-link" href="#" unselectable="on"' +
39643                                      (this.disableTooltips ? '' : ' title="{text}"') +
39644                                      ' >{text}</a>'
39645                             ) : (
39646                                 '<a class="nav-link" href="#">' +
39647                                 '<span unselectable="on"' +
39648                                          (this.disableTooltips ? '' : ' title="{text}"') +
39649                                     ' >{text}</span></a>'
39650                             )
39651                 );
39652             }
39653             
39654             switch (typeof(template)) {
39655                 case 'object' :
39656                     break;
39657                 case 'string' :
39658                     template = new Roo.Template(template);
39659                     break;
39660                 default :
39661                     break;
39662             }
39663             
39664             var el = template.overwrite(td, {"text": text});
39665             
39666             var inner = el.getElementsByTagName("span")[0];
39667             
39668             return {"el": el, "inner": inner};
39669             
39670     }
39671         
39672     
39673 });
39674
39675 /**
39676  * @class Roo.TabPanelItem
39677  * @extends Roo.util.Observable
39678  * Represents an individual item (tab plus body) in a TabPanel.
39679  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
39680  * @param {String} id The id of this TabPanelItem
39681  * @param {String} text The text for the tab of this TabPanelItem
39682  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
39683  */
39684 Roo.bootstrap.panel.TabItem = function(config){
39685     /**
39686      * The {@link Roo.TabPanel} this TabPanelItem belongs to
39687      * @type Roo.TabPanel
39688      */
39689     this.tabPanel = config.panel;
39690     /**
39691      * The id for this TabPanelItem
39692      * @type String
39693      */
39694     this.id = config.id;
39695     /** @private */
39696     this.disabled = false;
39697     /** @private */
39698     this.text = config.text;
39699     /** @private */
39700     this.loaded = false;
39701     this.closable = config.closable;
39702
39703     /**
39704      * The body element for this TabPanelItem.
39705      * @type Roo.Element
39706      */
39707     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
39708     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
39709     this.bodyEl.setStyle("display", "block");
39710     this.bodyEl.setStyle("zoom", "1");
39711     //this.hideAction();
39712
39713     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
39714     /** @private */
39715     this.el = Roo.get(els.el);
39716     this.inner = Roo.get(els.inner, true);
39717      this.textEl = Roo.bootstrap.version == 4 ?
39718         this.el : Roo.get(this.el.dom.firstChild, true);
39719
39720     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
39721     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
39722
39723     
39724 //    this.el.on("mousedown", this.onTabMouseDown, this);
39725     this.el.on("click", this.onTabClick, this);
39726     /** @private */
39727     if(config.closable){
39728         var c = Roo.get(els.close, true);
39729         c.dom.title = this.closeText;
39730         c.addClassOnOver("close-over");
39731         c.on("click", this.closeClick, this);
39732      }
39733
39734     this.addEvents({
39735          /**
39736          * @event activate
39737          * Fires when this tab becomes the active tab.
39738          * @param {Roo.TabPanel} tabPanel The parent TabPanel
39739          * @param {Roo.TabPanelItem} this
39740          */
39741         "activate": true,
39742         /**
39743          * @event beforeclose
39744          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
39745          * @param {Roo.TabPanelItem} this
39746          * @param {Object} e Set cancel to true on this object to cancel the close.
39747          */
39748         "beforeclose": true,
39749         /**
39750          * @event close
39751          * Fires when this tab is closed.
39752          * @param {Roo.TabPanelItem} this
39753          */
39754          "close": true,
39755         /**
39756          * @event deactivate
39757          * Fires when this tab is no longer the active tab.
39758          * @param {Roo.TabPanel} tabPanel The parent TabPanel
39759          * @param {Roo.TabPanelItem} this
39760          */
39761          "deactivate" : true
39762     });
39763     this.hidden = false;
39764
39765     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
39766 };
39767
39768 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
39769            {
39770     purgeListeners : function(){
39771        Roo.util.Observable.prototype.purgeListeners.call(this);
39772        this.el.removeAllListeners();
39773     },
39774     /**
39775      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
39776      */
39777     show : function(){
39778         this.status_node.addClass("active");
39779         this.showAction();
39780         if(Roo.isOpera){
39781             this.tabPanel.stripWrap.repaint();
39782         }
39783         this.fireEvent("activate", this.tabPanel, this);
39784     },
39785
39786     /**
39787      * Returns true if this tab is the active tab.
39788      * @return {Boolean}
39789      */
39790     isActive : function(){
39791         return this.tabPanel.getActiveTab() == this;
39792     },
39793
39794     /**
39795      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
39796      */
39797     hide : function(){
39798         this.status_node.removeClass("active");
39799         this.hideAction();
39800         this.fireEvent("deactivate", this.tabPanel, this);
39801     },
39802
39803     hideAction : function(){
39804         this.bodyEl.hide();
39805         this.bodyEl.setStyle("position", "absolute");
39806         this.bodyEl.setLeft("-20000px");
39807         this.bodyEl.setTop("-20000px");
39808     },
39809
39810     showAction : function(){
39811         this.bodyEl.setStyle("position", "relative");
39812         this.bodyEl.setTop("");
39813         this.bodyEl.setLeft("");
39814         this.bodyEl.show();
39815     },
39816
39817     /**
39818      * Set the tooltip for the tab.
39819      * @param {String} tooltip The tab's tooltip
39820      */
39821     setTooltip : function(text){
39822         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
39823             this.textEl.dom.qtip = text;
39824             this.textEl.dom.removeAttribute('title');
39825         }else{
39826             this.textEl.dom.title = text;
39827         }
39828     },
39829
39830     onTabClick : function(e){
39831         e.preventDefault();
39832         this.tabPanel.activate(this.id);
39833     },
39834
39835     onTabMouseDown : function(e){
39836         e.preventDefault();
39837         this.tabPanel.activate(this.id);
39838     },
39839 /*
39840     getWidth : function(){
39841         return this.inner.getWidth();
39842     },
39843
39844     setWidth : function(width){
39845         var iwidth = width - this.linode.getPadding("lr");
39846         this.inner.setWidth(iwidth);
39847         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
39848         this.linode.setWidth(width);
39849     },
39850 */
39851     /**
39852      * Show or hide the tab
39853      * @param {Boolean} hidden True to hide or false to show.
39854      */
39855     setHidden : function(hidden){
39856         this.hidden = hidden;
39857         this.linode.setStyle("display", hidden ? "none" : "");
39858     },
39859
39860     /**
39861      * Returns true if this tab is "hidden"
39862      * @return {Boolean}
39863      */
39864     isHidden : function(){
39865         return this.hidden;
39866     },
39867
39868     /**
39869      * Returns the text for this tab
39870      * @return {String}
39871      */
39872     getText : function(){
39873         return this.text;
39874     },
39875     /*
39876     autoSize : function(){
39877         //this.el.beginMeasure();
39878         this.textEl.setWidth(1);
39879         /*
39880          *  #2804 [new] Tabs in Roojs
39881          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
39882          */
39883         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
39884         //this.el.endMeasure();
39885     //},
39886
39887     /**
39888      * Sets the text for the tab (Note: this also sets the tooltip text)
39889      * @param {String} text The tab's text and tooltip
39890      */
39891     setText : function(text){
39892         this.text = text;
39893         this.textEl.update(text);
39894         this.setTooltip(text);
39895         //if(!this.tabPanel.resizeTabs){
39896         //    this.autoSize();
39897         //}
39898     },
39899     /**
39900      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
39901      */
39902     activate : function(){
39903         this.tabPanel.activate(this.id);
39904     },
39905
39906     /**
39907      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
39908      */
39909     disable : function(){
39910         if(this.tabPanel.active != this){
39911             this.disabled = true;
39912             this.status_node.addClass("disabled");
39913         }
39914     },
39915
39916     /**
39917      * Enables this TabPanelItem if it was previously disabled.
39918      */
39919     enable : function(){
39920         this.disabled = false;
39921         this.status_node.removeClass("disabled");
39922     },
39923
39924     /**
39925      * Sets the content for this TabPanelItem.
39926      * @param {String} content The content
39927      * @param {Boolean} loadScripts true to look for and load scripts
39928      */
39929     setContent : function(content, loadScripts){
39930         this.bodyEl.update(content, loadScripts);
39931     },
39932
39933     /**
39934      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
39935      * @return {Roo.UpdateManager} The UpdateManager
39936      */
39937     getUpdateManager : function(){
39938         return this.bodyEl.getUpdateManager();
39939     },
39940
39941     /**
39942      * Set a URL to be used to load the content for this TabPanelItem.
39943      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
39944      * @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)
39945      * @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)
39946      * @return {Roo.UpdateManager} The UpdateManager
39947      */
39948     setUrl : function(url, params, loadOnce){
39949         if(this.refreshDelegate){
39950             this.un('activate', this.refreshDelegate);
39951         }
39952         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39953         this.on("activate", this.refreshDelegate);
39954         return this.bodyEl.getUpdateManager();
39955     },
39956
39957     /** @private */
39958     _handleRefresh : function(url, params, loadOnce){
39959         if(!loadOnce || !this.loaded){
39960             var updater = this.bodyEl.getUpdateManager();
39961             updater.update(url, params, this._setLoaded.createDelegate(this));
39962         }
39963     },
39964
39965     /**
39966      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
39967      *   Will fail silently if the setUrl method has not been called.
39968      *   This does not activate the panel, just updates its content.
39969      */
39970     refresh : function(){
39971         if(this.refreshDelegate){
39972            this.loaded = false;
39973            this.refreshDelegate();
39974         }
39975     },
39976
39977     /** @private */
39978     _setLoaded : function(){
39979         this.loaded = true;
39980     },
39981
39982     /** @private */
39983     closeClick : function(e){
39984         var o = {};
39985         e.stopEvent();
39986         this.fireEvent("beforeclose", this, o);
39987         if(o.cancel !== true){
39988             this.tabPanel.removeTab(this.id);
39989         }
39990     },
39991     /**
39992      * The text displayed in the tooltip for the close icon.
39993      * @type String
39994      */
39995     closeText : "Close this tab"
39996 });
39997 /**
39998 *    This script refer to:
39999 *    Title: International Telephone Input
40000 *    Author: Jack O'Connor
40001 *    Code version:  v12.1.12
40002 *    Availability: https://github.com/jackocnr/intl-tel-input.git
40003 **/
40004
40005 Roo.bootstrap.PhoneInputData = function() {
40006     var d = [
40007       [
40008         "Afghanistan (‫افغانستان‬‎)",
40009         "af",
40010         "93"
40011       ],
40012       [
40013         "Albania (Shqipëri)",
40014         "al",
40015         "355"
40016       ],
40017       [
40018         "Algeria (‫الجزائر‬‎)",
40019         "dz",
40020         "213"
40021       ],
40022       [
40023         "American Samoa",
40024         "as",
40025         "1684"
40026       ],
40027       [
40028         "Andorra",
40029         "ad",
40030         "376"
40031       ],
40032       [
40033         "Angola",
40034         "ao",
40035         "244"
40036       ],
40037       [
40038         "Anguilla",
40039         "ai",
40040         "1264"
40041       ],
40042       [
40043         "Antigua and Barbuda",
40044         "ag",
40045         "1268"
40046       ],
40047       [
40048         "Argentina",
40049         "ar",
40050         "54"
40051       ],
40052       [
40053         "Armenia (Հայաստան)",
40054         "am",
40055         "374"
40056       ],
40057       [
40058         "Aruba",
40059         "aw",
40060         "297"
40061       ],
40062       [
40063         "Australia",
40064         "au",
40065         "61",
40066         0
40067       ],
40068       [
40069         "Austria (Österreich)",
40070         "at",
40071         "43"
40072       ],
40073       [
40074         "Azerbaijan (Azərbaycan)",
40075         "az",
40076         "994"
40077       ],
40078       [
40079         "Bahamas",
40080         "bs",
40081         "1242"
40082       ],
40083       [
40084         "Bahrain (‫البحرين‬‎)",
40085         "bh",
40086         "973"
40087       ],
40088       [
40089         "Bangladesh (বাংলাদেশ)",
40090         "bd",
40091         "880"
40092       ],
40093       [
40094         "Barbados",
40095         "bb",
40096         "1246"
40097       ],
40098       [
40099         "Belarus (Беларусь)",
40100         "by",
40101         "375"
40102       ],
40103       [
40104         "Belgium (België)",
40105         "be",
40106         "32"
40107       ],
40108       [
40109         "Belize",
40110         "bz",
40111         "501"
40112       ],
40113       [
40114         "Benin (Bénin)",
40115         "bj",
40116         "229"
40117       ],
40118       [
40119         "Bermuda",
40120         "bm",
40121         "1441"
40122       ],
40123       [
40124         "Bhutan (འབྲུག)",
40125         "bt",
40126         "975"
40127       ],
40128       [
40129         "Bolivia",
40130         "bo",
40131         "591"
40132       ],
40133       [
40134         "Bosnia and Herzegovina (Босна и Херцеговина)",
40135         "ba",
40136         "387"
40137       ],
40138       [
40139         "Botswana",
40140         "bw",
40141         "267"
40142       ],
40143       [
40144         "Brazil (Brasil)",
40145         "br",
40146         "55"
40147       ],
40148       [
40149         "British Indian Ocean Territory",
40150         "io",
40151         "246"
40152       ],
40153       [
40154         "British Virgin Islands",
40155         "vg",
40156         "1284"
40157       ],
40158       [
40159         "Brunei",
40160         "bn",
40161         "673"
40162       ],
40163       [
40164         "Bulgaria (България)",
40165         "bg",
40166         "359"
40167       ],
40168       [
40169         "Burkina Faso",
40170         "bf",
40171         "226"
40172       ],
40173       [
40174         "Burundi (Uburundi)",
40175         "bi",
40176         "257"
40177       ],
40178       [
40179         "Cambodia (កម្ពុជា)",
40180         "kh",
40181         "855"
40182       ],
40183       [
40184         "Cameroon (Cameroun)",
40185         "cm",
40186         "237"
40187       ],
40188       [
40189         "Canada",
40190         "ca",
40191         "1",
40192         1,
40193         ["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"]
40194       ],
40195       [
40196         "Cape Verde (Kabu Verdi)",
40197         "cv",
40198         "238"
40199       ],
40200       [
40201         "Caribbean Netherlands",
40202         "bq",
40203         "599",
40204         1
40205       ],
40206       [
40207         "Cayman Islands",
40208         "ky",
40209         "1345"
40210       ],
40211       [
40212         "Central African Republic (République centrafricaine)",
40213         "cf",
40214         "236"
40215       ],
40216       [
40217         "Chad (Tchad)",
40218         "td",
40219         "235"
40220       ],
40221       [
40222         "Chile",
40223         "cl",
40224         "56"
40225       ],
40226       [
40227         "China (中国)",
40228         "cn",
40229         "86"
40230       ],
40231       [
40232         "Christmas Island",
40233         "cx",
40234         "61",
40235         2
40236       ],
40237       [
40238         "Cocos (Keeling) Islands",
40239         "cc",
40240         "61",
40241         1
40242       ],
40243       [
40244         "Colombia",
40245         "co",
40246         "57"
40247       ],
40248       [
40249         "Comoros (‫جزر القمر‬‎)",
40250         "km",
40251         "269"
40252       ],
40253       [
40254         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
40255         "cd",
40256         "243"
40257       ],
40258       [
40259         "Congo (Republic) (Congo-Brazzaville)",
40260         "cg",
40261         "242"
40262       ],
40263       [
40264         "Cook Islands",
40265         "ck",
40266         "682"
40267       ],
40268       [
40269         "Costa Rica",
40270         "cr",
40271         "506"
40272       ],
40273       [
40274         "Côte d’Ivoire",
40275         "ci",
40276         "225"
40277       ],
40278       [
40279         "Croatia (Hrvatska)",
40280         "hr",
40281         "385"
40282       ],
40283       [
40284         "Cuba",
40285         "cu",
40286         "53"
40287       ],
40288       [
40289         "Curaçao",
40290         "cw",
40291         "599",
40292         0
40293       ],
40294       [
40295         "Cyprus (Κύπρος)",
40296         "cy",
40297         "357"
40298       ],
40299       [
40300         "Czech Republic (Česká republika)",
40301         "cz",
40302         "420"
40303       ],
40304       [
40305         "Denmark (Danmark)",
40306         "dk",
40307         "45"
40308       ],
40309       [
40310         "Djibouti",
40311         "dj",
40312         "253"
40313       ],
40314       [
40315         "Dominica",
40316         "dm",
40317         "1767"
40318       ],
40319       [
40320         "Dominican Republic (República Dominicana)",
40321         "do",
40322         "1",
40323         2,
40324         ["809", "829", "849"]
40325       ],
40326       [
40327         "Ecuador",
40328         "ec",
40329         "593"
40330       ],
40331       [
40332         "Egypt (‫مصر‬‎)",
40333         "eg",
40334         "20"
40335       ],
40336       [
40337         "El Salvador",
40338         "sv",
40339         "503"
40340       ],
40341       [
40342         "Equatorial Guinea (Guinea Ecuatorial)",
40343         "gq",
40344         "240"
40345       ],
40346       [
40347         "Eritrea",
40348         "er",
40349         "291"
40350       ],
40351       [
40352         "Estonia (Eesti)",
40353         "ee",
40354         "372"
40355       ],
40356       [
40357         "Ethiopia",
40358         "et",
40359         "251"
40360       ],
40361       [
40362         "Falkland Islands (Islas Malvinas)",
40363         "fk",
40364         "500"
40365       ],
40366       [
40367         "Faroe Islands (Føroyar)",
40368         "fo",
40369         "298"
40370       ],
40371       [
40372         "Fiji",
40373         "fj",
40374         "679"
40375       ],
40376       [
40377         "Finland (Suomi)",
40378         "fi",
40379         "358",
40380         0
40381       ],
40382       [
40383         "France",
40384         "fr",
40385         "33"
40386       ],
40387       [
40388         "French Guiana (Guyane française)",
40389         "gf",
40390         "594"
40391       ],
40392       [
40393         "French Polynesia (Polynésie française)",
40394         "pf",
40395         "689"
40396       ],
40397       [
40398         "Gabon",
40399         "ga",
40400         "241"
40401       ],
40402       [
40403         "Gambia",
40404         "gm",
40405         "220"
40406       ],
40407       [
40408         "Georgia (საქართველო)",
40409         "ge",
40410         "995"
40411       ],
40412       [
40413         "Germany (Deutschland)",
40414         "de",
40415         "49"
40416       ],
40417       [
40418         "Ghana (Gaana)",
40419         "gh",
40420         "233"
40421       ],
40422       [
40423         "Gibraltar",
40424         "gi",
40425         "350"
40426       ],
40427       [
40428         "Greece (Ελλάδα)",
40429         "gr",
40430         "30"
40431       ],
40432       [
40433         "Greenland (Kalaallit Nunaat)",
40434         "gl",
40435         "299"
40436       ],
40437       [
40438         "Grenada",
40439         "gd",
40440         "1473"
40441       ],
40442       [
40443         "Guadeloupe",
40444         "gp",
40445         "590",
40446         0
40447       ],
40448       [
40449         "Guam",
40450         "gu",
40451         "1671"
40452       ],
40453       [
40454         "Guatemala",
40455         "gt",
40456         "502"
40457       ],
40458       [
40459         "Guernsey",
40460         "gg",
40461         "44",
40462         1
40463       ],
40464       [
40465         "Guinea (Guinée)",
40466         "gn",
40467         "224"
40468       ],
40469       [
40470         "Guinea-Bissau (Guiné Bissau)",
40471         "gw",
40472         "245"
40473       ],
40474       [
40475         "Guyana",
40476         "gy",
40477         "592"
40478       ],
40479       [
40480         "Haiti",
40481         "ht",
40482         "509"
40483       ],
40484       [
40485         "Honduras",
40486         "hn",
40487         "504"
40488       ],
40489       [
40490         "Hong Kong (香港)",
40491         "hk",
40492         "852"
40493       ],
40494       [
40495         "Hungary (Magyarország)",
40496         "hu",
40497         "36"
40498       ],
40499       [
40500         "Iceland (Ísland)",
40501         "is",
40502         "354"
40503       ],
40504       [
40505         "India (भारत)",
40506         "in",
40507         "91"
40508       ],
40509       [
40510         "Indonesia",
40511         "id",
40512         "62"
40513       ],
40514       [
40515         "Iran (‫ایران‬‎)",
40516         "ir",
40517         "98"
40518       ],
40519       [
40520         "Iraq (‫العراق‬‎)",
40521         "iq",
40522         "964"
40523       ],
40524       [
40525         "Ireland",
40526         "ie",
40527         "353"
40528       ],
40529       [
40530         "Isle of Man",
40531         "im",
40532         "44",
40533         2
40534       ],
40535       [
40536         "Israel (‫ישראל‬‎)",
40537         "il",
40538         "972"
40539       ],
40540       [
40541         "Italy (Italia)",
40542         "it",
40543         "39",
40544         0
40545       ],
40546       [
40547         "Jamaica",
40548         "jm",
40549         "1876"
40550       ],
40551       [
40552         "Japan (日本)",
40553         "jp",
40554         "81"
40555       ],
40556       [
40557         "Jersey",
40558         "je",
40559         "44",
40560         3
40561       ],
40562       [
40563         "Jordan (‫الأردن‬‎)",
40564         "jo",
40565         "962"
40566       ],
40567       [
40568         "Kazakhstan (Казахстан)",
40569         "kz",
40570         "7",
40571         1
40572       ],
40573       [
40574         "Kenya",
40575         "ke",
40576         "254"
40577       ],
40578       [
40579         "Kiribati",
40580         "ki",
40581         "686"
40582       ],
40583       [
40584         "Kosovo",
40585         "xk",
40586         "383"
40587       ],
40588       [
40589         "Kuwait (‫الكويت‬‎)",
40590         "kw",
40591         "965"
40592       ],
40593       [
40594         "Kyrgyzstan (Кыргызстан)",
40595         "kg",
40596         "996"
40597       ],
40598       [
40599         "Laos (ລາວ)",
40600         "la",
40601         "856"
40602       ],
40603       [
40604         "Latvia (Latvija)",
40605         "lv",
40606         "371"
40607       ],
40608       [
40609         "Lebanon (‫لبنان‬‎)",
40610         "lb",
40611         "961"
40612       ],
40613       [
40614         "Lesotho",
40615         "ls",
40616         "266"
40617       ],
40618       [
40619         "Liberia",
40620         "lr",
40621         "231"
40622       ],
40623       [
40624         "Libya (‫ليبيا‬‎)",
40625         "ly",
40626         "218"
40627       ],
40628       [
40629         "Liechtenstein",
40630         "li",
40631         "423"
40632       ],
40633       [
40634         "Lithuania (Lietuva)",
40635         "lt",
40636         "370"
40637       ],
40638       [
40639         "Luxembourg",
40640         "lu",
40641         "352"
40642       ],
40643       [
40644         "Macau (澳門)",
40645         "mo",
40646         "853"
40647       ],
40648       [
40649         "Macedonia (FYROM) (Македонија)",
40650         "mk",
40651         "389"
40652       ],
40653       [
40654         "Madagascar (Madagasikara)",
40655         "mg",
40656         "261"
40657       ],
40658       [
40659         "Malawi",
40660         "mw",
40661         "265"
40662       ],
40663       [
40664         "Malaysia",
40665         "my",
40666         "60"
40667       ],
40668       [
40669         "Maldives",
40670         "mv",
40671         "960"
40672       ],
40673       [
40674         "Mali",
40675         "ml",
40676         "223"
40677       ],
40678       [
40679         "Malta",
40680         "mt",
40681         "356"
40682       ],
40683       [
40684         "Marshall Islands",
40685         "mh",
40686         "692"
40687       ],
40688       [
40689         "Martinique",
40690         "mq",
40691         "596"
40692       ],
40693       [
40694         "Mauritania (‫موريتانيا‬‎)",
40695         "mr",
40696         "222"
40697       ],
40698       [
40699         "Mauritius (Moris)",
40700         "mu",
40701         "230"
40702       ],
40703       [
40704         "Mayotte",
40705         "yt",
40706         "262",
40707         1
40708       ],
40709       [
40710         "Mexico (México)",
40711         "mx",
40712         "52"
40713       ],
40714       [
40715         "Micronesia",
40716         "fm",
40717         "691"
40718       ],
40719       [
40720         "Moldova (Republica Moldova)",
40721         "md",
40722         "373"
40723       ],
40724       [
40725         "Monaco",
40726         "mc",
40727         "377"
40728       ],
40729       [
40730         "Mongolia (Монгол)",
40731         "mn",
40732         "976"
40733       ],
40734       [
40735         "Montenegro (Crna Gora)",
40736         "me",
40737         "382"
40738       ],
40739       [
40740         "Montserrat",
40741         "ms",
40742         "1664"
40743       ],
40744       [
40745         "Morocco (‫المغرب‬‎)",
40746         "ma",
40747         "212",
40748         0
40749       ],
40750       [
40751         "Mozambique (Moçambique)",
40752         "mz",
40753         "258"
40754       ],
40755       [
40756         "Myanmar (Burma) (မြန်မာ)",
40757         "mm",
40758         "95"
40759       ],
40760       [
40761         "Namibia (Namibië)",
40762         "na",
40763         "264"
40764       ],
40765       [
40766         "Nauru",
40767         "nr",
40768         "674"
40769       ],
40770       [
40771         "Nepal (नेपाल)",
40772         "np",
40773         "977"
40774       ],
40775       [
40776         "Netherlands (Nederland)",
40777         "nl",
40778         "31"
40779       ],
40780       [
40781         "New Caledonia (Nouvelle-Calédonie)",
40782         "nc",
40783         "687"
40784       ],
40785       [
40786         "New Zealand",
40787         "nz",
40788         "64"
40789       ],
40790       [
40791         "Nicaragua",
40792         "ni",
40793         "505"
40794       ],
40795       [
40796         "Niger (Nijar)",
40797         "ne",
40798         "227"
40799       ],
40800       [
40801         "Nigeria",
40802         "ng",
40803         "234"
40804       ],
40805       [
40806         "Niue",
40807         "nu",
40808         "683"
40809       ],
40810       [
40811         "Norfolk Island",
40812         "nf",
40813         "672"
40814       ],
40815       [
40816         "North Korea (조선 민주주의 인민 공화국)",
40817         "kp",
40818         "850"
40819       ],
40820       [
40821         "Northern Mariana Islands",
40822         "mp",
40823         "1670"
40824       ],
40825       [
40826         "Norway (Norge)",
40827         "no",
40828         "47",
40829         0
40830       ],
40831       [
40832         "Oman (‫عُمان‬‎)",
40833         "om",
40834         "968"
40835       ],
40836       [
40837         "Pakistan (‫پاکستان‬‎)",
40838         "pk",
40839         "92"
40840       ],
40841       [
40842         "Palau",
40843         "pw",
40844         "680"
40845       ],
40846       [
40847         "Palestine (‫فلسطين‬‎)",
40848         "ps",
40849         "970"
40850       ],
40851       [
40852         "Panama (Panamá)",
40853         "pa",
40854         "507"
40855       ],
40856       [
40857         "Papua New Guinea",
40858         "pg",
40859         "675"
40860       ],
40861       [
40862         "Paraguay",
40863         "py",
40864         "595"
40865       ],
40866       [
40867         "Peru (Perú)",
40868         "pe",
40869         "51"
40870       ],
40871       [
40872         "Philippines",
40873         "ph",
40874         "63"
40875       ],
40876       [
40877         "Poland (Polska)",
40878         "pl",
40879         "48"
40880       ],
40881       [
40882         "Portugal",
40883         "pt",
40884         "351"
40885       ],
40886       [
40887         "Puerto Rico",
40888         "pr",
40889         "1",
40890         3,
40891         ["787", "939"]
40892       ],
40893       [
40894         "Qatar (‫قطر‬‎)",
40895         "qa",
40896         "974"
40897       ],
40898       [
40899         "Réunion (La Réunion)",
40900         "re",
40901         "262",
40902         0
40903       ],
40904       [
40905         "Romania (România)",
40906         "ro",
40907         "40"
40908       ],
40909       [
40910         "Russia (Россия)",
40911         "ru",
40912         "7",
40913         0
40914       ],
40915       [
40916         "Rwanda",
40917         "rw",
40918         "250"
40919       ],
40920       [
40921         "Saint Barthélemy",
40922         "bl",
40923         "590",
40924         1
40925       ],
40926       [
40927         "Saint Helena",
40928         "sh",
40929         "290"
40930       ],
40931       [
40932         "Saint Kitts and Nevis",
40933         "kn",
40934         "1869"
40935       ],
40936       [
40937         "Saint Lucia",
40938         "lc",
40939         "1758"
40940       ],
40941       [
40942         "Saint Martin (Saint-Martin (partie française))",
40943         "mf",
40944         "590",
40945         2
40946       ],
40947       [
40948         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
40949         "pm",
40950         "508"
40951       ],
40952       [
40953         "Saint Vincent and the Grenadines",
40954         "vc",
40955         "1784"
40956       ],
40957       [
40958         "Samoa",
40959         "ws",
40960         "685"
40961       ],
40962       [
40963         "San Marino",
40964         "sm",
40965         "378"
40966       ],
40967       [
40968         "São Tomé and Príncipe (São Tomé e Príncipe)",
40969         "st",
40970         "239"
40971       ],
40972       [
40973         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
40974         "sa",
40975         "966"
40976       ],
40977       [
40978         "Senegal (Sénégal)",
40979         "sn",
40980         "221"
40981       ],
40982       [
40983         "Serbia (Србија)",
40984         "rs",
40985         "381"
40986       ],
40987       [
40988         "Seychelles",
40989         "sc",
40990         "248"
40991       ],
40992       [
40993         "Sierra Leone",
40994         "sl",
40995         "232"
40996       ],
40997       [
40998         "Singapore",
40999         "sg",
41000         "65"
41001       ],
41002       [
41003         "Sint Maarten",
41004         "sx",
41005         "1721"
41006       ],
41007       [
41008         "Slovakia (Slovensko)",
41009         "sk",
41010         "421"
41011       ],
41012       [
41013         "Slovenia (Slovenija)",
41014         "si",
41015         "386"
41016       ],
41017       [
41018         "Solomon Islands",
41019         "sb",
41020         "677"
41021       ],
41022       [
41023         "Somalia (Soomaaliya)",
41024         "so",
41025         "252"
41026       ],
41027       [
41028         "South Africa",
41029         "za",
41030         "27"
41031       ],
41032       [
41033         "South Korea (대한민국)",
41034         "kr",
41035         "82"
41036       ],
41037       [
41038         "South Sudan (‫جنوب السودان‬‎)",
41039         "ss",
41040         "211"
41041       ],
41042       [
41043         "Spain (España)",
41044         "es",
41045         "34"
41046       ],
41047       [
41048         "Sri Lanka (ශ්‍රී ලංකාව)",
41049         "lk",
41050         "94"
41051       ],
41052       [
41053         "Sudan (‫السودان‬‎)",
41054         "sd",
41055         "249"
41056       ],
41057       [
41058         "Suriname",
41059         "sr",
41060         "597"
41061       ],
41062       [
41063         "Svalbard and Jan Mayen",
41064         "sj",
41065         "47",
41066         1
41067       ],
41068       [
41069         "Swaziland",
41070         "sz",
41071         "268"
41072       ],
41073       [
41074         "Sweden (Sverige)",
41075         "se",
41076         "46"
41077       ],
41078       [
41079         "Switzerland (Schweiz)",
41080         "ch",
41081         "41"
41082       ],
41083       [
41084         "Syria (‫سوريا‬‎)",
41085         "sy",
41086         "963"
41087       ],
41088       [
41089         "Taiwan (台灣)",
41090         "tw",
41091         "886"
41092       ],
41093       [
41094         "Tajikistan",
41095         "tj",
41096         "992"
41097       ],
41098       [
41099         "Tanzania",
41100         "tz",
41101         "255"
41102       ],
41103       [
41104         "Thailand (ไทย)",
41105         "th",
41106         "66"
41107       ],
41108       [
41109         "Timor-Leste",
41110         "tl",
41111         "670"
41112       ],
41113       [
41114         "Togo",
41115         "tg",
41116         "228"
41117       ],
41118       [
41119         "Tokelau",
41120         "tk",
41121         "690"
41122       ],
41123       [
41124         "Tonga",
41125         "to",
41126         "676"
41127       ],
41128       [
41129         "Trinidad and Tobago",
41130         "tt",
41131         "1868"
41132       ],
41133       [
41134         "Tunisia (‫تونس‬‎)",
41135         "tn",
41136         "216"
41137       ],
41138       [
41139         "Turkey (Türkiye)",
41140         "tr",
41141         "90"
41142       ],
41143       [
41144         "Turkmenistan",
41145         "tm",
41146         "993"
41147       ],
41148       [
41149         "Turks and Caicos Islands",
41150         "tc",
41151         "1649"
41152       ],
41153       [
41154         "Tuvalu",
41155         "tv",
41156         "688"
41157       ],
41158       [
41159         "U.S. Virgin Islands",
41160         "vi",
41161         "1340"
41162       ],
41163       [
41164         "Uganda",
41165         "ug",
41166         "256"
41167       ],
41168       [
41169         "Ukraine (Україна)",
41170         "ua",
41171         "380"
41172       ],
41173       [
41174         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
41175         "ae",
41176         "971"
41177       ],
41178       [
41179         "United Kingdom",
41180         "gb",
41181         "44",
41182         0
41183       ],
41184       [
41185         "United States",
41186         "us",
41187         "1",
41188         0
41189       ],
41190       [
41191         "Uruguay",
41192         "uy",
41193         "598"
41194       ],
41195       [
41196         "Uzbekistan (Oʻzbekiston)",
41197         "uz",
41198         "998"
41199       ],
41200       [
41201         "Vanuatu",
41202         "vu",
41203         "678"
41204       ],
41205       [
41206         "Vatican City (Città del Vaticano)",
41207         "va",
41208         "39",
41209         1
41210       ],
41211       [
41212         "Venezuela",
41213         "ve",
41214         "58"
41215       ],
41216       [
41217         "Vietnam (Việt Nam)",
41218         "vn",
41219         "84"
41220       ],
41221       [
41222         "Wallis and Futuna (Wallis-et-Futuna)",
41223         "wf",
41224         "681"
41225       ],
41226       [
41227         "Western Sahara (‫الصحراء الغربية‬‎)",
41228         "eh",
41229         "212",
41230         1
41231       ],
41232       [
41233         "Yemen (‫اليمن‬‎)",
41234         "ye",
41235         "967"
41236       ],
41237       [
41238         "Zambia",
41239         "zm",
41240         "260"
41241       ],
41242       [
41243         "Zimbabwe",
41244         "zw",
41245         "263"
41246       ],
41247       [
41248         "Åland Islands",
41249         "ax",
41250         "358",
41251         1
41252       ]
41253   ];
41254   
41255   return d;
41256 }/**
41257 *    This script refer to:
41258 *    Title: International Telephone Input
41259 *    Author: Jack O'Connor
41260 *    Code version:  v12.1.12
41261 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41262 **/
41263
41264 /**
41265  * @class Roo.bootstrap.PhoneInput
41266  * @extends Roo.bootstrap.TriggerField
41267  * An input with International dial-code selection
41268  
41269  * @cfg {String} defaultDialCode default '+852'
41270  * @cfg {Array} preferedCountries default []
41271   
41272  * @constructor
41273  * Create a new PhoneInput.
41274  * @param {Object} config Configuration options
41275  */
41276
41277 Roo.bootstrap.PhoneInput = function(config) {
41278     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
41279 };
41280
41281 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
41282         
41283         listWidth: undefined,
41284         
41285         selectedClass: 'active',
41286         
41287         invalidClass : "has-warning",
41288         
41289         validClass: 'has-success',
41290         
41291         allowed: '0123456789',
41292         
41293         max_length: 15,
41294         
41295         /**
41296          * @cfg {String} defaultDialCode The default dial code when initializing the input
41297          */
41298         defaultDialCode: '+852',
41299         
41300         /**
41301          * @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
41302          */
41303         preferedCountries: false,
41304         
41305         getAutoCreate : function()
41306         {
41307             var data = Roo.bootstrap.PhoneInputData();
41308             var align = this.labelAlign || this.parentLabelAlign();
41309             var id = Roo.id();
41310             
41311             this.allCountries = [];
41312             this.dialCodeMapping = [];
41313             
41314             for (var i = 0; i < data.length; i++) {
41315               var c = data[i];
41316               this.allCountries[i] = {
41317                 name: c[0],
41318                 iso2: c[1],
41319                 dialCode: c[2],
41320                 priority: c[3] || 0,
41321                 areaCodes: c[4] || null
41322               };
41323               this.dialCodeMapping[c[2]] = {
41324                   name: c[0],
41325                   iso2: c[1],
41326                   priority: c[3] || 0,
41327                   areaCodes: c[4] || null
41328               };
41329             }
41330             
41331             var cfg = {
41332                 cls: 'form-group',
41333                 cn: []
41334             };
41335             
41336             var input =  {
41337                 tag: 'input',
41338                 id : id,
41339                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
41340                 maxlength: this.max_length,
41341                 cls : 'form-control tel-input',
41342                 autocomplete: 'new-password'
41343             };
41344             
41345             var hiddenInput = {
41346                 tag: 'input',
41347                 type: 'hidden',
41348                 cls: 'hidden-tel-input'
41349             };
41350             
41351             if (this.name) {
41352                 hiddenInput.name = this.name;
41353             }
41354             
41355             if (this.disabled) {
41356                 input.disabled = true;
41357             }
41358             
41359             var flag_container = {
41360                 tag: 'div',
41361                 cls: 'flag-box',
41362                 cn: [
41363                     {
41364                         tag: 'div',
41365                         cls: 'flag'
41366                     },
41367                     {
41368                         tag: 'div',
41369                         cls: 'caret'
41370                     }
41371                 ]
41372             };
41373             
41374             var box = {
41375                 tag: 'div',
41376                 cls: this.hasFeedback ? 'has-feedback' : '',
41377                 cn: [
41378                     hiddenInput,
41379                     input,
41380                     {
41381                         tag: 'input',
41382                         cls: 'dial-code-holder',
41383                         disabled: true
41384                     }
41385                 ]
41386             };
41387             
41388             var container = {
41389                 cls: 'roo-select2-container input-group',
41390                 cn: [
41391                     flag_container,
41392                     box
41393                 ]
41394             };
41395             
41396             if (this.fieldLabel.length) {
41397                 var indicator = {
41398                     tag: 'i',
41399                     tooltip: 'This field is required'
41400                 };
41401                 
41402                 var label = {
41403                     tag: 'label',
41404                     'for':  id,
41405                     cls: 'control-label',
41406                     cn: []
41407                 };
41408                 
41409                 var label_text = {
41410                     tag: 'span',
41411                     html: this.fieldLabel
41412                 };
41413                 
41414                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
41415                 label.cn = [
41416                     indicator,
41417                     label_text
41418                 ];
41419                 
41420                 if(this.indicatorpos == 'right') {
41421                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
41422                     label.cn = [
41423                         label_text,
41424                         indicator
41425                     ];
41426                 }
41427                 
41428                 if(align == 'left') {
41429                     container = {
41430                         tag: 'div',
41431                         cn: [
41432                             container
41433                         ]
41434                     };
41435                     
41436                     if(this.labelWidth > 12){
41437                         label.style = "width: " + this.labelWidth + 'px';
41438                     }
41439                     if(this.labelWidth < 13 && this.labelmd == 0){
41440                         this.labelmd = this.labelWidth;
41441                     }
41442                     if(this.labellg > 0){
41443                         label.cls += ' col-lg-' + this.labellg;
41444                         input.cls += ' col-lg-' + (12 - this.labellg);
41445                     }
41446                     if(this.labelmd > 0){
41447                         label.cls += ' col-md-' + this.labelmd;
41448                         container.cls += ' col-md-' + (12 - this.labelmd);
41449                     }
41450                     if(this.labelsm > 0){
41451                         label.cls += ' col-sm-' + this.labelsm;
41452                         container.cls += ' col-sm-' + (12 - this.labelsm);
41453                     }
41454                     if(this.labelxs > 0){
41455                         label.cls += ' col-xs-' + this.labelxs;
41456                         container.cls += ' col-xs-' + (12 - this.labelxs);
41457                     }
41458                 }
41459             }
41460             
41461             cfg.cn = [
41462                 label,
41463                 container
41464             ];
41465             
41466             var settings = this;
41467             
41468             ['xs','sm','md','lg'].map(function(size){
41469                 if (settings[size]) {
41470                     cfg.cls += ' col-' + size + '-' + settings[size];
41471                 }
41472             });
41473             
41474             this.store = new Roo.data.Store({
41475                 proxy : new Roo.data.MemoryProxy({}),
41476                 reader : new Roo.data.JsonReader({
41477                     fields : [
41478                         {
41479                             'name' : 'name',
41480                             'type' : 'string'
41481                         },
41482                         {
41483                             'name' : 'iso2',
41484                             'type' : 'string'
41485                         },
41486                         {
41487                             'name' : 'dialCode',
41488                             'type' : 'string'
41489                         },
41490                         {
41491                             'name' : 'priority',
41492                             'type' : 'string'
41493                         },
41494                         {
41495                             'name' : 'areaCodes',
41496                             'type' : 'string'
41497                         }
41498                     ]
41499                 })
41500             });
41501             
41502             if(!this.preferedCountries) {
41503                 this.preferedCountries = [
41504                     'hk',
41505                     'gb',
41506                     'us'
41507                 ];
41508             }
41509             
41510             var p = this.preferedCountries.reverse();
41511             
41512             if(p) {
41513                 for (var i = 0; i < p.length; i++) {
41514                     for (var j = 0; j < this.allCountries.length; j++) {
41515                         if(this.allCountries[j].iso2 == p[i]) {
41516                             var t = this.allCountries[j];
41517                             this.allCountries.splice(j,1);
41518                             this.allCountries.unshift(t);
41519                         }
41520                     } 
41521                 }
41522             }
41523             
41524             this.store.proxy.data = {
41525                 success: true,
41526                 data: this.allCountries
41527             };
41528             
41529             return cfg;
41530         },
41531         
41532         initEvents : function()
41533         {
41534             this.createList();
41535             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
41536             
41537             this.indicator = this.indicatorEl();
41538             this.flag = this.flagEl();
41539             this.dialCodeHolder = this.dialCodeHolderEl();
41540             
41541             this.trigger = this.el.select('div.flag-box',true).first();
41542             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
41543             
41544             var _this = this;
41545             
41546             (function(){
41547                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
41548                 _this.list.setWidth(lw);
41549             }).defer(100);
41550             
41551             this.list.on('mouseover', this.onViewOver, this);
41552             this.list.on('mousemove', this.onViewMove, this);
41553             this.inputEl().on("keyup", this.onKeyUp, this);
41554             this.inputEl().on("keypress", this.onKeyPress, this);
41555             
41556             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
41557
41558             this.view = new Roo.View(this.list, this.tpl, {
41559                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
41560             });
41561             
41562             this.view.on('click', this.onViewClick, this);
41563             this.setValue(this.defaultDialCode);
41564         },
41565         
41566         onTriggerClick : function(e)
41567         {
41568             Roo.log('trigger click');
41569             if(this.disabled){
41570                 return;
41571             }
41572             
41573             if(this.isExpanded()){
41574                 this.collapse();
41575                 this.hasFocus = false;
41576             }else {
41577                 this.store.load({});
41578                 this.hasFocus = true;
41579                 this.expand();
41580             }
41581         },
41582         
41583         isExpanded : function()
41584         {
41585             return this.list.isVisible();
41586         },
41587         
41588         collapse : function()
41589         {
41590             if(!this.isExpanded()){
41591                 return;
41592             }
41593             this.list.hide();
41594             Roo.get(document).un('mousedown', this.collapseIf, this);
41595             Roo.get(document).un('mousewheel', this.collapseIf, this);
41596             this.fireEvent('collapse', this);
41597             this.validate();
41598         },
41599         
41600         expand : function()
41601         {
41602             Roo.log('expand');
41603
41604             if(this.isExpanded() || !this.hasFocus){
41605                 return;
41606             }
41607             
41608             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
41609             this.list.setWidth(lw);
41610             
41611             this.list.show();
41612             this.restrictHeight();
41613             
41614             Roo.get(document).on('mousedown', this.collapseIf, this);
41615             Roo.get(document).on('mousewheel', this.collapseIf, this);
41616             
41617             this.fireEvent('expand', this);
41618         },
41619         
41620         restrictHeight : function()
41621         {
41622             this.list.alignTo(this.inputEl(), this.listAlign);
41623             this.list.alignTo(this.inputEl(), this.listAlign);
41624         },
41625         
41626         onViewOver : function(e, t)
41627         {
41628             if(this.inKeyMode){
41629                 return;
41630             }
41631             var item = this.view.findItemFromChild(t);
41632             
41633             if(item){
41634                 var index = this.view.indexOf(item);
41635                 this.select(index, false);
41636             }
41637         },
41638
41639         // private
41640         onViewClick : function(view, doFocus, el, e)
41641         {
41642             var index = this.view.getSelectedIndexes()[0];
41643             
41644             var r = this.store.getAt(index);
41645             
41646             if(r){
41647                 this.onSelect(r, index);
41648             }
41649             if(doFocus !== false && !this.blockFocus){
41650                 this.inputEl().focus();
41651             }
41652         },
41653         
41654         onViewMove : function(e, t)
41655         {
41656             this.inKeyMode = false;
41657         },
41658         
41659         select : function(index, scrollIntoView)
41660         {
41661             this.selectedIndex = index;
41662             this.view.select(index);
41663             if(scrollIntoView !== false){
41664                 var el = this.view.getNode(index);
41665                 if(el){
41666                     this.list.scrollChildIntoView(el, false);
41667                 }
41668             }
41669         },
41670         
41671         createList : function()
41672         {
41673             this.list = Roo.get(document.body).createChild({
41674                 tag: 'ul',
41675                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
41676                 style: 'display:none'
41677             });
41678             
41679             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
41680         },
41681         
41682         collapseIf : function(e)
41683         {
41684             var in_combo  = e.within(this.el);
41685             var in_list =  e.within(this.list);
41686             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
41687             
41688             if (in_combo || in_list || is_list) {
41689                 return;
41690             }
41691             this.collapse();
41692         },
41693         
41694         onSelect : function(record, index)
41695         {
41696             if(this.fireEvent('beforeselect', this, record, index) !== false){
41697                 
41698                 this.setFlagClass(record.data.iso2);
41699                 this.setDialCode(record.data.dialCode);
41700                 this.hasFocus = false;
41701                 this.collapse();
41702                 this.fireEvent('select', this, record, index);
41703             }
41704         },
41705         
41706         flagEl : function()
41707         {
41708             var flag = this.el.select('div.flag',true).first();
41709             if(!flag){
41710                 return false;
41711             }
41712             return flag;
41713         },
41714         
41715         dialCodeHolderEl : function()
41716         {
41717             var d = this.el.select('input.dial-code-holder',true).first();
41718             if(!d){
41719                 return false;
41720             }
41721             return d;
41722         },
41723         
41724         setDialCode : function(v)
41725         {
41726             this.dialCodeHolder.dom.value = '+'+v;
41727         },
41728         
41729         setFlagClass : function(n)
41730         {
41731             this.flag.dom.className = 'flag '+n;
41732         },
41733         
41734         getValue : function()
41735         {
41736             var v = this.inputEl().getValue();
41737             if(this.dialCodeHolder) {
41738                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
41739             }
41740             return v;
41741         },
41742         
41743         setValue : function(v)
41744         {
41745             var d = this.getDialCode(v);
41746             
41747             //invalid dial code
41748             if(v.length == 0 || !d || d.length == 0) {
41749                 if(this.rendered){
41750                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
41751                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41752                 }
41753                 return;
41754             }
41755             
41756             //valid dial code
41757             this.setFlagClass(this.dialCodeMapping[d].iso2);
41758             this.setDialCode(d);
41759             this.inputEl().dom.value = v.replace('+'+d,'');
41760             this.hiddenEl().dom.value = this.getValue();
41761             
41762             this.validate();
41763         },
41764         
41765         getDialCode : function(v)
41766         {
41767             v = v ||  '';
41768             
41769             if (v.length == 0) {
41770                 return this.dialCodeHolder.dom.value;
41771             }
41772             
41773             var dialCode = "";
41774             if (v.charAt(0) != "+") {
41775                 return false;
41776             }
41777             var numericChars = "";
41778             for (var i = 1; i < v.length; i++) {
41779               var c = v.charAt(i);
41780               if (!isNaN(c)) {
41781                 numericChars += c;
41782                 if (this.dialCodeMapping[numericChars]) {
41783                   dialCode = v.substr(1, i);
41784                 }
41785                 if (numericChars.length == 4) {
41786                   break;
41787                 }
41788               }
41789             }
41790             return dialCode;
41791         },
41792         
41793         reset : function()
41794         {
41795             this.setValue(this.defaultDialCode);
41796             this.validate();
41797         },
41798         
41799         hiddenEl : function()
41800         {
41801             return this.el.select('input.hidden-tel-input',true).first();
41802         },
41803         
41804         // after setting val
41805         onKeyUp : function(e){
41806             this.setValue(this.getValue());
41807         },
41808         
41809         onKeyPress : function(e){
41810             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
41811                 e.stopEvent();
41812             }
41813         }
41814         
41815 });
41816 /**
41817  * @class Roo.bootstrap.MoneyField
41818  * @extends Roo.bootstrap.ComboBox
41819  * Bootstrap MoneyField class
41820  * 
41821  * @constructor
41822  * Create a new MoneyField.
41823  * @param {Object} config Configuration options
41824  */
41825
41826 Roo.bootstrap.MoneyField = function(config) {
41827     
41828     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
41829     
41830 };
41831
41832 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
41833     
41834     /**
41835      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
41836      */
41837     allowDecimals : true,
41838     /**
41839      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
41840      */
41841     decimalSeparator : ".",
41842     /**
41843      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
41844      */
41845     decimalPrecision : 0,
41846     /**
41847      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
41848      */
41849     allowNegative : true,
41850     /**
41851      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
41852      */
41853     allowZero: true,
41854     /**
41855      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
41856      */
41857     minValue : Number.NEGATIVE_INFINITY,
41858     /**
41859      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
41860      */
41861     maxValue : Number.MAX_VALUE,
41862     /**
41863      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
41864      */
41865     minText : "The minimum value for this field is {0}",
41866     /**
41867      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
41868      */
41869     maxText : "The maximum value for this field is {0}",
41870     /**
41871      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
41872      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
41873      */
41874     nanText : "{0} is not a valid number",
41875     /**
41876      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
41877      */
41878     castInt : true,
41879     /**
41880      * @cfg {String} defaults currency of the MoneyField
41881      * value should be in lkey
41882      */
41883     defaultCurrency : false,
41884     /**
41885      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
41886      */
41887     thousandsDelimiter : false,
41888     /**
41889      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
41890      */
41891     max_length: false,
41892     
41893     inputlg : 9,
41894     inputmd : 9,
41895     inputsm : 9,
41896     inputxs : 6,
41897     
41898     store : false,
41899     
41900     getAutoCreate : function()
41901     {
41902         var align = this.labelAlign || this.parentLabelAlign();
41903         
41904         var id = Roo.id();
41905
41906         var cfg = {
41907             cls: 'form-group',
41908             cn: []
41909         };
41910
41911         var input =  {
41912             tag: 'input',
41913             id : id,
41914             cls : 'form-control roo-money-amount-input',
41915             autocomplete: 'new-password'
41916         };
41917         
41918         var hiddenInput = {
41919             tag: 'input',
41920             type: 'hidden',
41921             id: Roo.id(),
41922             cls: 'hidden-number-input'
41923         };
41924         
41925         if(this.max_length) {
41926             input.maxlength = this.max_length; 
41927         }
41928         
41929         if (this.name) {
41930             hiddenInput.name = this.name;
41931         }
41932
41933         if (this.disabled) {
41934             input.disabled = true;
41935         }
41936
41937         var clg = 12 - this.inputlg;
41938         var cmd = 12 - this.inputmd;
41939         var csm = 12 - this.inputsm;
41940         var cxs = 12 - this.inputxs;
41941         
41942         var container = {
41943             tag : 'div',
41944             cls : 'row roo-money-field',
41945             cn : [
41946                 {
41947                     tag : 'div',
41948                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
41949                     cn : [
41950                         {
41951                             tag : 'div',
41952                             cls: 'roo-select2-container input-group',
41953                             cn: [
41954                                 {
41955                                     tag : 'input',
41956                                     cls : 'form-control roo-money-currency-input',
41957                                     autocomplete: 'new-password',
41958                                     readOnly : 1,
41959                                     name : this.currencyName
41960                                 },
41961                                 {
41962                                     tag :'span',
41963                                     cls : 'input-group-addon',
41964                                     cn : [
41965                                         {
41966                                             tag: 'span',
41967                                             cls: 'caret'
41968                                         }
41969                                     ]
41970                                 }
41971                             ]
41972                         }
41973                     ]
41974                 },
41975                 {
41976                     tag : 'div',
41977                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
41978                     cn : [
41979                         {
41980                             tag: 'div',
41981                             cls: this.hasFeedback ? 'has-feedback' : '',
41982                             cn: [
41983                                 input
41984                             ]
41985                         }
41986                     ]
41987                 }
41988             ]
41989             
41990         };
41991         
41992         if (this.fieldLabel.length) {
41993             var indicator = {
41994                 tag: 'i',
41995                 tooltip: 'This field is required'
41996             };
41997
41998             var label = {
41999                 tag: 'label',
42000                 'for':  id,
42001                 cls: 'control-label',
42002                 cn: []
42003             };
42004
42005             var label_text = {
42006                 tag: 'span',
42007                 html: this.fieldLabel
42008             };
42009
42010             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42011             label.cn = [
42012                 indicator,
42013                 label_text
42014             ];
42015
42016             if(this.indicatorpos == 'right') {
42017                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42018                 label.cn = [
42019                     label_text,
42020                     indicator
42021                 ];
42022             }
42023
42024             if(align == 'left') {
42025                 container = {
42026                     tag: 'div',
42027                     cn: [
42028                         container
42029                     ]
42030                 };
42031
42032                 if(this.labelWidth > 12){
42033                     label.style = "width: " + this.labelWidth + 'px';
42034                 }
42035                 if(this.labelWidth < 13 && this.labelmd == 0){
42036                     this.labelmd = this.labelWidth;
42037                 }
42038                 if(this.labellg > 0){
42039                     label.cls += ' col-lg-' + this.labellg;
42040                     input.cls += ' col-lg-' + (12 - this.labellg);
42041                 }
42042                 if(this.labelmd > 0){
42043                     label.cls += ' col-md-' + this.labelmd;
42044                     container.cls += ' col-md-' + (12 - this.labelmd);
42045                 }
42046                 if(this.labelsm > 0){
42047                     label.cls += ' col-sm-' + this.labelsm;
42048                     container.cls += ' col-sm-' + (12 - this.labelsm);
42049                 }
42050                 if(this.labelxs > 0){
42051                     label.cls += ' col-xs-' + this.labelxs;
42052                     container.cls += ' col-xs-' + (12 - this.labelxs);
42053                 }
42054             }
42055         }
42056
42057         cfg.cn = [
42058             label,
42059             container,
42060             hiddenInput
42061         ];
42062         
42063         var settings = this;
42064
42065         ['xs','sm','md','lg'].map(function(size){
42066             if (settings[size]) {
42067                 cfg.cls += ' col-' + size + '-' + settings[size];
42068             }
42069         });
42070         
42071         return cfg;
42072     },
42073     
42074     initEvents : function()
42075     {
42076         this.indicator = this.indicatorEl();
42077         
42078         this.initCurrencyEvent();
42079         
42080         this.initNumberEvent();
42081     },
42082     
42083     initCurrencyEvent : function()
42084     {
42085         if (!this.store) {
42086             throw "can not find store for combo";
42087         }
42088         
42089         this.store = Roo.factory(this.store, Roo.data);
42090         this.store.parent = this;
42091         
42092         this.createList();
42093         
42094         this.triggerEl = this.el.select('.input-group-addon', true).first();
42095         
42096         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
42097         
42098         var _this = this;
42099         
42100         (function(){
42101             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42102             _this.list.setWidth(lw);
42103         }).defer(100);
42104         
42105         this.list.on('mouseover', this.onViewOver, this);
42106         this.list.on('mousemove', this.onViewMove, this);
42107         this.list.on('scroll', this.onViewScroll, this);
42108         
42109         if(!this.tpl){
42110             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
42111         }
42112         
42113         this.view = new Roo.View(this.list, this.tpl, {
42114             singleSelect:true, store: this.store, selectedClass: this.selectedClass
42115         });
42116         
42117         this.view.on('click', this.onViewClick, this);
42118         
42119         this.store.on('beforeload', this.onBeforeLoad, this);
42120         this.store.on('load', this.onLoad, this);
42121         this.store.on('loadexception', this.onLoadException, this);
42122         
42123         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
42124             "up" : function(e){
42125                 this.inKeyMode = true;
42126                 this.selectPrev();
42127             },
42128
42129             "down" : function(e){
42130                 if(!this.isExpanded()){
42131                     this.onTriggerClick();
42132                 }else{
42133                     this.inKeyMode = true;
42134                     this.selectNext();
42135                 }
42136             },
42137
42138             "enter" : function(e){
42139                 this.collapse();
42140                 
42141                 if(this.fireEvent("specialkey", this, e)){
42142                     this.onViewClick(false);
42143                 }
42144                 
42145                 return true;
42146             },
42147
42148             "esc" : function(e){
42149                 this.collapse();
42150             },
42151
42152             "tab" : function(e){
42153                 this.collapse();
42154                 
42155                 if(this.fireEvent("specialkey", this, e)){
42156                     this.onViewClick(false);
42157                 }
42158                 
42159                 return true;
42160             },
42161
42162             scope : this,
42163
42164             doRelay : function(foo, bar, hname){
42165                 if(hname == 'down' || this.scope.isExpanded()){
42166                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
42167                 }
42168                 return true;
42169             },
42170
42171             forceKeyDown: true
42172         });
42173         
42174         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
42175         
42176     },
42177     
42178     initNumberEvent : function(e)
42179     {
42180         this.inputEl().on("keydown" , this.fireKey,  this);
42181         this.inputEl().on("focus", this.onFocus,  this);
42182         this.inputEl().on("blur", this.onBlur,  this);
42183         
42184         this.inputEl().relayEvent('keyup', this);
42185         
42186         if(this.indicator){
42187             this.indicator.addClass('invisible');
42188         }
42189  
42190         this.originalValue = this.getValue();
42191         
42192         if(this.validationEvent == 'keyup'){
42193             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
42194             this.inputEl().on('keyup', this.filterValidation, this);
42195         }
42196         else if(this.validationEvent !== false){
42197             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
42198         }
42199         
42200         if(this.selectOnFocus){
42201             this.on("focus", this.preFocus, this);
42202             
42203         }
42204         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
42205             this.inputEl().on("keypress", this.filterKeys, this);
42206         } else {
42207             this.inputEl().relayEvent('keypress', this);
42208         }
42209         
42210         var allowed = "0123456789";
42211         
42212         if(this.allowDecimals){
42213             allowed += this.decimalSeparator;
42214         }
42215         
42216         if(this.allowNegative){
42217             allowed += "-";
42218         }
42219         
42220         if(this.thousandsDelimiter) {
42221             allowed += ",";
42222         }
42223         
42224         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
42225         
42226         var keyPress = function(e){
42227             
42228             var k = e.getKey();
42229             
42230             var c = e.getCharCode();
42231             
42232             if(
42233                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
42234                     allowed.indexOf(String.fromCharCode(c)) === -1
42235             ){
42236                 e.stopEvent();
42237                 return;
42238             }
42239             
42240             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
42241                 return;
42242             }
42243             
42244             if(allowed.indexOf(String.fromCharCode(c)) === -1){
42245                 e.stopEvent();
42246             }
42247         };
42248         
42249         this.inputEl().on("keypress", keyPress, this);
42250         
42251     },
42252     
42253     onTriggerClick : function(e)
42254     {   
42255         if(this.disabled){
42256             return;
42257         }
42258         
42259         this.page = 0;
42260         this.loadNext = false;
42261         
42262         if(this.isExpanded()){
42263             this.collapse();
42264             return;
42265         }
42266         
42267         this.hasFocus = true;
42268         
42269         if(this.triggerAction == 'all') {
42270             this.doQuery(this.allQuery, true);
42271             return;
42272         }
42273         
42274         this.doQuery(this.getRawValue());
42275     },
42276     
42277     getCurrency : function()
42278     {   
42279         var v = this.currencyEl().getValue();
42280         
42281         return v;
42282     },
42283     
42284     restrictHeight : function()
42285     {
42286         this.list.alignTo(this.currencyEl(), this.listAlign);
42287         this.list.alignTo(this.currencyEl(), this.listAlign);
42288     },
42289     
42290     onViewClick : function(view, doFocus, el, e)
42291     {
42292         var index = this.view.getSelectedIndexes()[0];
42293         
42294         var r = this.store.getAt(index);
42295         
42296         if(r){
42297             this.onSelect(r, index);
42298         }
42299     },
42300     
42301     onSelect : function(record, index){
42302         
42303         if(this.fireEvent('beforeselect', this, record, index) !== false){
42304         
42305             this.setFromCurrencyData(index > -1 ? record.data : false);
42306             
42307             this.collapse();
42308             
42309             this.fireEvent('select', this, record, index);
42310         }
42311     },
42312     
42313     setFromCurrencyData : function(o)
42314     {
42315         var currency = '';
42316         
42317         this.lastCurrency = o;
42318         
42319         if (this.currencyField) {
42320             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
42321         } else {
42322             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
42323         }
42324         
42325         this.lastSelectionText = currency;
42326         
42327         //setting default currency
42328         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
42329             this.setCurrency(this.defaultCurrency);
42330             return;
42331         }
42332         
42333         this.setCurrency(currency);
42334     },
42335     
42336     setFromData : function(o)
42337     {
42338         var c = {};
42339         
42340         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
42341         
42342         this.setFromCurrencyData(c);
42343         
42344         var value = '';
42345         
42346         if (this.name) {
42347             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
42348         } else {
42349             Roo.log('no value set for '+ (this.name ? this.name : this.id));
42350         }
42351         
42352         this.setValue(value);
42353         
42354     },
42355     
42356     setCurrency : function(v)
42357     {   
42358         this.currencyValue = v;
42359         
42360         if(this.rendered){
42361             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
42362             this.validate();
42363         }
42364     },
42365     
42366     setValue : function(v)
42367     {
42368         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
42369         
42370         this.value = v;
42371         
42372         if(this.rendered){
42373             
42374             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
42375             
42376             this.inputEl().dom.value = (v == '') ? '' :
42377                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
42378             
42379             if(!this.allowZero && v === '0') {
42380                 this.hiddenEl().dom.value = '';
42381                 this.inputEl().dom.value = '';
42382             }
42383             
42384             this.validate();
42385         }
42386     },
42387     
42388     getRawValue : function()
42389     {
42390         var v = this.inputEl().getValue();
42391         
42392         return v;
42393     },
42394     
42395     getValue : function()
42396     {
42397         return this.fixPrecision(this.parseValue(this.getRawValue()));
42398     },
42399     
42400     parseValue : function(value)
42401     {
42402         if(this.thousandsDelimiter) {
42403             value += "";
42404             r = new RegExp(",", "g");
42405             value = value.replace(r, "");
42406         }
42407         
42408         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
42409         return isNaN(value) ? '' : value;
42410         
42411     },
42412     
42413     fixPrecision : function(value)
42414     {
42415         if(this.thousandsDelimiter) {
42416             value += "";
42417             r = new RegExp(",", "g");
42418             value = value.replace(r, "");
42419         }
42420         
42421         var nan = isNaN(value);
42422         
42423         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
42424             return nan ? '' : value;
42425         }
42426         return parseFloat(value).toFixed(this.decimalPrecision);
42427     },
42428     
42429     decimalPrecisionFcn : function(v)
42430     {
42431         return Math.floor(v);
42432     },
42433     
42434     validateValue : function(value)
42435     {
42436         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
42437             return false;
42438         }
42439         
42440         var num = this.parseValue(value);
42441         
42442         if(isNaN(num)){
42443             this.markInvalid(String.format(this.nanText, value));
42444             return false;
42445         }
42446         
42447         if(num < this.minValue){
42448             this.markInvalid(String.format(this.minText, this.minValue));
42449             return false;
42450         }
42451         
42452         if(num > this.maxValue){
42453             this.markInvalid(String.format(this.maxText, this.maxValue));
42454             return false;
42455         }
42456         
42457         return true;
42458     },
42459     
42460     validate : function()
42461     {
42462         if(this.disabled || this.allowBlank){
42463             this.markValid();
42464             return true;
42465         }
42466         
42467         var currency = this.getCurrency();
42468         
42469         if(this.validateValue(this.getRawValue()) && currency.length){
42470             this.markValid();
42471             return true;
42472         }
42473         
42474         this.markInvalid();
42475         return false;
42476     },
42477     
42478     getName: function()
42479     {
42480         return this.name;
42481     },
42482     
42483     beforeBlur : function()
42484     {
42485         if(!this.castInt){
42486             return;
42487         }
42488         
42489         var v = this.parseValue(this.getRawValue());
42490         
42491         if(v || v == 0){
42492             this.setValue(v);
42493         }
42494     },
42495     
42496     onBlur : function()
42497     {
42498         this.beforeBlur();
42499         
42500         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
42501             //this.el.removeClass(this.focusClass);
42502         }
42503         
42504         this.hasFocus = false;
42505         
42506         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
42507             this.validate();
42508         }
42509         
42510         var v = this.getValue();
42511         
42512         if(String(v) !== String(this.startValue)){
42513             this.fireEvent('change', this, v, this.startValue);
42514         }
42515         
42516         this.fireEvent("blur", this);
42517     },
42518     
42519     inputEl : function()
42520     {
42521         return this.el.select('.roo-money-amount-input', true).first();
42522     },
42523     
42524     currencyEl : function()
42525     {
42526         return this.el.select('.roo-money-currency-input', true).first();
42527     },
42528     
42529     hiddenEl : function()
42530     {
42531         return this.el.select('input.hidden-number-input',true).first();
42532     }
42533     
42534 });/**
42535  * @class Roo.bootstrap.BezierSignature
42536  * @extends Roo.bootstrap.Component
42537  * Bootstrap BezierSignature class
42538  * This script refer to:
42539  *    Title: Signature Pad
42540  *    Author: szimek
42541  *    Availability: https://github.com/szimek/signature_pad
42542  *
42543  * @constructor
42544  * Create a new BezierSignature
42545  * @param {Object} config The config object
42546  */
42547
42548 Roo.bootstrap.BezierSignature = function(config){
42549     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
42550     this.addEvents({
42551         "resize" : true
42552     });
42553 };
42554
42555 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
42556 {
42557      
42558     curve_data: [],
42559     
42560     is_empty: true,
42561     
42562     mouse_btn_down: true,
42563     
42564     /**
42565      * @cfg {int} canvas height
42566      */
42567     canvas_height: '200px',
42568     
42569     /**
42570      * @cfg {float|function} Radius of a single dot.
42571      */ 
42572     dot_size: false,
42573     
42574     /**
42575      * @cfg {float} Minimum width of a line. Defaults to 0.5.
42576      */
42577     min_width: 0.5,
42578     
42579     /**
42580      * @cfg {float} Maximum width of a line. Defaults to 2.5.
42581      */
42582     max_width: 2.5,
42583     
42584     /**
42585      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
42586      */
42587     throttle: 16,
42588     
42589     /**
42590      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
42591      */
42592     min_distance: 5,
42593     
42594     /**
42595      * @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.
42596      */
42597     bg_color: 'rgba(0, 0, 0, 0)',
42598     
42599     /**
42600      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
42601      */
42602     dot_color: 'black',
42603     
42604     /**
42605      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
42606      */ 
42607     velocity_filter_weight: 0.7,
42608     
42609     /**
42610      * @cfg {function} Callback when stroke begin. 
42611      */
42612     onBegin: false,
42613     
42614     /**
42615      * @cfg {function} Callback when stroke end.
42616      */
42617     onEnd: false,
42618     
42619     getAutoCreate : function()
42620     {
42621         var cls = 'roo-signature column';
42622         
42623         if(this.cls){
42624             cls += ' ' + this.cls;
42625         }
42626         
42627         var col_sizes = [
42628             'lg',
42629             'md',
42630             'sm',
42631             'xs'
42632         ];
42633         
42634         for(var i = 0; i < col_sizes.length; i++) {
42635             if(this[col_sizes[i]]) {
42636                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
42637             }
42638         }
42639         
42640         var cfg = {
42641             tag: 'div',
42642             cls: cls,
42643             cn: [
42644                 {
42645                     tag: 'div',
42646                     cls: 'roo-signature-body',
42647                     cn: [
42648                         {
42649                             tag: 'canvas',
42650                             cls: 'roo-signature-body-canvas',
42651                             height: this.canvas_height,
42652                             width: this.canvas_width
42653                         }
42654                     ]
42655                 },
42656                 {
42657                     tag: 'input',
42658                     type: 'file',
42659                     style: 'display: none'
42660                 }
42661             ]
42662         };
42663         
42664         return cfg;
42665     },
42666     
42667     initEvents: function() 
42668     {
42669         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
42670         
42671         var canvas = this.canvasEl();
42672         
42673         // mouse && touch event swapping...
42674         canvas.dom.style.touchAction = 'none';
42675         canvas.dom.style.msTouchAction = 'none';
42676         
42677         this.mouse_btn_down = false;
42678         canvas.on('mousedown', this._handleMouseDown, this);
42679         canvas.on('mousemove', this._handleMouseMove, this);
42680         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
42681         
42682         if (window.PointerEvent) {
42683             canvas.on('pointerdown', this._handleMouseDown, this);
42684             canvas.on('pointermove', this._handleMouseMove, this);
42685             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
42686         }
42687         
42688         if ('ontouchstart' in window) {
42689             canvas.on('touchstart', this._handleTouchStart, this);
42690             canvas.on('touchmove', this._handleTouchMove, this);
42691             canvas.on('touchend', this._handleTouchEnd, this);
42692         }
42693         
42694         Roo.EventManager.onWindowResize(this.resize, this, true);
42695         
42696         // file input event
42697         this.fileEl().on('change', this.uploadImage, this);
42698         
42699         this.clear();
42700         
42701         this.resize();
42702     },
42703     
42704     resize: function(){
42705         
42706         var canvas = this.canvasEl().dom;
42707         var ctx = this.canvasElCtx();
42708         var img_data = false;
42709         
42710         if(canvas.width > 0) {
42711             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
42712         }
42713         // setting canvas width will clean img data
42714         canvas.width = 0;
42715         
42716         var style = window.getComputedStyle ? 
42717             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
42718             
42719         var padding_left = parseInt(style.paddingLeft) || 0;
42720         var padding_right = parseInt(style.paddingRight) || 0;
42721         
42722         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
42723         
42724         if(img_data) {
42725             ctx.putImageData(img_data, 0, 0);
42726         }
42727     },
42728     
42729     _handleMouseDown: function(e)
42730     {
42731         if (e.browserEvent.which === 1) {
42732             this.mouse_btn_down = true;
42733             this.strokeBegin(e);
42734         }
42735     },
42736     
42737     _handleMouseMove: function (e)
42738     {
42739         if (this.mouse_btn_down) {
42740             this.strokeMoveUpdate(e);
42741         }
42742     },
42743     
42744     _handleMouseUp: function (e)
42745     {
42746         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
42747             this.mouse_btn_down = false;
42748             this.strokeEnd(e);
42749         }
42750     },
42751     
42752     _handleTouchStart: function (e) {
42753         
42754         e.preventDefault();
42755         if (e.browserEvent.targetTouches.length === 1) {
42756             // var touch = e.browserEvent.changedTouches[0];
42757             // this.strokeBegin(touch);
42758             
42759              this.strokeBegin(e); // assume e catching the correct xy...
42760         }
42761     },
42762     
42763     _handleTouchMove: function (e) {
42764         e.preventDefault();
42765         // var touch = event.targetTouches[0];
42766         // _this._strokeMoveUpdate(touch);
42767         this.strokeMoveUpdate(e);
42768     },
42769     
42770     _handleTouchEnd: function (e) {
42771         var wasCanvasTouched = e.target === this.canvasEl().dom;
42772         if (wasCanvasTouched) {
42773             e.preventDefault();
42774             // var touch = event.changedTouches[0];
42775             // _this._strokeEnd(touch);
42776             this.strokeEnd(e);
42777         }
42778     },
42779     
42780     reset: function () {
42781         this._lastPoints = [];
42782         this._lastVelocity = 0;
42783         this._lastWidth = (this.min_width + this.max_width) / 2;
42784         this.canvasElCtx().fillStyle = this.dot_color;
42785     },
42786     
42787     strokeMoveUpdate: function(e)
42788     {
42789         this.strokeUpdate(e);
42790         
42791         if (this.throttle) {
42792             this.throttleStroke(this.strokeUpdate, this.throttle);
42793         }
42794         else {
42795             this.strokeUpdate(e);
42796         }
42797     },
42798     
42799     strokeBegin: function(e)
42800     {
42801         var newPointGroup = {
42802             color: this.dot_color,
42803             points: []
42804         };
42805         
42806         if (typeof this.onBegin === 'function') {
42807             this.onBegin(e);
42808         }
42809         
42810         this.curve_data.push(newPointGroup);
42811         this.reset();
42812         this.strokeUpdate(e);
42813     },
42814     
42815     strokeUpdate: function(e)
42816     {
42817         var rect = this.canvasEl().dom.getBoundingClientRect();
42818         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
42819         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
42820         var lastPoints = lastPointGroup.points;
42821         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
42822         var isLastPointTooClose = lastPoint
42823             ? point.distanceTo(lastPoint) <= this.min_distance
42824             : false;
42825         var color = lastPointGroup.color;
42826         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
42827             var curve = this.addPoint(point);
42828             if (!lastPoint) {
42829                 this.drawDot({color: color, point: point});
42830             }
42831             else if (curve) {
42832                 this.drawCurve({color: color, curve: curve});
42833             }
42834             lastPoints.push({
42835                 time: point.time,
42836                 x: point.x,
42837                 y: point.y
42838             });
42839         }
42840     },
42841     
42842     strokeEnd: function(e)
42843     {
42844         this.strokeUpdate(e);
42845         if (typeof this.onEnd === 'function') {
42846             this.onEnd(e);
42847         }
42848     },
42849     
42850     addPoint:  function (point) {
42851         var _lastPoints = this._lastPoints;
42852         _lastPoints.push(point);
42853         if (_lastPoints.length > 2) {
42854             if (_lastPoints.length === 3) {
42855                 _lastPoints.unshift(_lastPoints[0]);
42856             }
42857             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
42858             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
42859             _lastPoints.shift();
42860             return curve;
42861         }
42862         return null;
42863     },
42864     
42865     calculateCurveWidths: function (startPoint, endPoint) {
42866         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
42867             (1 - this.velocity_filter_weight) * this._lastVelocity;
42868
42869         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
42870         var widths = {
42871             end: newWidth,
42872             start: this._lastWidth
42873         };
42874         
42875         this._lastVelocity = velocity;
42876         this._lastWidth = newWidth;
42877         return widths;
42878     },
42879     
42880     drawDot: function (_a) {
42881         var color = _a.color, point = _a.point;
42882         var ctx = this.canvasElCtx();
42883         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
42884         ctx.beginPath();
42885         this.drawCurveSegment(point.x, point.y, width);
42886         ctx.closePath();
42887         ctx.fillStyle = color;
42888         ctx.fill();
42889     },
42890     
42891     drawCurve: function (_a) {
42892         var color = _a.color, curve = _a.curve;
42893         var ctx = this.canvasElCtx();
42894         var widthDelta = curve.endWidth - curve.startWidth;
42895         var drawSteps = Math.floor(curve.length()) * 2;
42896         ctx.beginPath();
42897         ctx.fillStyle = color;
42898         for (var i = 0; i < drawSteps; i += 1) {
42899         var t = i / drawSteps;
42900         var tt = t * t;
42901         var ttt = tt * t;
42902         var u = 1 - t;
42903         var uu = u * u;
42904         var uuu = uu * u;
42905         var x = uuu * curve.startPoint.x;
42906         x += 3 * uu * t * curve.control1.x;
42907         x += 3 * u * tt * curve.control2.x;
42908         x += ttt * curve.endPoint.x;
42909         var y = uuu * curve.startPoint.y;
42910         y += 3 * uu * t * curve.control1.y;
42911         y += 3 * u * tt * curve.control2.y;
42912         y += ttt * curve.endPoint.y;
42913         var width = curve.startWidth + ttt * widthDelta;
42914         this.drawCurveSegment(x, y, width);
42915         }
42916         ctx.closePath();
42917         ctx.fill();
42918     },
42919     
42920     drawCurveSegment: function (x, y, width) {
42921         var ctx = this.canvasElCtx();
42922         ctx.moveTo(x, y);
42923         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
42924         this.is_empty = false;
42925     },
42926     
42927     clear: function()
42928     {
42929         var ctx = this.canvasElCtx();
42930         var canvas = this.canvasEl().dom;
42931         ctx.fillStyle = this.bg_color;
42932         ctx.clearRect(0, 0, canvas.width, canvas.height);
42933         ctx.fillRect(0, 0, canvas.width, canvas.height);
42934         this.curve_data = [];
42935         this.reset();
42936         this.is_empty = true;
42937     },
42938     
42939     fileEl: function()
42940     {
42941         return  this.el.select('input',true).first();
42942     },
42943     
42944     canvasEl: function()
42945     {
42946         return this.el.select('canvas',true).first();
42947     },
42948     
42949     canvasElCtx: function()
42950     {
42951         return this.el.select('canvas',true).first().dom.getContext('2d');
42952     },
42953     
42954     getImage: function(type)
42955     {
42956         if(this.is_empty) {
42957             return false;
42958         }
42959         
42960         // encryption ?
42961         return this.canvasEl().dom.toDataURL('image/'+type, 1);
42962     },
42963     
42964     drawFromImage: function(img_src)
42965     {
42966         var img = new Image();
42967         
42968         img.onload = function(){
42969             this.canvasElCtx().drawImage(img, 0, 0);
42970         }.bind(this);
42971         
42972         img.src = img_src;
42973         
42974         this.is_empty = false;
42975     },
42976     
42977     selectImage: function()
42978     {
42979         this.fileEl().dom.click();
42980     },
42981     
42982     uploadImage: function(e)
42983     {
42984         var reader = new FileReader();
42985         
42986         reader.onload = function(e){
42987             var img = new Image();
42988             img.onload = function(){
42989                 this.reset();
42990                 this.canvasElCtx().drawImage(img, 0, 0);
42991             }.bind(this);
42992             img.src = e.target.result;
42993         }.bind(this);
42994         
42995         reader.readAsDataURL(e.target.files[0]);
42996     },
42997     
42998     // Bezier Point Constructor
42999     Point: (function () {
43000         function Point(x, y, time) {
43001             this.x = x;
43002             this.y = y;
43003             this.time = time || Date.now();
43004         }
43005         Point.prototype.distanceTo = function (start) {
43006             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
43007         };
43008         Point.prototype.equals = function (other) {
43009             return this.x === other.x && this.y === other.y && this.time === other.time;
43010         };
43011         Point.prototype.velocityFrom = function (start) {
43012             return this.time !== start.time
43013             ? this.distanceTo(start) / (this.time - start.time)
43014             : 0;
43015         };
43016         return Point;
43017     }()),
43018     
43019     
43020     // Bezier Constructor
43021     Bezier: (function () {
43022         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
43023             this.startPoint = startPoint;
43024             this.control2 = control2;
43025             this.control1 = control1;
43026             this.endPoint = endPoint;
43027             this.startWidth = startWidth;
43028             this.endWidth = endWidth;
43029         }
43030         Bezier.fromPoints = function (points, widths, scope) {
43031             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
43032             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
43033             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
43034         };
43035         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
43036             var dx1 = s1.x - s2.x;
43037             var dy1 = s1.y - s2.y;
43038             var dx2 = s2.x - s3.x;
43039             var dy2 = s2.y - s3.y;
43040             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
43041             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
43042             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
43043             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
43044             var dxm = m1.x - m2.x;
43045             var dym = m1.y - m2.y;
43046             var k = l2 / (l1 + l2);
43047             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
43048             var tx = s2.x - cm.x;
43049             var ty = s2.y - cm.y;
43050             return {
43051                 c1: new scope.Point(m1.x + tx, m1.y + ty),
43052                 c2: new scope.Point(m2.x + tx, m2.y + ty)
43053             };
43054         };
43055         Bezier.prototype.length = function () {
43056             var steps = 10;
43057             var length = 0;
43058             var px;
43059             var py;
43060             for (var i = 0; i <= steps; i += 1) {
43061                 var t = i / steps;
43062                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
43063                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
43064                 if (i > 0) {
43065                     var xdiff = cx - px;
43066                     var ydiff = cy - py;
43067                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
43068                 }
43069                 px = cx;
43070                 py = cy;
43071             }
43072             return length;
43073         };
43074         Bezier.prototype.point = function (t, start, c1, c2, end) {
43075             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
43076             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
43077             + (3.0 * c2 * (1.0 - t) * t * t)
43078             + (end * t * t * t);
43079         };
43080         return Bezier;
43081     }()),
43082     
43083     throttleStroke: function(fn, wait) {
43084       if (wait === void 0) { wait = 250; }
43085       var previous = 0;
43086       var timeout = null;
43087       var result;
43088       var storedContext;
43089       var storedArgs;
43090       var later = function () {
43091           previous = Date.now();
43092           timeout = null;
43093           result = fn.apply(storedContext, storedArgs);
43094           if (!timeout) {
43095               storedContext = null;
43096               storedArgs = [];
43097           }
43098       };
43099       return function wrapper() {
43100           var args = [];
43101           for (var _i = 0; _i < arguments.length; _i++) {
43102               args[_i] = arguments[_i];
43103           }
43104           var now = Date.now();
43105           var remaining = wait - (now - previous);
43106           storedContext = this;
43107           storedArgs = args;
43108           if (remaining <= 0 || remaining > wait) {
43109               if (timeout) {
43110                   clearTimeout(timeout);
43111                   timeout = null;
43112               }
43113               previous = now;
43114               result = fn.apply(storedContext, storedArgs);
43115               if (!timeout) {
43116                   storedContext = null;
43117                   storedArgs = [];
43118               }
43119           }
43120           else if (!timeout) {
43121               timeout = window.setTimeout(later, remaining);
43122           }
43123           return result;
43124       };
43125   }
43126   
43127 });
43128
43129  
43130
43131