Roo/bootstrap/Card.js
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = (
7         function() {
8                 var ret=3;
9                 Roo.each(document.styleSheets, function(s) {
10                     if ( s.href  && s.href.match(/css-bootstrap4/)) {
11                         ret=4;
12                     }
13                 });
14         return ret;
15 })(); /*
16  * Based on:
17  * Ext JS Library 1.1.1
18  * Copyright(c) 2006-2007, Ext JS, LLC.
19  *
20  * Originally Released Under LGPL - original licence link has changed is not relivant.
21  *
22  * Fork - LGPL
23  * <script type="text/javascript">
24  */
25
26
27 /**
28  * @class Roo.Shadow
29  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
30  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
31  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
32  * @constructor
33  * Create a new Shadow
34  * @param {Object} config The config object
35  */
36 Roo.Shadow = function(config){
37     Roo.apply(this, config);
38     if(typeof this.mode != "string"){
39         this.mode = this.defaultMode;
40     }
41     var o = this.offset, a = {h: 0};
42     var rad = Math.floor(this.offset/2);
43     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
44         case "drop":
45             a.w = 0;
46             a.l = a.t = o;
47             a.t -= 1;
48             if(Roo.isIE){
49                 a.l -= this.offset + rad;
50                 a.t -= this.offset + rad;
51                 a.w -= rad;
52                 a.h -= rad;
53                 a.t += 1;
54             }
55         break;
56         case "sides":
57             a.w = (o*2);
58             a.l = -o;
59             a.t = o-1;
60             if(Roo.isIE){
61                 a.l -= (this.offset - rad);
62                 a.t -= this.offset + rad;
63                 a.l += 1;
64                 a.w -= (this.offset - rad)*2;
65                 a.w -= rad + 1;
66                 a.h -= 1;
67             }
68         break;
69         case "frame":
70             a.w = a.h = (o*2);
71             a.l = a.t = -o;
72             a.t += 1;
73             a.h -= 2;
74             if(Roo.isIE){
75                 a.l -= (this.offset - rad);
76                 a.t -= (this.offset - rad);
77                 a.l += 1;
78                 a.w -= (this.offset + rad + 1);
79                 a.h -= (this.offset + rad);
80                 a.h += 1;
81             }
82         break;
83     };
84
85     this.adjusts = a;
86 };
87
88 Roo.Shadow.prototype = {
89     /**
90      * @cfg {String} mode
91      * The shadow display mode.  Supports the following options:<br />
92      * sides: Shadow displays on both sides and bottom only<br />
93      * frame: Shadow displays equally on all four sides<br />
94      * drop: Traditional bottom-right drop shadow (default)
95      */
96     /**
97      * @cfg {String} offset
98      * The number of pixels to offset the shadow from the element (defaults to 4)
99      */
100     offset: 4,
101
102     // private
103     defaultMode: "drop",
104
105     /**
106      * Displays the shadow under the target element
107      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
108      */
109     show : function(target){
110         target = Roo.get(target);
111         if(!this.el){
112             this.el = Roo.Shadow.Pool.pull();
113             if(this.el.dom.nextSibling != target.dom){
114                 this.el.insertBefore(target);
115             }
116         }
117         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
118         if(Roo.isIE){
119             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
120         }
121         this.realign(
122             target.getLeft(true),
123             target.getTop(true),
124             target.getWidth(),
125             target.getHeight()
126         );
127         this.el.dom.style.display = "block";
128     },
129
130     /**
131      * Returns true if the shadow is visible, else false
132      */
133     isVisible : function(){
134         return this.el ? true : false;  
135     },
136
137     /**
138      * Direct alignment when values are already available. Show must be called at least once before
139      * calling this method to ensure it is initialized.
140      * @param {Number} left The target element left position
141      * @param {Number} top The target element top position
142      * @param {Number} width The target element width
143      * @param {Number} height The target element height
144      */
145     realign : function(l, t, w, h){
146         if(!this.el){
147             return;
148         }
149         var a = this.adjusts, d = this.el.dom, s = d.style;
150         var iea = 0;
151         s.left = (l+a.l)+"px";
152         s.top = (t+a.t)+"px";
153         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
154  
155         if(s.width != sws || s.height != shs){
156             s.width = sws;
157             s.height = shs;
158             if(!Roo.isIE){
159                 var cn = d.childNodes;
160                 var sww = Math.max(0, (sw-12))+"px";
161                 cn[0].childNodes[1].style.width = sww;
162                 cn[1].childNodes[1].style.width = sww;
163                 cn[2].childNodes[1].style.width = sww;
164                 cn[1].style.height = Math.max(0, (sh-12))+"px";
165             }
166         }
167     },
168
169     /**
170      * Hides this shadow
171      */
172     hide : function(){
173         if(this.el){
174             this.el.dom.style.display = "none";
175             Roo.Shadow.Pool.push(this.el);
176             delete this.el;
177         }
178     },
179
180     /**
181      * Adjust the z-index of this shadow
182      * @param {Number} zindex The new z-index
183      */
184     setZIndex : function(z){
185         this.zIndex = z;
186         if(this.el){
187             this.el.setStyle("z-index", z);
188         }
189     }
190 };
191
192 // Private utility class that manages the internal Shadow cache
193 Roo.Shadow.Pool = function(){
194     var p = [];
195     var markup = Roo.isIE ?
196                  '<div class="x-ie-shadow"></div>' :
197                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
198     return {
199         pull : function(){
200             var sh = p.shift();
201             if(!sh){
202                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
203                 sh.autoBoxAdjust = false;
204             }
205             return sh;
206         },
207
208         push : function(sh){
209             p.push(sh);
210         }
211     };
212 }();/*
213  * - LGPL
214  *
215  * base class for bootstrap elements.
216  * 
217  */
218
219 Roo.bootstrap = Roo.bootstrap || {};
220 /**
221  * @class Roo.bootstrap.Component
222  * @extends Roo.Component
223  * Bootstrap Component base class
224  * @cfg {String} cls css class
225  * @cfg {String} style any extra css
226  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
227  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
228  * @cfg {string} dataId cutomer id
229  * @cfg {string} name Specifies name attribute
230  * @cfg {string} tooltip  Text for the tooltip
231  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
232  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
233  
234  * @constructor
235  * Do not use directly - it does not do anything..
236  * @param {Object} config The config object
237  */
238
239
240
241 Roo.bootstrap.Component = function(config){
242     Roo.bootstrap.Component.superclass.constructor.call(this, config);
243        
244     this.addEvents({
245         /**
246          * @event childrenrendered
247          * Fires when the children have been rendered..
248          * @param {Roo.bootstrap.Component} this
249          */
250         "childrenrendered" : true
251         
252         
253         
254     });
255     
256     
257 };
258
259 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
260     
261     
262     allowDomMove : false, // to stop relocations in parent onRender...
263     
264     cls : false,
265     
266     style : false,
267     
268     autoCreate : false,
269     
270     tooltip : null,
271     /**
272      * Initialize Events for the element
273      */
274     initEvents : function() { },
275     
276     xattr : false,
277     
278     parentId : false,
279     
280     can_build_overlaid : true,
281     
282     container_method : false,
283     
284     dataId : false,
285     
286     name : false,
287     
288     parent: function() {
289         // returns the parent component..
290         return Roo.ComponentMgr.get(this.parentId)
291         
292         
293     },
294     
295     // private
296     onRender : function(ct, position)
297     {
298        // Roo.log("Call onRender: " + this.xtype);
299         
300         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
301         
302         if(this.el){
303             if (this.el.attr('xtype')) {
304                 this.el.attr('xtypex', this.el.attr('xtype'));
305                 this.el.dom.removeAttribute('xtype');
306                 
307                 this.initEvents();
308             }
309             
310             return;
311         }
312         
313          
314         
315         var cfg = Roo.apply({},  this.getAutoCreate());
316         
317         cfg.id = this.id || Roo.id();
318         
319         // fill in the extra attributes 
320         if (this.xattr && typeof(this.xattr) =='object') {
321             for (var i in this.xattr) {
322                 cfg[i] = this.xattr[i];
323             }
324         }
325         
326         if(this.dataId){
327             cfg.dataId = this.dataId;
328         }
329         
330         if (this.cls) {
331             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
332         }
333         
334         if (this.style) { // fixme needs to support more complex style data.
335             cfg.style = this.style;
336         }
337         
338         if(this.name){
339             cfg.name = this.name;
340         }
341         
342         this.el = ct.createChild(cfg, position);
343         
344         if (this.tooltip) {
345             this.tooltipEl().attr('tooltip', this.tooltip);
346         }
347         
348         if(this.tabIndex !== undefined){
349             this.el.dom.setAttribute('tabIndex', this.tabIndex);
350         }
351         
352         this.initEvents();
353         
354     },
355     /**
356      * Fetch the element to add children to
357      * @return {Roo.Element} defaults to this.el
358      */
359     getChildContainer : function()
360     {
361         return this.el;
362     },
363     /**
364      * Fetch the element to display the tooltip on.
365      * @return {Roo.Element} defaults to this.el
366      */
367     tooltipEl : function()
368     {
369         return this.el;
370     },
371         
372     addxtype  : function(tree,cntr)
373     {
374         var cn = this;
375         
376         cn = Roo.factory(tree);
377         //Roo.log(['addxtype', cn]);
378            
379         cn.parentType = this.xtype; //??
380         cn.parentId = this.id;
381         
382         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
383         if (typeof(cn.container_method) == 'string') {
384             cntr = cn.container_method;
385         }
386         
387         
388         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
389         
390         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
391         
392         var build_from_html =  Roo.XComponent.build_from_html;
393           
394         var is_body  = (tree.xtype == 'Body') ;
395           
396         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
397           
398         var self_cntr_el = Roo.get(this[cntr](false));
399         
400         // do not try and build conditional elements 
401         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
402             return false;
403         }
404         
405         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
406             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
407                 return this.addxtypeChild(tree,cntr, is_body);
408             }
409             
410             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
411                 
412             if(echild){
413                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
414             }
415             
416             Roo.log('skipping render');
417             return cn;
418             
419         }
420         
421         var ret = false;
422         if (!build_from_html) {
423             return false;
424         }
425         
426         // this i think handles overlaying multiple children of the same type
427         // with the sam eelement.. - which might be buggy..
428         while (true) {
429             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
430             
431             if (!echild) {
432                 break;
433             }
434             
435             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
436                 break;
437             }
438             
439             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
440         }
441        
442         return ret;
443     },
444     
445     
446     addxtypeChild : function (tree, cntr, is_body)
447     {
448         Roo.debug && Roo.log('addxtypeChild:' + cntr);
449         var cn = this;
450         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
451         
452         
453         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
454                     (typeof(tree['flexy:foreach']) != 'undefined');
455           
456     
457         
458         skip_children = false;
459         // render the element if it's not BODY.
460         if (!is_body) {
461             
462             // if parent was disabled, then do not try and create the children..
463             if(!this[cntr](true)){
464                 tree.items = [];
465                 return tree;
466             }
467            
468             cn = Roo.factory(tree);
469            
470             cn.parentType = this.xtype; //??
471             cn.parentId = this.id;
472             
473             var build_from_html =  Roo.XComponent.build_from_html;
474             
475             
476             // does the container contain child eleemnts with 'xtype' attributes.
477             // that match this xtype..
478             // note - when we render we create these as well..
479             // so we should check to see if body has xtype set.
480             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
481                
482                 var self_cntr_el = Roo.get(this[cntr](false));
483                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
484                 if (echild) { 
485                     //Roo.log(Roo.XComponent.build_from_html);
486                     //Roo.log("got echild:");
487                     //Roo.log(echild);
488                 }
489                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
490                 // and are not displayed -this causes this to use up the wrong element when matching.
491                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
492                 
493                 
494                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
495                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
496                   
497                   
498                   
499                     cn.el = echild;
500                   //  Roo.log("GOT");
501                     //echild.dom.removeAttribute('xtype');
502                 } else {
503                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
504                     Roo.debug && Roo.log(self_cntr_el);
505                     Roo.debug && Roo.log(echild);
506                     Roo.debug && Roo.log(cn);
507                 }
508             }
509            
510             
511            
512             // if object has flexy:if - then it may or may not be rendered.
513             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
514                 // skip a flexy if element.
515                 Roo.debug && Roo.log('skipping render');
516                 Roo.debug && Roo.log(tree);
517                 if (!cn.el) {
518                     Roo.debug && Roo.log('skipping all children');
519                     skip_children = true;
520                 }
521                 
522              } else {
523                  
524                 // actually if flexy:foreach is found, we really want to create 
525                 // multiple copies here...
526                 //Roo.log('render');
527                 //Roo.log(this[cntr]());
528                 // some elements do not have render methods.. like the layouts...
529                 /*
530                 if(this[cntr](true) === false){
531                     cn.items = [];
532                     return cn;
533                 }
534                 */
535                 cn.render && cn.render(this[cntr](true));
536                 
537              }
538             // then add the element..
539         }
540          
541         // handle the kids..
542         
543         var nitems = [];
544         /*
545         if (typeof (tree.menu) != 'undefined') {
546             tree.menu.parentType = cn.xtype;
547             tree.menu.triggerEl = cn.el;
548             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
549             
550         }
551         */
552         if (!tree.items || !tree.items.length) {
553             cn.items = nitems;
554             //Roo.log(["no children", this]);
555             
556             return cn;
557         }
558          
559         var items = tree.items;
560         delete tree.items;
561         
562         //Roo.log(items.length);
563             // add the items..
564         if (!skip_children) {    
565             for(var i =0;i < items.length;i++) {
566               //  Roo.log(['add child', items[i]]);
567                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
568             }
569         }
570         
571         cn.items = nitems;
572         
573         //Roo.log("fire childrenrendered");
574         
575         cn.fireEvent('childrenrendered', this);
576         
577         return cn;
578     },
579     
580     /**
581      * Set the element that will be used to show or hide
582      */
583     setVisibilityEl : function(el)
584     {
585         this.visibilityEl = el;
586     },
587     
588      /**
589      * Get the element that will be used to show or hide
590      */
591     getVisibilityEl : function()
592     {
593         if (typeof(this.visibilityEl) == 'object') {
594             return this.visibilityEl;
595         }
596         
597         if (typeof(this.visibilityEl) == 'string') {
598             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
599         }
600         
601         return this.getEl();
602     },
603     
604     /**
605      * Show a component - removes 'hidden' class
606      */
607     show : function()
608     {
609         if(!this.getVisibilityEl()){
610             return;
611         }
612          
613         this.getVisibilityEl().removeClass(['hidden','d-none']);
614         
615         this.fireEvent('show', this);
616         
617         
618     },
619     /**
620      * Hide a component - adds 'hidden' class
621      */
622     hide: function()
623     {
624         if(!this.getVisibilityEl()){
625             return;
626         }
627         
628         this.getVisibilityEl().addClass(['hidden','d-none']);
629         
630         this.fireEvent('hide', this);
631         
632     }
633 });
634
635  /*
636  * - LGPL
637  *
638  * element
639  * 
640  */
641
642 /**
643  * @class Roo.bootstrap.Element
644  * @extends Roo.bootstrap.Component
645  * Bootstrap Element class
646  * @cfg {String} html contents of the element
647  * @cfg {String} tag tag of the element
648  * @cfg {String} cls class of the element
649  * @cfg {Boolean} preventDefault (true|false) default false
650  * @cfg {Boolean} clickable (true|false) default false
651  * 
652  * @constructor
653  * Create a new Element
654  * @param {Object} config The config object
655  */
656
657 Roo.bootstrap.Element = function(config){
658     Roo.bootstrap.Element.superclass.constructor.call(this, config);
659     
660     this.addEvents({
661         // raw events
662         /**
663          * @event click
664          * When a element is chick
665          * @param {Roo.bootstrap.Element} this
666          * @param {Roo.EventObject} e
667          */
668         "click" : true
669     });
670 };
671
672 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
673     
674     tag: 'div',
675     cls: '',
676     html: '',
677     preventDefault: false, 
678     clickable: false,
679     
680     getAutoCreate : function(){
681         
682         var cfg = {
683             tag: this.tag,
684             // cls: this.cls, double assign in parent class Component.js :: onRender
685             html: this.html
686         };
687         
688         return cfg;
689     },
690     
691     initEvents: function() 
692     {
693         Roo.bootstrap.Element.superclass.initEvents.call(this);
694         
695         if(this.clickable){
696             this.el.on('click', this.onClick, this);
697         }
698         
699     },
700     
701     onClick : function(e)
702     {
703         if(this.preventDefault){
704             e.preventDefault();
705         }
706         
707         this.fireEvent('click', this, e);
708     },
709     
710     getValue : function()
711     {
712         return this.el.dom.innerHTML;
713     },
714     
715     setValue : function(value)
716     {
717         this.el.dom.innerHTML = value;
718     }
719    
720 });
721
722  
723
724  /*
725  * - LGPL
726  *
727  * dropable area
728  * 
729  */
730
731 /**
732  * @class Roo.bootstrap.DropTarget
733  * @extends Roo.bootstrap.Element
734  * Bootstrap DropTarget class
735  
736  * @cfg {string} name dropable name
737  * 
738  * @constructor
739  * Create a new Dropable Area
740  * @param {Object} config The config object
741  */
742
743 Roo.bootstrap.DropTarget = function(config){
744     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
745     
746     this.addEvents({
747         // raw events
748         /**
749          * @event click
750          * When a element is chick
751          * @param {Roo.bootstrap.Element} this
752          * @param {Roo.EventObject} e
753          */
754         "drop" : true
755     });
756 };
757
758 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
759     
760     
761     getAutoCreate : function(){
762         
763          
764     },
765     
766     initEvents: function() 
767     {
768         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
769         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
770             ddGroup: this.name,
771             listeners : {
772                 drop : this.dragDrop.createDelegate(this),
773                 enter : this.dragEnter.createDelegate(this),
774                 out : this.dragOut.createDelegate(this),
775                 over : this.dragOver.createDelegate(this)
776             }
777             
778         });
779         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
780     },
781     
782     dragDrop : function(source,e,data)
783     {
784         // user has to decide how to impliment this.
785         Roo.log('drop');
786         Roo.log(this);
787         //this.fireEvent('drop', this, source, e ,data);
788         return false;
789     },
790     
791     dragEnter : function(n, dd, e, data)
792     {
793         // probably want to resize the element to match the dropped element..
794         Roo.log("enter");
795         this.originalSize = this.el.getSize();
796         this.el.setSize( n.el.getSize());
797         this.dropZone.DDM.refreshCache(this.name);
798         Roo.log([n, dd, e, data]);
799     },
800     
801     dragOut : function(value)
802     {
803         // resize back to normal
804         Roo.log("out");
805         this.el.setSize(this.originalSize);
806         this.dropZone.resetConstraints();
807     },
808     
809     dragOver : function()
810     {
811         // ??? do nothing?
812     }
813    
814 });
815
816  
817
818  /*
819  * - LGPL
820  *
821  * Body
822  *
823  */
824
825 /**
826  * @class Roo.bootstrap.Body
827  * @extends Roo.bootstrap.Component
828  * Bootstrap Body class
829  *
830  * @constructor
831  * Create a new body
832  * @param {Object} config The config object
833  */
834
835 Roo.bootstrap.Body = function(config){
836
837     config = config || {};
838
839     Roo.bootstrap.Body.superclass.constructor.call(this, config);
840     this.el = Roo.get(config.el ? config.el : document.body );
841     if (this.cls && this.cls.length) {
842         Roo.get(document.body).addClass(this.cls);
843     }
844 };
845
846 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
847
848     is_body : true,// just to make sure it's constructed?
849
850         autoCreate : {
851         cls: 'container'
852     },
853     onRender : function(ct, position)
854     {
855        /* Roo.log("Roo.bootstrap.Body - onRender");
856         if (this.cls && this.cls.length) {
857             Roo.get(document.body).addClass(this.cls);
858         }
859         // style??? xttr???
860         */
861     }
862
863
864
865
866 });
867 /*
868  * - LGPL
869  *
870  * button group
871  * 
872  */
873
874
875 /**
876  * @class Roo.bootstrap.ButtonGroup
877  * @extends Roo.bootstrap.Component
878  * Bootstrap ButtonGroup class
879  * @cfg {String} size lg | sm | xs (default empty normal)
880  * @cfg {String} align vertical | justified  (default none)
881  * @cfg {String} direction up | down (default down)
882  * @cfg {Boolean} toolbar false | true
883  * @cfg {Boolean} btn true | false
884  * 
885  * 
886  * @constructor
887  * Create a new Input
888  * @param {Object} config The config object
889  */
890
891 Roo.bootstrap.ButtonGroup = function(config){
892     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
893 };
894
895 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
896     
897     size: '',
898     align: '',
899     direction: '',
900     toolbar: false,
901     btn: true,
902
903     getAutoCreate : function(){
904         var cfg = {
905             cls: 'btn-group',
906             html : null
907         };
908         
909         cfg.html = this.html || cfg.html;
910         
911         if (this.toolbar) {
912             cfg = {
913                 cls: 'btn-toolbar',
914                 html: null
915             };
916             
917             return cfg;
918         }
919         
920         if (['vertical','justified'].indexOf(this.align)!==-1) {
921             cfg.cls = 'btn-group-' + this.align;
922             
923             if (this.align == 'justified') {
924                 console.log(this.items);
925             }
926         }
927         
928         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
929             cfg.cls += ' btn-group-' + this.size;
930         }
931         
932         if (this.direction == 'up') {
933             cfg.cls += ' dropup' ;
934         }
935         
936         return cfg;
937     },
938     /**
939      * Add a button to the group (similar to NavItem API.)
940      */
941     addItem : function(cfg)
942     {
943         var cn = new Roo.bootstrap.Button(cfg);
944         //this.register(cn);
945         cn.parentId = this.id;
946         cn.onRender(this.el, null);
947         return cn;
948     }
949    
950 });
951
952  /*
953  * - LGPL
954  *
955  * button
956  * 
957  */
958
959 /**
960  * @class Roo.bootstrap.Button
961  * @extends Roo.bootstrap.Component
962  * Bootstrap Button class
963  * @cfg {String} html The button content
964  * @cfg {String} weight (default | primary | secondary | success | info | warning | danger | link ) default
965  * @cfg {String} badge_weight (default | primary | secondary | success | info | warning | danger | link ) default (same as button)
966  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
967  * @cfg {String} size ( lg | sm | xs)
968  * @cfg {String} tag ( a | input | submit)
969  * @cfg {String} href empty or href
970  * @cfg {Boolean} disabled default false;
971  * @cfg {Boolean} isClose default false;
972  * @cfg {String} glyphicon depricated - use fa
973  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
974  * @cfg {String} badge text for badge
975  * @cfg {String} theme (default|glow)  
976  * @cfg {Boolean} inverse dark themed version
977  * @cfg {Boolean} toggle is it a slidy toggle button
978  * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
979  * @cfg {String} ontext text for on slidy toggle state
980  * @cfg {String} offtext text for off slidy toggle state
981  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
982  * @cfg {Boolean} removeClass remove the standard class..
983  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
984  * 
985  * @constructor
986  * Create a new button
987  * @param {Object} config The config object
988  */
989
990
991 Roo.bootstrap.Button = function(config){
992     Roo.bootstrap.Button.superclass.constructor.call(this, config);
993     this.weightClass = ["btn-default btn-outline-secondary", 
994                        "btn-primary", 
995                        "btn-success", 
996                        "btn-info", 
997                        "btn-warning",
998                        "btn-danger",
999                        "btn-link"
1000                       ],  
1001     this.addEvents({
1002         // raw events
1003         /**
1004          * @event click
1005          * When a butotn is pressed
1006          * @param {Roo.bootstrap.Button} btn
1007          * @param {Roo.EventObject} e
1008          */
1009         "click" : true,
1010          /**
1011          * @event toggle
1012          * After the button has been toggles
1013          * @param {Roo.bootstrap.Button} btn
1014          * @param {Roo.EventObject} e
1015          * @param {boolean} pressed (also available as button.pressed)
1016          */
1017         "toggle" : true
1018     });
1019 };
1020
1021 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1022     html: false,
1023     active: false,
1024     weight: '',
1025     badge_weight: '',
1026     outline : false,
1027     size: '',
1028     tag: 'button',
1029     href: '',
1030     disabled: false,
1031     isClose: false,
1032     glyphicon: '',
1033     fa: '',
1034     badge: '',
1035     theme: 'default',
1036     inverse: false,
1037     
1038     toggle: false,
1039     ontext: 'ON',
1040     offtext: 'OFF',
1041     defaulton: true,
1042     preventDefault: true,
1043     removeClass: false,
1044     name: false,
1045     target: false,
1046      
1047     pressed : null,
1048      
1049     
1050     getAutoCreate : function(){
1051         
1052         var cfg = {
1053             tag : 'button',
1054             cls : 'roo-button',
1055             html: ''
1056         };
1057         
1058         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1059             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1060             this.tag = 'button';
1061         } else {
1062             cfg.tag = this.tag;
1063         }
1064         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1065         
1066         if (this.toggle == true) {
1067             cfg={
1068                 tag: 'div',
1069                 cls: 'slider-frame roo-button',
1070                 cn: [
1071                     {
1072                         tag: 'span',
1073                         'data-on-text':'ON',
1074                         'data-off-text':'OFF',
1075                         cls: 'slider-button',
1076                         html: this.offtext
1077                     }
1078                 ]
1079             };
1080             
1081             if (['default', 'secondary' , 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
1082                 cfg.cls += ' '+this.weight;
1083             }
1084             
1085             return cfg;
1086         }
1087         
1088         if (this.isClose) {
1089             cfg.cls += ' close';
1090             
1091             cfg["aria-hidden"] = true;
1092             
1093             cfg.html = "&times;";
1094             
1095             return cfg;
1096         }
1097         
1098          
1099         if (this.theme==='default') {
1100             cfg.cls = 'btn roo-button';
1101             
1102             //if (this.parentType != 'Navbar') {
1103             this.weight = this.weight.length ?  this.weight : 'default';
1104             //}
1105             if (['default', 'primary', 'secondary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
1106                 
1107                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1108                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1109                 cfg.cls += ' btn-' + outline + weight;
1110                 if (this.weight == 'default') {
1111                     // BC
1112                     cfg.cls += ' btn-' + this.weight;
1113                 }
1114             }
1115         } else if (this.theme==='glow') {
1116             
1117             cfg.tag = 'a';
1118             cfg.cls = 'btn-glow roo-button';
1119             
1120             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
1121                 
1122                 cfg.cls += ' ' + this.weight;
1123             }
1124         }
1125    
1126         
1127         if (this.inverse) {
1128             this.cls += ' inverse';
1129         }
1130         
1131         
1132         if (this.active || this.pressed === true) {
1133             cfg.cls += ' active';
1134         }
1135         
1136         if (this.disabled) {
1137             cfg.disabled = 'disabled';
1138         }
1139         
1140         if (this.items) {
1141             Roo.log('changing to ul' );
1142             cfg.tag = 'ul';
1143             this.glyphicon = 'caret';
1144             if (Roo.bootstrap.version == 4) {
1145                 this.fa = 'caret-down';
1146             }
1147             
1148         }
1149         
1150         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1151          
1152         //gsRoo.log(this.parentType);
1153         if (this.parentType === 'Navbar' && !this.parent().bar) {
1154             Roo.log('changing to li?');
1155             
1156             cfg.tag = 'li';
1157             
1158             cfg.cls = '';
1159             cfg.cn =  [{
1160                 tag : 'a',
1161                 cls : 'roo-button',
1162                 html : this.html,
1163                 href : this.href || '#'
1164             }];
1165             if (this.menu) {
1166                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1167                 cfg.cls += ' dropdown';
1168             }   
1169             
1170             delete cfg.html;
1171             
1172         }
1173         
1174        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1175         
1176         if (this.glyphicon) {
1177             cfg.html = ' ' + cfg.html;
1178             
1179             cfg.cn = [
1180                 {
1181                     tag: 'span',
1182                     cls: 'glyphicon glyphicon-' + this.glyphicon
1183                 }
1184             ];
1185         }
1186         if (this.fa) {
1187             cfg.html = ' ' + cfg.html;
1188             
1189             cfg.cn = [
1190                 {
1191                     tag: 'i',
1192                     cls: 'fa fas fa-' + this.fa
1193                 }
1194             ];
1195         }
1196         
1197         if (this.badge) {
1198             cfg.html += ' ';
1199             
1200             cfg.tag = 'a';
1201             
1202 //            cfg.cls='btn roo-button';
1203             
1204             cfg.href=this.href;
1205             
1206             var value = cfg.html;
1207             
1208             if(this.glyphicon){
1209                 value = {
1210                     tag: 'span',
1211                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1212                     html: this.html
1213                 };
1214             }
1215             if(this.fa){
1216                 value = {
1217                     tag: 'i',
1218                     cls: 'fa fas fa-' + this.fa,
1219                     html: this.html
1220                 };
1221             }
1222             
1223             var bw = this.badge_weight.length ? this.badge_weight :
1224                 (this.weight.length ? this.weight : 'secondary');
1225             bw = bw == 'default' ? 'secondary' : bw;
1226             
1227             cfg.cn = [
1228                 value,
1229                 {
1230                     tag: 'span',
1231                     cls: 'badge badge-' + bw,
1232                     html: this.badge
1233                 }
1234             ];
1235             
1236             cfg.html='';
1237         }
1238         
1239         if (this.menu) {
1240             cfg.cls += ' dropdown';
1241             cfg.html = typeof(cfg.html) != 'undefined' ?
1242                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1243         }
1244         
1245         if (cfg.tag !== 'a' && this.href !== '') {
1246             throw "Tag must be a to set href.";
1247         } else if (this.href.length > 0) {
1248             cfg.href = this.href;
1249         }
1250         
1251         if(this.removeClass){
1252             cfg.cls = '';
1253         }
1254         
1255         if(this.target){
1256             cfg.target = this.target;
1257         }
1258         
1259         return cfg;
1260     },
1261     initEvents: function() {
1262        // Roo.log('init events?');
1263 //        Roo.log(this.el.dom);
1264         // add the menu...
1265         
1266         if (typeof (this.menu) != 'undefined') {
1267             this.menu.parentType = this.xtype;
1268             this.menu.triggerEl = this.el;
1269             this.addxtype(Roo.apply({}, this.menu));
1270         }
1271
1272
1273        if (this.el.hasClass('roo-button')) {
1274             this.el.on('click', this.onClick, this);
1275        } else {
1276             this.el.select('.roo-button').on('click', this.onClick, this);
1277        }
1278        
1279        if(this.removeClass){
1280            this.el.on('click', this.onClick, this);
1281        }
1282        
1283        this.el.enableDisplayMode();
1284         
1285     },
1286     onClick : function(e)
1287     {
1288         if (this.disabled) {
1289             return;
1290         }
1291         
1292         Roo.log('button on click ');
1293         if(this.preventDefault){
1294             e.preventDefault();
1295         }
1296         
1297         if (this.pressed === true || this.pressed === false) {
1298             this.toggleActive(e);
1299         }
1300         
1301         
1302         this.fireEvent('click', this, e);
1303     },
1304     
1305     /**
1306      * Enables this button
1307      */
1308     enable : function()
1309     {
1310         this.disabled = false;
1311         this.el.removeClass('disabled');
1312     },
1313     
1314     /**
1315      * Disable this button
1316      */
1317     disable : function()
1318     {
1319         this.disabled = true;
1320         this.el.addClass('disabled');
1321     },
1322      /**
1323      * sets the active state on/off, 
1324      * @param {Boolean} state (optional) Force a particular state
1325      */
1326     setActive : function(v) {
1327         
1328         this.el[v ? 'addClass' : 'removeClass']('active');
1329         this.pressed = v;
1330     },
1331      /**
1332      * toggles the current active state 
1333      */
1334     toggleActive : function(e)
1335     {
1336         this.setActive(!this.pressed);
1337         this.fireEvent('toggle', this, e, !this.pressed);
1338     },
1339      /**
1340      * get the current active state
1341      * @return {boolean} true if it's active
1342      */
1343     isActive : function()
1344     {
1345         return this.el.hasClass('active');
1346     },
1347     /**
1348      * set the text of the first selected button
1349      */
1350     setText : function(str)
1351     {
1352         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1353     },
1354     /**
1355      * get the text of the first selected button
1356      */
1357     getText : function()
1358     {
1359         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1360     },
1361     
1362     setWeight : function(str)
1363     {
1364         this.el.removeClass(this.weightClass);
1365         this.weight = str;
1366         var outline = this.outline ? 'outline-' : '';
1367         if (str == 'default') {
1368             this.el.addClass('btn-default btn-outline-secondary');        
1369             return;
1370         }
1371         this.el.addClass('btn-' + outline + str);        
1372     }
1373     
1374     
1375 });
1376
1377  /*
1378  * - LGPL
1379  *
1380  * column
1381  * 
1382  */
1383
1384 /**
1385  * @class Roo.bootstrap.Column
1386  * @extends Roo.bootstrap.Component
1387  * Bootstrap Column class
1388  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1389  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1390  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1391  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1392  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1393  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1394  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1395  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1396  *
1397  * 
1398  * @cfg {Boolean} hidden (true|false) hide the element
1399  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1400  * @cfg {String} fa (ban|check|...) font awesome icon
1401  * @cfg {Number} fasize (1|2|....) font awsome size
1402
1403  * @cfg {String} icon (info-sign|check|...) glyphicon name
1404
1405  * @cfg {String} html content of column.
1406  * 
1407  * @constructor
1408  * Create a new Column
1409  * @param {Object} config The config object
1410  */
1411
1412 Roo.bootstrap.Column = function(config){
1413     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1414 };
1415
1416 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1417     
1418     xs: false,
1419     sm: false,
1420     md: false,
1421     lg: false,
1422     xsoff: false,
1423     smoff: false,
1424     mdoff: false,
1425     lgoff: false,
1426     html: '',
1427     offset: 0,
1428     alert: false,
1429     fa: false,
1430     icon : false,
1431     hidden : false,
1432     fasize : 1,
1433     
1434     getAutoCreate : function(){
1435         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1436         
1437         cfg = {
1438             tag: 'div',
1439             cls: 'column'
1440         };
1441         
1442         var settings=this;
1443         var sizes =   ['xs','sm','md','lg'];
1444         sizes.map(function(size ,ix){
1445             //Roo.log( size + ':' + settings[size]);
1446             
1447             if (settings[size+'off'] !== false) {
1448                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1449             }
1450             
1451             if (settings[size] === false) {
1452                 return;
1453             }
1454             
1455             if (!settings[size]) { // 0 = hidden
1456                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1457                 // bootsrap4
1458                 for (var i = ix; i > -1; i--) {
1459                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1460                 }
1461                 
1462                 
1463                 return;
1464             }
1465             cfg.cls += ' col-' + size + '-' + settings[size] + (
1466                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1467             );
1468             
1469         });
1470         
1471         if (this.hidden) {
1472             cfg.cls += ' hidden';
1473         }
1474         
1475         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1476             cfg.cls +=' alert alert-' + this.alert;
1477         }
1478         
1479         
1480         if (this.html.length) {
1481             cfg.html = this.html;
1482         }
1483         if (this.fa) {
1484             var fasize = '';
1485             if (this.fasize > 1) {
1486                 fasize = ' fa-' + this.fasize + 'x';
1487             }
1488             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1489             
1490             
1491         }
1492         if (this.icon) {
1493             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1494         }
1495         
1496         return cfg;
1497     }
1498    
1499 });
1500
1501  
1502
1503  /*
1504  * - LGPL
1505  *
1506  * page container.
1507  * 
1508  */
1509
1510
1511 /**
1512  * @class Roo.bootstrap.Container
1513  * @extends Roo.bootstrap.Component
1514  * Bootstrap Container class
1515  * @cfg {Boolean} jumbotron is it a jumbotron element
1516  * @cfg {String} html content of element
1517  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1518  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1519  * @cfg {String} header content of header (for panel)
1520  * @cfg {String} footer content of footer (for panel)
1521  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1522  * @cfg {String} tag (header|aside|section) type of HTML tag.
1523  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1524  * @cfg {String} fa font awesome icon
1525  * @cfg {String} icon (info-sign|check|...) glyphicon name
1526  * @cfg {Boolean} hidden (true|false) hide the element
1527  * @cfg {Boolean} expandable (true|false) default false
1528  * @cfg {Boolean} expanded (true|false) default true
1529  * @cfg {String} rheader contet on the right of header
1530  * @cfg {Boolean} clickable (true|false) default false
1531
1532  *     
1533  * @constructor
1534  * Create a new Container
1535  * @param {Object} config The config object
1536  */
1537
1538 Roo.bootstrap.Container = function(config){
1539     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1540     
1541     this.addEvents({
1542         // raw events
1543          /**
1544          * @event expand
1545          * After the panel has been expand
1546          * 
1547          * @param {Roo.bootstrap.Container} this
1548          */
1549         "expand" : true,
1550         /**
1551          * @event collapse
1552          * After the panel has been collapsed
1553          * 
1554          * @param {Roo.bootstrap.Container} this
1555          */
1556         "collapse" : true,
1557         /**
1558          * @event click
1559          * When a element is chick
1560          * @param {Roo.bootstrap.Container} this
1561          * @param {Roo.EventObject} e
1562          */
1563         "click" : true
1564     });
1565 };
1566
1567 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1568     
1569     jumbotron : false,
1570     well: '',
1571     panel : '',
1572     header: '',
1573     footer : '',
1574     sticky: '',
1575     tag : false,
1576     alert : false,
1577     fa: false,
1578     icon : false,
1579     expandable : false,
1580     rheader : '',
1581     expanded : true,
1582     clickable: false,
1583   
1584      
1585     getChildContainer : function() {
1586         
1587         if(!this.el){
1588             return false;
1589         }
1590         
1591         if (this.panel.length) {
1592             return this.el.select('.panel-body',true).first();
1593         }
1594         
1595         return this.el;
1596     },
1597     
1598     
1599     getAutoCreate : function(){
1600         
1601         var cfg = {
1602             tag : this.tag || 'div',
1603             html : '',
1604             cls : ''
1605         };
1606         if (this.jumbotron) {
1607             cfg.cls = 'jumbotron';
1608         }
1609         
1610         
1611         
1612         // - this is applied by the parent..
1613         //if (this.cls) {
1614         //    cfg.cls = this.cls + '';
1615         //}
1616         
1617         if (this.sticky.length) {
1618             
1619             var bd = Roo.get(document.body);
1620             if (!bd.hasClass('bootstrap-sticky')) {
1621                 bd.addClass('bootstrap-sticky');
1622                 Roo.select('html',true).setStyle('height', '100%');
1623             }
1624              
1625             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1626         }
1627         
1628         
1629         if (this.well.length) {
1630             switch (this.well) {
1631                 case 'lg':
1632                 case 'sm':
1633                     cfg.cls +=' well well-' +this.well;
1634                     break;
1635                 default:
1636                     cfg.cls +=' well';
1637                     break;
1638             }
1639         }
1640         
1641         if (this.hidden) {
1642             cfg.cls += ' hidden';
1643         }
1644         
1645         
1646         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1647             cfg.cls +=' alert alert-' + this.alert;
1648         }
1649         
1650         var body = cfg;
1651         
1652         if (this.panel.length) {
1653             cfg.cls += ' panel panel-' + this.panel;
1654             cfg.cn = [];
1655             if (this.header.length) {
1656                 
1657                 var h = [];
1658                 
1659                 if(this.expandable){
1660                     
1661                     cfg.cls = cfg.cls + ' expandable';
1662                     
1663                     h.push({
1664                         tag: 'i',
1665                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1666                     });
1667                     
1668                 }
1669                 
1670                 h.push(
1671                     {
1672                         tag: 'span',
1673                         cls : 'panel-title',
1674                         html : (this.expandable ? '&nbsp;' : '') + this.header
1675                     },
1676                     {
1677                         tag: 'span',
1678                         cls: 'panel-header-right',
1679                         html: this.rheader
1680                     }
1681                 );
1682                 
1683                 cfg.cn.push({
1684                     cls : 'panel-heading',
1685                     style : this.expandable ? 'cursor: pointer' : '',
1686                     cn : h
1687                 });
1688                 
1689             }
1690             
1691             body = false;
1692             cfg.cn.push({
1693                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1694                 html : this.html
1695             });
1696             
1697             
1698             if (this.footer.length) {
1699                 cfg.cn.push({
1700                     cls : 'panel-footer',
1701                     html : this.footer
1702                     
1703                 });
1704             }
1705             
1706         }
1707         
1708         if (body) {
1709             body.html = this.html || cfg.html;
1710             // prefix with the icons..
1711             if (this.fa) {
1712                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1713             }
1714             if (this.icon) {
1715                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1716             }
1717             
1718             
1719         }
1720         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1721             cfg.cls =  'container';
1722         }
1723         
1724         return cfg;
1725     },
1726     
1727     initEvents: function() 
1728     {
1729         if(this.expandable){
1730             var headerEl = this.headerEl();
1731         
1732             if(headerEl){
1733                 headerEl.on('click', this.onToggleClick, this);
1734             }
1735         }
1736         
1737         if(this.clickable){
1738             this.el.on('click', this.onClick, this);
1739         }
1740         
1741     },
1742     
1743     onToggleClick : function()
1744     {
1745         var headerEl = this.headerEl();
1746         
1747         if(!headerEl){
1748             return;
1749         }
1750         
1751         if(this.expanded){
1752             this.collapse();
1753             return;
1754         }
1755         
1756         this.expand();
1757     },
1758     
1759     expand : function()
1760     {
1761         if(this.fireEvent('expand', this)) {
1762             
1763             this.expanded = true;
1764             
1765             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1766             
1767             this.el.select('.panel-body',true).first().removeClass('hide');
1768             
1769             var toggleEl = this.toggleEl();
1770
1771             if(!toggleEl){
1772                 return;
1773             }
1774
1775             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1776         }
1777         
1778     },
1779     
1780     collapse : function()
1781     {
1782         if(this.fireEvent('collapse', this)) {
1783             
1784             this.expanded = false;
1785             
1786             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1787             this.el.select('.panel-body',true).first().addClass('hide');
1788         
1789             var toggleEl = this.toggleEl();
1790
1791             if(!toggleEl){
1792                 return;
1793             }
1794
1795             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1796         }
1797     },
1798     
1799     toggleEl : function()
1800     {
1801         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1802             return;
1803         }
1804         
1805         return this.el.select('.panel-heading .fa',true).first();
1806     },
1807     
1808     headerEl : function()
1809     {
1810         if(!this.el || !this.panel.length || !this.header.length){
1811             return;
1812         }
1813         
1814         return this.el.select('.panel-heading',true).first()
1815     },
1816     
1817     bodyEl : function()
1818     {
1819         if(!this.el || !this.panel.length){
1820             return;
1821         }
1822         
1823         return this.el.select('.panel-body',true).first()
1824     },
1825     
1826     titleEl : function()
1827     {
1828         if(!this.el || !this.panel.length || !this.header.length){
1829             return;
1830         }
1831         
1832         return this.el.select('.panel-title',true).first();
1833     },
1834     
1835     setTitle : function(v)
1836     {
1837         var titleEl = this.titleEl();
1838         
1839         if(!titleEl){
1840             return;
1841         }
1842         
1843         titleEl.dom.innerHTML = v;
1844     },
1845     
1846     getTitle : function()
1847     {
1848         
1849         var titleEl = this.titleEl();
1850         
1851         if(!titleEl){
1852             return '';
1853         }
1854         
1855         return titleEl.dom.innerHTML;
1856     },
1857     
1858     setRightTitle : function(v)
1859     {
1860         var t = this.el.select('.panel-header-right',true).first();
1861         
1862         if(!t){
1863             return;
1864         }
1865         
1866         t.dom.innerHTML = v;
1867     },
1868     
1869     onClick : function(e)
1870     {
1871         e.preventDefault();
1872         
1873         this.fireEvent('click', this, e);
1874     }
1875 });
1876
1877  /*
1878  *  - LGPL
1879  *
1880  *  This is BS4's Card element.. - similar to our containers probably..
1881  * 
1882  */
1883 /**
1884  * @class Roo.bootstrap.Card
1885  * @extends Roo.bootstrap.Component
1886  * Bootstrap Card class
1887  *
1888  *
1889  * possible... may not be implemented..
1890  * @cfg {String} header_image  src url of image.
1891  * @cfg {String|Object} header
1892  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1893  * 
1894  * @cfg {String} title
1895  * @cfg {String} subtitle
1896  * @cfg {String} html -- html contents - or just use children..
1897  * @cfg {String} footer
1898  
1899  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1900  * 
1901  * @cfg {String} margin (0|1|2|3|4|5|auto)
1902  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1903  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1904  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1905  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1906  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1907  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1908  *
1909  * @cfg {String} padding (0|1|2|3|4|5)
1910  * @cfg {String} padding_top (0|1|2|3|4|5)
1911  * @cfg {String} padding_bottom (0|1|2|3|4|5)
1912  * @cfg {String} padding_left (0|1|2|3|4|5)
1913  * @cfg {String} padding_right (0|1|2|3|4|5)
1914  * @cfg {String} padding_x (0|1|2|3|4|5)
1915  * @cfg {String} padding_y (0|1|2|3|4|5)
1916  *
1917  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1918  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1919  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1920  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1921  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1922  
1923  * @config {Boolean} dragable  if this card can be dragged.
1924  * @config {String} drag_group  group for drag
1925  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
1926  * @config {String} drop_group  group for drag
1927  * 
1928  * @config {Boolean} collapsable can the body be collapsed.
1929  * @config {Boolean} collapsed is the body collapsed when rendered...
1930  * @constructor
1931  * Create a new Container
1932  * @param {Object} config The config object
1933  */
1934
1935 Roo.bootstrap.Card = function(config){
1936     Roo.bootstrap.Card.superclass.constructor.call(this, config);
1937     
1938     this.addEvents({
1939          // raw events
1940         /**
1941          * @event drop
1942          * When a element a card is dropped
1943          * @param {Roo.bootstrap.Element} this
1944          * @param {Roo.Element} n the node being dropped?
1945          * @param {Object} dd Drag and drop data
1946          * @param {Roo.EventObject} e
1947          * @param {Roo.EventObject} data  the data passed via getDragData
1948          */
1949         'drop' : true
1950         
1951     });
1952 };
1953
1954
1955 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
1956     
1957     
1958     weight : '',
1959     
1960     margin: '', /// may be better in component?
1961     margin_top: '', 
1962     margin_bottom: '', 
1963     margin_left: '',
1964     margin_right: '',
1965     margin_x: '',
1966     margin_y: '',
1967     
1968     padding : '',
1969     padding_top: '', 
1970     padding_bottom: '', 
1971     padding_left: '',
1972     padding_right: '',
1973     padding_x: '',
1974     padding_y: '',
1975     
1976     display: '', 
1977     display_xs: '', 
1978     display_sm: '', 
1979     display_lg: '',
1980     display_xl: '',
1981  
1982     header_image  : '',
1983     header : '',
1984     header_size : 0,
1985     title : '',
1986     subtitle : '',
1987     html : '',
1988     footer: '',
1989
1990     collapsable : false,
1991     collapsed : false,
1992     
1993     dragable : false,
1994     drag_group : false,
1995     dropable : false,
1996     drop_group : false,
1997     childContainer : false,
1998     dropEl : false, /// the dom placeholde element that indicates drop location.
1999     
2000     layoutCls : function()
2001     {
2002         var cls = '';
2003         var t = this;
2004         Roo.log(this.margin_bottom.length);
2005         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2006             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2007             
2008             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2009                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2010             }
2011             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2012                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2013             }
2014         });
2015         
2016         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2017             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2018                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['margin' + (v.length ? '_' : '') + v]
2019             }
2020         });
2021         
2022         // more generic support?
2023         if (this.hidden) {
2024             cls += ' d-none';
2025         }
2026         
2027         return cls;
2028     },
2029  
2030        // Roo.log("Call onRender: " + this.xtype);
2031         /*  We are looking at something like this.
2032 <div class="card">
2033     <img src="..." class="card-img-top" alt="...">
2034     <div class="card-body">
2035         <h5 class="card-title">Card title</h5>
2036          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2037
2038         >> this bit is really the body...
2039         <div> << we will ad dthis in hopefully it will not break shit.
2040         
2041         ** card text does not actually have any styling...
2042         
2043             <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2044         
2045         </div> <<
2046           <a href="#" class="card-link">Card link</a>
2047           
2048     </div>
2049     <div class="card-footer">
2050         <small class="text-muted">Last updated 3 mins ago</small>
2051     </div>
2052 </div>
2053          */
2054     getAutoCreate : function(){
2055         
2056         var cfg = {
2057             tag : 'div',
2058             cls : 'card',
2059             cn : [ ]
2060         };
2061         
2062         if (this.weight.length && this.weight != 'light') {
2063             cfg.cls += ' text-white';
2064         } else {
2065             cfg.cls += ' text-dark'; // need as it's nested..
2066         }
2067         if (this.weight.length) {
2068             cfg.cls += ' bg-' + this.weight;
2069         }
2070         
2071         cfg.cls += this.layoutCls(); 
2072         
2073     var hdr = false;
2074         if (this.header.length) {
2075             hdr = {
2076                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2077                 cls : 'card-header',
2078         cn : []
2079             };
2080         cfg.cn.push(hdr);
2081         hdr_ctr = hdr;
2082         } else {
2083         hdr = {
2084                 tag : 'div',
2085                 cls : 'card-header d-none',
2086         cn : []
2087             };
2088         cfg.cn.push(hdr);
2089     }
2090     if (this.collapsable) {
2091         hdr_ctr = {
2092         tag : 'a',
2093         cls : 'd-block user-select-none',
2094         cn: [
2095             {
2096             tag: 'i',
2097             cls : 'roo-collapse-toggle fa fa-chevron-down float-right'
2098             }
2099            
2100         ]
2101         };
2102         hdr.cn.push(hdr_ctr);
2103     }
2104     if (this.header.length) {
2105         hdr_ctr.cn.push(        {
2106         tag: 'span',
2107         cls: 'roo-card-header-ctr',
2108         html : this.header
2109         })
2110     }
2111     
2112         if (this.header_image.length) {
2113             cfg.cn.push({
2114                 tag : 'img',
2115                 cls : 'card-img-top',
2116                 src: this.header_image // escape?
2117             });
2118         } else {
2119         cfg.cn.push({
2120                 tag : 'div',
2121                 cls : 'card-img-top d-none' 
2122             });
2123     }
2124         
2125         var body = {
2126             tag : 'div',
2127             cls : 'card-body',
2128             cn : []
2129         };
2130     var obody = body;
2131     if (this.collapsable) {
2132         obody = {
2133         tag: 'div',
2134         cls : 'roo-collapsable collapse ' + (this.collapsed ? '' : 'show'),
2135         cn : [  body ]
2136         };
2137     }
2138     
2139         cfg.cn.push(obody);
2140         
2141         if (this.title.length) {
2142             body.cn.push({
2143                 tag : 'div',
2144                 cls : 'card-title',
2145                 src: this.title // escape?
2146             });
2147         }
2148         
2149         if (this.subtitle.length) {
2150             body.cn.push({
2151                 tag : 'div',
2152                 cls : 'card-title',
2153                 src: this.subtitle // escape?
2154             });
2155         }
2156         
2157         body.cn.push({
2158             tag : 'div',
2159             cls : 'roo-card-body-ctr'
2160         });
2161         
2162         if (this.html.length) {
2163             body.cn.push({
2164                 tag: 'div',
2165                 html : this.html
2166             });
2167         }
2168         // fixme ? handle objects?
2169         if (this.footer.length) {
2170             cfg.cn.push({
2171                 tag : 'div',
2172                 cls : 'card-footer',
2173                 html: this.footer // escape?
2174             });
2175         }
2176         // footer...
2177         
2178         return cfg;
2179     },
2180     
2181     
2182     getCardHeader : function()
2183     {
2184         var  ret = this.el.select('.card-header',true).first();
2185     if (ret.hasClass('d-none')) {
2186         ret.removeClass('d-none');
2187     }
2188         
2189         return ret;
2190     },
2191     
2192     getCardImageTop : function()
2193     {
2194         var  ret = this.el.select('.card-img-top',true).first();
2195     if (ret.hasClass('d-none')) {
2196         ret.removeClass('d-none');
2197     }
2198         
2199         return ret;
2200     },
2201     
2202     getChildContainer : function()
2203     {
2204         
2205         if(!this.el){
2206             return false;
2207         }
2208         return this.el.select('.roo-card-body-ctr',true).first();    
2209     },
2210     
2211     initEvents: function() 
2212     {
2213         
2214     this.bodyEl = this.getChildContainer();
2215     if(this.dragable){
2216             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2217                     containerScroll: true,
2218                     ddGroup: this.drag_group || 'default_card_drag_group'
2219             });
2220             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2221         }
2222         if (this.dropable) {
2223         this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2224             containerScroll: true,
2225             ddGroup: this.drop_group || 'default_card_drag_group'
2226         });
2227         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2228         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2229         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2230         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2231         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2232     }
2233         
2234         if (this.collapsable) {
2235         this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2236     }
2237     },
2238     getDragData : function(e)
2239     {
2240         var target = this.getEl();
2241         if (target) {
2242             //this.handleSelection(e);
2243             
2244             var dragData = {
2245                 source: this,
2246                 copy: false,
2247                 nodes: this.getEl(),
2248                 records: []
2249             };
2250             
2251             
2252             dragData.ddel = target.dom ;    // the div element
2253             Roo.log(target.getWidth( ));
2254             dragData.ddel.style.width = target.getWidth() + 'px';
2255             
2256             return dragData;
2257         }
2258         return false;
2259     },
2260     /**
2261  *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2262  *    whole Element becomes the target, and this causes the drop gesture to append.
2263  */
2264     getTargetFromEvent : function(e, dragged_card_el)
2265     {
2266         var target = e.getTarget();
2267         while ((target !== null) && (target.parentNode != this.bodyEl.dom)) {
2268             target = target.parentNode;
2269         }
2270         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2271         // see if target is one of the 'cards'...
2272         var ctarget = -1;
2273         var cards = [];
2274         //Roo.log(this.items.length);
2275         var lpos = pos = cpos = false;
2276         for (var i = 0;i< this.items.length;i++) {
2277             
2278             if (!this.items[i].el.hasClass('card')) {
2279                  continue;
2280             }
2281             pos = this.getDropPoint(e, this.items[i].el.dom);
2282             
2283             //Roo.log(this.items[i].el.dom.id);
2284             cards.push(this.items[i]);
2285             if (ctarget < 0 && pos == 'above') {
2286                 ctarget = i > 0 ? i - 1 : 0;
2287                 cpos = i > 0 ? 'below' : pos;
2288             }
2289         }
2290         if (!cards.length) {
2291             return [ true, 'below' ];
2292         }
2293         
2294         if (ctarget < 0) {
2295             ctarget = cards.length -1;
2296             cpos = 'below';
2297         }
2298         if (cards[ctarget].el == dragged_card_el) {
2299             return false;
2300         }
2301         
2302         if (cpos == 'below') {
2303             var card_after = ctarget+1 == cards.length ? false : cards[ctarget+1];
2304             
2305             // then above should not be dragged_card_el.
2306             // and ctarget sho
2307             
2308             if (card_after  && card_after.el == dragged_card_el) {
2309                 return false;
2310             }
2311             return [ cards[ctarget], cpos ];
2312         }
2313         
2314         // its's after ..
2315         var card_before = ctarget > 0 ? cards[ctarget-1] : false;
2316         
2317             
2318         if (card_before  && card_before.el == dragged_card_el) {
2319             return false;
2320         }
2321         
2322         return [ cards[ctarget], cpos ];
2323     },
2324     
2325     onNodeEnter : function(n, dd, e, data){
2326         return false;
2327     },
2328     onNodeOver : function(n, dd, e, data)
2329     {
2330        
2331         var target_info = this.getTargetFromEvent(e,data.source.el);
2332         if (target_info === false) {
2333             this.dropPlaceHolder('hide');
2334             return false;
2335         }
2336         Roo.log(['getTargetFromEvent', target_info[0].el.dom.id,target_info[1]]);
2337         
2338          
2339         this.dropPlaceHolder('show', target_info,data);
2340         
2341         return false; 
2342     },
2343     onNodeOut : function(n, dd, e, data){
2344         this.dropPlaceHolder('hide');
2345      
2346     },
2347     onNodeDrop : function(n, dd, e, data)
2348     {
2349         
2350         // call drop - return false if  
2351         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
2352             return false;
2353         }
2354         
2355         
2356         
2357         var pt = this.getDropPoint(e, n, dd);
2358         var insertAt = (n == this.bodyEl.dom) ? this.items.length : n.nodeIndex;
2359         if (pt == "below") {
2360             insertAt++;
2361         }
2362         for (var i = 0; i < this.items.length; i++) {
2363             var r = this.items[i];
2364             //var dup = this.store.getById(r.id);
2365             if (dup && (dd != this.dragZone)) {
2366                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
2367             } else {
2368             if (data.copy) {
2369                 this.store.insert(insertAt++, r.copy());
2370             } else {
2371                 data.source.isDirtyFlag = true;
2372                 r.store.remove(r);
2373                 this.store.insert(insertAt++, r);
2374             }
2375             this.isDirtyFlag = true;
2376             }
2377         }
2378         this.dragZone.cachedTarget = null;
2379         return true;
2380     },
2381     
2382     /**    Decide whether to drop above or below a View node. */
2383     getDropPoint : function(e, n, dd)
2384     {
2385         if (dd) {
2386              return false;
2387         }
2388         if (n == this.bodyEl.dom) {
2389             return "above";
2390         }
2391         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2392         var c = t + (b - t) / 2;
2393         var y = Roo.lib.Event.getPageY(e);
2394         if(y <= c) {
2395             return "above";
2396         }else{
2397             return "below";
2398         }
2399     },
2400     onToggleCollapse : function(e)
2401         {
2402         if (this.collapsed) {
2403             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2404             this.el.select('.roo-collapsable').addClass('show');
2405             this.collapsed = false;
2406             return;
2407         }
2408         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2409         this.el.select('.roo-collapsable').removeClass('show');
2410         this.collapsed = true;
2411         
2412     
2413     },
2414     dropPlaceHolder: function (action, where_ar, data)
2415     {
2416         if (this.dropEl === false) {
2417             this.dropEl = Roo.DomHelper.append(this.bodyEl, {
2418             cls : 'd-none'
2419             },true);
2420         }
2421         this.dropEl.removeClass(['d-none', 'd-block']);        
2422         if (action == 'hide') {
2423             
2424             this.dropEl.addClass('d-none');
2425             return;
2426         }
2427         var cardel = where_ar[0].el.dom;
2428         
2429         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2430         if (where_ar[1] == 'above') {
2431             cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2432         } else if (cardel.nextSibling) {
2433             cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2434         } else {
2435             cardel.parentNode.append(this.dropEl.dom);
2436         }
2437         this.dropEl.addClass('d-block roo-card-dropzone');
2438         
2439         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2440         
2441         
2442     
2443     
2444     
2445     }
2446
2447     
2448 });
2449
2450 /*
2451  * - LGPL
2452  *
2453  * Card header - holder for the card header elements.
2454  * 
2455  */
2456
2457 /**
2458  * @class Roo.bootstrap.CardHeader
2459  * @extends Roo.bootstrap.Element
2460  * Bootstrap CardHeader class
2461  * @constructor
2462  * Create a new Card Header - that you can embed children into
2463  * @param {Object} config The config object
2464  */
2465
2466 Roo.bootstrap.CardHeader = function(config){
2467     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2468 };
2469
2470 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2471     
2472     
2473     container_method : 'getCardHeader' 
2474     
2475      
2476     
2477     
2478    
2479 });
2480
2481  
2482
2483  /*
2484  * - LGPL
2485  *
2486  * Card header - holder for the card header elements.
2487  * 
2488  */
2489
2490 /**
2491  * @class Roo.bootstrap.CardImageTop
2492  * @extends Roo.bootstrap.Element
2493  * Bootstrap CardImageTop class
2494  * @constructor
2495  * Create a new Card Image Top container
2496  * @param {Object} config The config object
2497  */
2498
2499 Roo.bootstrap.CardImageTop = function(config){
2500     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2501 };
2502
2503 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2504     
2505    
2506     container_method : 'getCardImageTop' 
2507     
2508      
2509     
2510    
2511 });
2512
2513  
2514
2515  /*
2516  * - LGPL
2517  *
2518  * image
2519  * 
2520  */
2521
2522
2523 /**
2524  * @class Roo.bootstrap.Img
2525  * @extends Roo.bootstrap.Component
2526  * Bootstrap Img class
2527  * @cfg {Boolean} imgResponsive false | true
2528  * @cfg {String} border rounded | circle | thumbnail
2529  * @cfg {String} src image source
2530  * @cfg {String} alt image alternative text
2531  * @cfg {String} href a tag href
2532  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2533  * @cfg {String} xsUrl xs image source
2534  * @cfg {String} smUrl sm image source
2535  * @cfg {String} mdUrl md image source
2536  * @cfg {String} lgUrl lg image source
2537  * 
2538  * @constructor
2539  * Create a new Input
2540  * @param {Object} config The config object
2541  */
2542
2543 Roo.bootstrap.Img = function(config){
2544     Roo.bootstrap.Img.superclass.constructor.call(this, config);
2545     
2546     this.addEvents({
2547         // img events
2548         /**
2549          * @event click
2550          * The img click event for the img.
2551          * @param {Roo.EventObject} e
2552          */
2553         "click" : true
2554     });
2555 };
2556
2557 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
2558     
2559     imgResponsive: true,
2560     border: '',
2561     src: 'about:blank',
2562     href: false,
2563     target: false,
2564     xsUrl: '',
2565     smUrl: '',
2566     mdUrl: '',
2567     lgUrl: '',
2568
2569     getAutoCreate : function()
2570     {   
2571         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2572             return this.createSingleImg();
2573         }
2574         
2575         var cfg = {
2576             tag: 'div',
2577             cls: 'roo-image-responsive-group',
2578             cn: []
2579         };
2580         var _this = this;
2581         
2582         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2583             
2584             if(!_this[size + 'Url']){
2585                 return;
2586             }
2587             
2588             var img = {
2589                 tag: 'img',
2590                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2591                 html: _this.html || cfg.html,
2592                 src: _this[size + 'Url']
2593             };
2594             
2595             img.cls += ' roo-image-responsive-' + size;
2596             
2597             var s = ['xs', 'sm', 'md', 'lg'];
2598             
2599             s.splice(s.indexOf(size), 1);
2600             
2601             Roo.each(s, function(ss){
2602                 img.cls += ' hidden-' + ss;
2603             });
2604             
2605             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2606                 cfg.cls += ' img-' + _this.border;
2607             }
2608             
2609             if(_this.alt){
2610                 cfg.alt = _this.alt;
2611             }
2612             
2613             if(_this.href){
2614                 var a = {
2615                     tag: 'a',
2616                     href: _this.href,
2617                     cn: [
2618                         img
2619                     ]
2620                 };
2621
2622                 if(this.target){
2623                     a.target = _this.target;
2624                 }
2625             }
2626             
2627             cfg.cn.push((_this.href) ? a : img);
2628             
2629         });
2630         
2631         return cfg;
2632     },
2633     
2634     createSingleImg : function()
2635     {
2636         var cfg = {
2637             tag: 'img',
2638             cls: (this.imgResponsive) ? 'img-responsive' : '',
2639             html : null,
2640             src : 'about:blank'  // just incase src get's set to undefined?!?
2641         };
2642         
2643         cfg.html = this.html || cfg.html;
2644         
2645         cfg.src = this.src || cfg.src;
2646         
2647         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2648             cfg.cls += ' img-' + this.border;
2649         }
2650         
2651         if(this.alt){
2652             cfg.alt = this.alt;
2653         }
2654         
2655         if(this.href){
2656             var a = {
2657                 tag: 'a',
2658                 href: this.href,
2659                 cn: [
2660                     cfg
2661                 ]
2662             };
2663             
2664             if(this.target){
2665                 a.target = this.target;
2666             }
2667             
2668         }
2669         
2670         return (this.href) ? a : cfg;
2671     },
2672     
2673     initEvents: function() 
2674     {
2675         if(!this.href){
2676             this.el.on('click', this.onClick, this);
2677         }
2678         
2679     },
2680     
2681     onClick : function(e)
2682     {
2683         Roo.log('img onclick');
2684         this.fireEvent('click', this, e);
2685     },
2686     /**
2687      * Sets the url of the image - used to update it
2688      * @param {String} url the url of the image
2689      */
2690     
2691     setSrc : function(url)
2692     {
2693         this.src =  url;
2694         
2695         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2696             this.el.dom.src =  url;
2697             return;
2698         }
2699         
2700         this.el.select('img', true).first().dom.src =  url;
2701     }
2702     
2703     
2704    
2705 });
2706
2707  /*
2708  * - LGPL
2709  *
2710  * image
2711  * 
2712  */
2713
2714
2715 /**
2716  * @class Roo.bootstrap.Link
2717  * @extends Roo.bootstrap.Component
2718  * Bootstrap Link Class
2719  * @cfg {String} alt image alternative text
2720  * @cfg {String} href a tag href
2721  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
2722  * @cfg {String} html the content of the link.
2723  * @cfg {String} anchor name for the anchor link
2724  * @cfg {String} fa - favicon
2725
2726  * @cfg {Boolean} preventDefault (true | false) default false
2727
2728  * 
2729  * @constructor
2730  * Create a new Input
2731  * @param {Object} config The config object
2732  */
2733
2734 Roo.bootstrap.Link = function(config){
2735     Roo.bootstrap.Link.superclass.constructor.call(this, config);
2736     
2737     this.addEvents({
2738         // img events
2739         /**
2740          * @event click
2741          * The img click event for the img.
2742          * @param {Roo.EventObject} e
2743          */
2744         "click" : true
2745     });
2746 };
2747
2748 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
2749     
2750     href: false,
2751     target: false,
2752     preventDefault: false,
2753     anchor : false,
2754     alt : false,
2755     fa: false,
2756
2757
2758     getAutoCreate : function()
2759     {
2760         var html = this.html || '';
2761         
2762         if (this.fa !== false) {
2763             html = '<i class="fa fa-' + this.fa + '"></i>';
2764         }
2765         var cfg = {
2766             tag: 'a'
2767         };
2768         // anchor's do not require html/href...
2769         if (this.anchor === false) {
2770             cfg.html = html;
2771             cfg.href = this.href || '#';
2772         } else {
2773             cfg.name = this.anchor;
2774             if (this.html !== false || this.fa !== false) {
2775                 cfg.html = html;
2776             }
2777             if (this.href !== false) {
2778                 cfg.href = this.href;
2779             }
2780         }
2781         
2782         if(this.alt !== false){
2783             cfg.alt = this.alt;
2784         }
2785         
2786         
2787         if(this.target !== false) {
2788             cfg.target = this.target;
2789         }
2790         
2791         return cfg;
2792     },
2793     
2794     initEvents: function() {
2795         
2796         if(!this.href || this.preventDefault){
2797             this.el.on('click', this.onClick, this);
2798         }
2799     },
2800     
2801     onClick : function(e)
2802     {
2803         if(this.preventDefault){
2804             e.preventDefault();
2805         }
2806         //Roo.log('img onclick');
2807         this.fireEvent('click', this, e);
2808     }
2809    
2810 });
2811
2812  /*
2813  * - LGPL
2814  *
2815  * header
2816  * 
2817  */
2818
2819 /**
2820  * @class Roo.bootstrap.Header
2821  * @extends Roo.bootstrap.Component
2822  * Bootstrap Header class
2823  * @cfg {String} html content of header
2824  * @cfg {Number} level (1|2|3|4|5|6) default 1
2825  * 
2826  * @constructor
2827  * Create a new Header
2828  * @param {Object} config The config object
2829  */
2830
2831
2832 Roo.bootstrap.Header  = function(config){
2833     Roo.bootstrap.Header.superclass.constructor.call(this, config);
2834 };
2835
2836 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
2837     
2838     //href : false,
2839     html : false,
2840     level : 1,
2841     
2842     
2843     
2844     getAutoCreate : function(){
2845         
2846         
2847         
2848         var cfg = {
2849             tag: 'h' + (1 *this.level),
2850             html: this.html || ''
2851         } ;
2852         
2853         return cfg;
2854     }
2855    
2856 });
2857
2858  
2859
2860  /*
2861  * Based on:
2862  * Ext JS Library 1.1.1
2863  * Copyright(c) 2006-2007, Ext JS, LLC.
2864  *
2865  * Originally Released Under LGPL - original licence link has changed is not relivant.
2866  *
2867  * Fork - LGPL
2868  * <script type="text/javascript">
2869  */
2870  
2871 /**
2872  * @class Roo.bootstrap.MenuMgr
2873  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
2874  * @singleton
2875  */
2876 Roo.bootstrap.MenuMgr = function(){
2877    var menus, active, groups = {}, attached = false, lastShow = new Date();
2878
2879    // private - called when first menu is created
2880    function init(){
2881        menus = {};
2882        active = new Roo.util.MixedCollection();
2883        Roo.get(document).addKeyListener(27, function(){
2884            if(active.length > 0){
2885                hideAll();
2886            }
2887        });
2888    }
2889
2890    // private
2891    function hideAll(){
2892        if(active && active.length > 0){
2893            var c = active.clone();
2894            c.each(function(m){
2895                m.hide();
2896            });
2897        }
2898    }
2899
2900    // private
2901    function onHide(m){
2902        active.remove(m);
2903        if(active.length < 1){
2904            Roo.get(document).un("mouseup", onMouseDown);
2905             
2906            attached = false;
2907        }
2908    }
2909
2910    // private
2911    function onShow(m){
2912        var last = active.last();
2913        lastShow = new Date();
2914        active.add(m);
2915        if(!attached){
2916           Roo.get(document).on("mouseup", onMouseDown);
2917            
2918            attached = true;
2919        }
2920        if(m.parentMenu){
2921           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
2922           m.parentMenu.activeChild = m;
2923        }else if(last && last.isVisible()){
2924           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
2925        }
2926    }
2927
2928    // private
2929    function onBeforeHide(m){
2930        if(m.activeChild){
2931            m.activeChild.hide();
2932        }
2933        if(m.autoHideTimer){
2934            clearTimeout(m.autoHideTimer);
2935            delete m.autoHideTimer;
2936        }
2937    }
2938
2939    // private
2940    function onBeforeShow(m){
2941        var pm = m.parentMenu;
2942        if(!pm && !m.allowOtherMenus){
2943            hideAll();
2944        }else if(pm && pm.activeChild && active != m){
2945            pm.activeChild.hide();
2946        }
2947    }
2948
2949    // private this should really trigger on mouseup..
2950    function onMouseDown(e){
2951         Roo.log("on Mouse Up");
2952         
2953         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
2954             Roo.log("MenuManager hideAll");
2955             hideAll();
2956             e.stopEvent();
2957         }
2958         
2959         
2960    }
2961
2962    // private
2963    function onBeforeCheck(mi, state){
2964        if(state){
2965            var g = groups[mi.group];
2966            for(var i = 0, l = g.length; i < l; i++){
2967                if(g[i] != mi){
2968                    g[i].setChecked(false);
2969                }
2970            }
2971        }
2972    }
2973
2974    return {
2975
2976        /**
2977         * Hides all menus that are currently visible
2978         */
2979        hideAll : function(){
2980             hideAll();  
2981        },
2982
2983        // private
2984        register : function(menu){
2985            if(!menus){
2986                init();
2987            }
2988            menus[menu.id] = menu;
2989            menu.on("beforehide", onBeforeHide);
2990            menu.on("hide", onHide);
2991            menu.on("beforeshow", onBeforeShow);
2992            menu.on("show", onShow);
2993            var g = menu.group;
2994            if(g && menu.events["checkchange"]){
2995                if(!groups[g]){
2996                    groups[g] = [];
2997                }
2998                groups[g].push(menu);
2999                menu.on("checkchange", onCheck);
3000            }
3001        },
3002
3003         /**
3004          * Returns a {@link Roo.menu.Menu} object
3005          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3006          * be used to generate and return a new Menu instance.
3007          */
3008        get : function(menu){
3009            if(typeof menu == "string"){ // menu id
3010                return menus[menu];
3011            }else if(menu.events){  // menu instance
3012                return menu;
3013            }
3014            /*else if(typeof menu.length == 'number'){ // array of menu items?
3015                return new Roo.bootstrap.Menu({items:menu});
3016            }else{ // otherwise, must be a config
3017                return new Roo.bootstrap.Menu(menu);
3018            }
3019            */
3020            return false;
3021        },
3022
3023        // private
3024        unregister : function(menu){
3025            delete menus[menu.id];
3026            menu.un("beforehide", onBeforeHide);
3027            menu.un("hide", onHide);
3028            menu.un("beforeshow", onBeforeShow);
3029            menu.un("show", onShow);
3030            var g = menu.group;
3031            if(g && menu.events["checkchange"]){
3032                groups[g].remove(menu);
3033                menu.un("checkchange", onCheck);
3034            }
3035        },
3036
3037        // private
3038        registerCheckable : function(menuItem){
3039            var g = menuItem.group;
3040            if(g){
3041                if(!groups[g]){
3042                    groups[g] = [];
3043                }
3044                groups[g].push(menuItem);
3045                menuItem.on("beforecheckchange", onBeforeCheck);
3046            }
3047        },
3048
3049        // private
3050        unregisterCheckable : function(menuItem){
3051            var g = menuItem.group;
3052            if(g){
3053                groups[g].remove(menuItem);
3054                menuItem.un("beforecheckchange", onBeforeCheck);
3055            }
3056        }
3057    };
3058 }();/*
3059  * - LGPL
3060  *
3061  * menu
3062  * 
3063  */
3064
3065 /**
3066  * @class Roo.bootstrap.Menu
3067  * @extends Roo.bootstrap.Component
3068  * Bootstrap Menu class - container for MenuItems
3069  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3070  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3071  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3072  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3073  * 
3074  * @constructor
3075  * Create a new Menu
3076  * @param {Object} config The config object
3077  */
3078
3079
3080 Roo.bootstrap.Menu = function(config){
3081     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3082     if (this.registerMenu && this.type != 'treeview')  {
3083         Roo.bootstrap.MenuMgr.register(this);
3084     }
3085     
3086     
3087     this.addEvents({
3088         /**
3089          * @event beforeshow
3090          * Fires before this menu is displayed (return false to block)
3091          * @param {Roo.menu.Menu} this
3092          */
3093         beforeshow : true,
3094         /**
3095          * @event beforehide
3096          * Fires before this menu is hidden (return false to block)
3097          * @param {Roo.menu.Menu} this
3098          */
3099         beforehide : true,
3100         /**
3101          * @event show
3102          * Fires after this menu is displayed
3103          * @param {Roo.menu.Menu} this
3104          */
3105         show : true,
3106         /**
3107          * @event hide
3108          * Fires after this menu is hidden
3109          * @param {Roo.menu.Menu} this
3110          */
3111         hide : true,
3112         /**
3113          * @event click
3114          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3115          * @param {Roo.menu.Menu} this
3116          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3117          * @param {Roo.EventObject} e
3118          */
3119         click : true,
3120         /**
3121          * @event mouseover
3122          * Fires when the mouse is hovering over this menu
3123          * @param {Roo.menu.Menu} this
3124          * @param {Roo.EventObject} e
3125          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3126          */
3127         mouseover : true,
3128         /**
3129          * @event mouseout
3130          * Fires when the mouse exits this menu
3131          * @param {Roo.menu.Menu} this
3132          * @param {Roo.EventObject} e
3133          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3134          */
3135         mouseout : true,
3136         /**
3137          * @event itemclick
3138          * Fires when a menu item contained in this menu is clicked
3139          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3140          * @param {Roo.EventObject} e
3141          */
3142         itemclick: true
3143     });
3144     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3145 };
3146
3147 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3148     
3149    /// html : false,
3150     //align : '',
3151     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3152     type: false,
3153     /**
3154      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3155      */
3156     registerMenu : true,
3157     
3158     menuItems :false, // stores the menu items..
3159     
3160     hidden:true,
3161         
3162     parentMenu : false,
3163     
3164     stopEvent : true,
3165     
3166     isLink : false,
3167     
3168     getChildContainer : function() {
3169         return this.el;  
3170     },
3171     
3172     getAutoCreate : function(){
3173          
3174         //if (['right'].indexOf(this.align)!==-1) {
3175         //    cfg.cn[1].cls += ' pull-right'
3176         //}
3177         
3178         
3179         var cfg = {
3180             tag : 'ul',
3181             cls : 'dropdown-menu' ,
3182             style : 'z-index:1000'
3183             
3184         };
3185         
3186         if (this.type === 'submenu') {
3187             cfg.cls = 'submenu active';
3188         }
3189         if (this.type === 'treeview') {
3190             cfg.cls = 'treeview-menu';
3191         }
3192         
3193         return cfg;
3194     },
3195     initEvents : function() {
3196         
3197        // Roo.log("ADD event");
3198        // Roo.log(this.triggerEl.dom);
3199         
3200         this.triggerEl.on('click', this.onTriggerClick, this);
3201         
3202         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3203         
3204         
3205         if (this.triggerEl.hasClass('nav-item')) {
3206             // dropdown toggle on the 'a' in BS4?
3207             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3208         } else {
3209             this.triggerEl.addClass('dropdown-toggle');
3210         }
3211         if (Roo.isTouch) {
3212             this.el.on('touchstart'  , this.onTouch, this);
3213         }
3214         this.el.on('click' , this.onClick, this);
3215
3216         this.el.on("mouseover", this.onMouseOver, this);
3217         this.el.on("mouseout", this.onMouseOut, this);
3218         
3219     },
3220     
3221     findTargetItem : function(e)
3222     {
3223         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3224         if(!t){
3225             return false;
3226         }
3227         //Roo.log(t);         Roo.log(t.id);
3228         if(t && t.id){
3229             //Roo.log(this.menuitems);
3230             return this.menuitems.get(t.id);
3231             
3232             //return this.items.get(t.menuItemId);
3233         }
3234         
3235         return false;
3236     },
3237     
3238     onTouch : function(e) 
3239     {
3240         Roo.log("menu.onTouch");
3241         //e.stopEvent(); this make the user popdown broken
3242         this.onClick(e);
3243     },
3244     
3245     onClick : function(e)
3246     {
3247         Roo.log("menu.onClick");
3248         
3249         var t = this.findTargetItem(e);
3250         if(!t || t.isContainer){
3251             return;
3252         }
3253         Roo.log(e);
3254         /*
3255         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3256             if(t == this.activeItem && t.shouldDeactivate(e)){
3257                 this.activeItem.deactivate();
3258                 delete this.activeItem;
3259                 return;
3260             }
3261             if(t.canActivate){
3262                 this.setActiveItem(t, true);
3263             }
3264             return;
3265             
3266             
3267         }
3268         */
3269        
3270         Roo.log('pass click event');
3271         
3272         t.onClick(e);
3273         
3274         this.fireEvent("click", this, t, e);
3275         
3276         var _this = this;
3277         
3278         if(!t.href.length || t.href == '#'){
3279             (function() { _this.hide(); }).defer(100);
3280         }
3281         
3282     },
3283     
3284     onMouseOver : function(e){
3285         var t  = this.findTargetItem(e);
3286         //Roo.log(t);
3287         //if(t){
3288         //    if(t.canActivate && !t.disabled){
3289         //        this.setActiveItem(t, true);
3290         //    }
3291         //}
3292         
3293         this.fireEvent("mouseover", this, e, t);
3294     },
3295     isVisible : function(){
3296         return !this.hidden;
3297     },
3298     onMouseOut : function(e){
3299         var t  = this.findTargetItem(e);
3300         
3301         //if(t ){
3302         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3303         //        this.activeItem.deactivate();
3304         //        delete this.activeItem;
3305         //    }
3306         //}
3307         this.fireEvent("mouseout", this, e, t);
3308     },
3309     
3310     
3311     /**
3312      * Displays this menu relative to another element
3313      * @param {String/HTMLElement/Roo.Element} element The element to align to
3314      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3315      * the element (defaults to this.defaultAlign)
3316      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3317      */
3318     show : function(el, pos, parentMenu)
3319     {
3320         if (false === this.fireEvent("beforeshow", this)) {
3321             Roo.log("show canceled");
3322             return;
3323         }
3324         this.parentMenu = parentMenu;
3325         if(!this.el){
3326             this.render();
3327         }
3328         
3329         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3330     },
3331      /**
3332      * Displays this menu at a specific xy position
3333      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3334      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3335      */
3336     showAt : function(xy, parentMenu, /* private: */_e){
3337         this.parentMenu = parentMenu;
3338         if(!this.el){
3339             this.render();
3340         }
3341         if(_e !== false){
3342             this.fireEvent("beforeshow", this);
3343             //xy = this.el.adjustForConstraints(xy);
3344         }
3345         
3346         //this.el.show();
3347         this.hideMenuItems();
3348         this.hidden = false;
3349         this.triggerEl.addClass('open');
3350         this.el.addClass('show');
3351         
3352         // reassign x when hitting right
3353         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3354             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3355         }
3356         
3357         // reassign y when hitting bottom
3358         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3359             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3360         }
3361         
3362         // but the list may align on trigger left or trigger top... should it be a properity?
3363         
3364         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3365             this.el.setXY(xy);
3366         }
3367         
3368         this.focus();
3369         this.fireEvent("show", this);
3370     },
3371     
3372     focus : function(){
3373         return;
3374         if(!this.hidden){
3375             this.doFocus.defer(50, this);
3376         }
3377     },
3378
3379     doFocus : function(){
3380         if(!this.hidden){
3381             this.focusEl.focus();
3382         }
3383     },
3384
3385     /**
3386      * Hides this menu and optionally all parent menus
3387      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3388      */
3389     hide : function(deep)
3390     {
3391         if (false === this.fireEvent("beforehide", this)) {
3392             Roo.log("hide canceled");
3393             return;
3394         }
3395         this.hideMenuItems();
3396         if(this.el && this.isVisible()){
3397            
3398             if(this.activeItem){
3399                 this.activeItem.deactivate();
3400                 this.activeItem = null;
3401             }
3402             this.triggerEl.removeClass('open');;
3403             this.el.removeClass('show');
3404             this.hidden = true;
3405             this.fireEvent("hide", this);
3406         }
3407         if(deep === true && this.parentMenu){
3408             this.parentMenu.hide(true);
3409         }
3410     },
3411     
3412     onTriggerClick : function(e)
3413     {
3414         Roo.log('trigger click');
3415         
3416         var target = e.getTarget();
3417         
3418         Roo.log(target.nodeName.toLowerCase());
3419         
3420         if(target.nodeName.toLowerCase() === 'i'){
3421             e.preventDefault();
3422         }
3423         
3424     },
3425     
3426     onTriggerPress  : function(e)
3427     {
3428         Roo.log('trigger press');
3429         //Roo.log(e.getTarget());
3430        // Roo.log(this.triggerEl.dom);
3431        
3432         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3433         var pel = Roo.get(e.getTarget());
3434         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3435             Roo.log('is treeview or dropdown?');
3436             return;
3437         }
3438         
3439         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3440             return;
3441         }
3442         
3443         if (this.isVisible()) {
3444             Roo.log('hide');
3445             this.hide();
3446         } else {
3447             Roo.log('show');
3448             this.show(this.triggerEl, '?', false);
3449         }
3450         
3451         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3452             e.stopEvent();
3453         }
3454         
3455     },
3456        
3457     
3458     hideMenuItems : function()
3459     {
3460         Roo.log("hide Menu Items");
3461         if (!this.el) { 
3462             return;
3463         }
3464         
3465         this.el.select('.open',true).each(function(aa) {
3466             
3467             aa.removeClass('open');
3468          
3469         });
3470     },
3471     addxtypeChild : function (tree, cntr) {
3472         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3473           
3474         this.menuitems.add(comp);
3475         return comp;
3476
3477     },
3478     getEl : function()
3479     {
3480         Roo.log(this.el);
3481         return this.el;
3482     },
3483     
3484     clear : function()
3485     {
3486         this.getEl().dom.innerHTML = '';
3487         this.menuitems.clear();
3488     }
3489 });
3490
3491  
3492  /*
3493  * - LGPL
3494  *
3495  * menu item
3496  * 
3497  */
3498
3499
3500 /**
3501  * @class Roo.bootstrap.MenuItem
3502  * @extends Roo.bootstrap.Component
3503  * Bootstrap MenuItem class
3504  * @cfg {String} html the menu label
3505  * @cfg {String} href the link
3506  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3507  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3508  * @cfg {Boolean} active  used on sidebars to highlight active itesm
3509  * @cfg {String} fa favicon to show on left of menu item.
3510  * @cfg {Roo.bootsrap.Menu} menu the child menu.
3511  * 
3512  * 
3513  * @constructor
3514  * Create a new MenuItem
3515  * @param {Object} config The config object
3516  */
3517
3518
3519 Roo.bootstrap.MenuItem = function(config){
3520     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3521     this.addEvents({
3522         // raw events
3523         /**
3524          * @event click
3525          * The raw click event for the entire grid.
3526          * @param {Roo.bootstrap.MenuItem} this
3527          * @param {Roo.EventObject} e
3528          */
3529         "click" : true
3530     });
3531 };
3532
3533 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
3534     
3535     href : false,
3536     html : false,
3537     preventDefault: false,
3538     isContainer : false,
3539     active : false,
3540     fa: false,
3541     
3542     getAutoCreate : function(){
3543         
3544         if(this.isContainer){
3545             return {
3546                 tag: 'li',
3547                 cls: 'dropdown-menu-item '
3548             };
3549         }
3550         var ctag = {
3551             tag: 'span',
3552             html: 'Link'
3553         };
3554         
3555         var anc = {
3556             tag : 'a',
3557             cls : 'dropdown-item',
3558             href : '#',
3559             cn : [  ]
3560         };
3561         
3562         if (this.fa !== false) {
3563             anc.cn.push({
3564                 tag : 'i',
3565                 cls : 'fa fa-' + this.fa
3566             });
3567         }
3568         
3569         anc.cn.push(ctag);
3570         
3571         
3572         var cfg= {
3573             tag: 'li',
3574             cls: 'dropdown-menu-item',
3575             cn: [ anc ]
3576         };
3577         if (this.parent().type == 'treeview') {
3578             cfg.cls = 'treeview-menu';
3579         }
3580         if (this.active) {
3581             cfg.cls += ' active';
3582         }
3583         
3584         
3585         
3586         anc.href = this.href || cfg.cn[0].href ;
3587         ctag.html = this.html || cfg.cn[0].html ;
3588         return cfg;
3589     },
3590     
3591     initEvents: function()
3592     {
3593         if (this.parent().type == 'treeview') {
3594             this.el.select('a').on('click', this.onClick, this);
3595         }
3596         
3597         if (this.menu) {
3598             this.menu.parentType = this.xtype;
3599             this.menu.triggerEl = this.el;
3600             this.menu = this.addxtype(Roo.apply({}, this.menu));
3601         }
3602         
3603     },
3604     onClick : function(e)
3605     {
3606         Roo.log('item on click ');
3607         
3608         if(this.preventDefault){
3609             e.preventDefault();
3610         }
3611         //this.parent().hideMenuItems();
3612         
3613         this.fireEvent('click', this, e);
3614     },
3615     getEl : function()
3616     {
3617         return this.el;
3618     } 
3619 });
3620
3621  
3622
3623  /*
3624  * - LGPL
3625  *
3626  * menu separator
3627  * 
3628  */
3629
3630
3631 /**
3632  * @class Roo.bootstrap.MenuSeparator
3633  * @extends Roo.bootstrap.Component
3634  * Bootstrap MenuSeparator class
3635  * 
3636  * @constructor
3637  * Create a new MenuItem
3638  * @param {Object} config The config object
3639  */
3640
3641
3642 Roo.bootstrap.MenuSeparator = function(config){
3643     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3644 };
3645
3646 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
3647     
3648     getAutoCreate : function(){
3649         var cfg = {
3650             cls: 'divider',
3651             tag : 'li'
3652         };
3653         
3654         return cfg;
3655     }
3656    
3657 });
3658
3659  
3660
3661  
3662 /*
3663 * Licence: LGPL
3664 */
3665
3666 /**
3667  * @class Roo.bootstrap.Modal
3668  * @extends Roo.bootstrap.Component
3669  * Bootstrap Modal class
3670  * @cfg {String} title Title of dialog
3671  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3672  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
3673  * @cfg {Boolean} specificTitle default false
3674  * @cfg {Array} buttons Array of buttons or standard button set..
3675  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3676  * @cfg {Boolean} animate default true
3677  * @cfg {Boolean} allow_close default true
3678  * @cfg {Boolean} fitwindow default false
3679  * @cfg {Number} width fixed width - usefull for chrome extension only really.
3680  * @cfg {Number} height fixed height - usefull for chrome extension only really.
3681  * @cfg {String} size (sm|lg) default empty
3682  * @cfg {Number} max_width set the max width of modal
3683  *
3684  *
3685  * @constructor
3686  * Create a new Modal Dialog
3687  * @param {Object} config The config object
3688  */
3689
3690 Roo.bootstrap.Modal = function(config){
3691     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3692     this.addEvents({
3693         // raw events
3694         /**
3695          * @event btnclick
3696          * The raw btnclick event for the button
3697          * @param {Roo.EventObject} e
3698          */
3699         "btnclick" : true,
3700         /**
3701          * @event resize
3702          * Fire when dialog resize
3703          * @param {Roo.bootstrap.Modal} this
3704          * @param {Roo.EventObject} e
3705          */
3706         "resize" : true
3707     });
3708     this.buttons = this.buttons || [];
3709
3710     if (this.tmpl) {
3711         this.tmpl = Roo.factory(this.tmpl);
3712     }
3713
3714 };
3715
3716 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
3717
3718     title : 'test dialog',
3719
3720     buttons : false,
3721
3722     // set on load...
3723
3724     html: false,
3725
3726     tmp: false,
3727
3728     specificTitle: false,
3729
3730     buttonPosition: 'right',
3731
3732     allow_close : true,
3733
3734     animate : true,
3735
3736     fitwindow: false,
3737     
3738      // private
3739     dialogEl: false,
3740     bodyEl:  false,
3741     footerEl:  false,
3742     titleEl:  false,
3743     closeEl:  false,
3744
3745     size: '',
3746     
3747     max_width: 0,
3748     
3749     max_height: 0,
3750     
3751     fit_content: false,
3752
3753     onRender : function(ct, position)
3754     {
3755         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
3756
3757         if(!this.el){
3758             var cfg = Roo.apply({},  this.getAutoCreate());
3759             cfg.id = Roo.id();
3760             //if(!cfg.name){
3761             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
3762             //}
3763             //if (!cfg.name.length) {
3764             //    delete cfg.name;
3765            // }
3766             if (this.cls) {
3767                 cfg.cls += ' ' + this.cls;
3768             }
3769             if (this.style) {
3770                 cfg.style = this.style;
3771             }
3772             this.el = Roo.get(document.body).createChild(cfg, position);
3773         }
3774         //var type = this.el.dom.type;
3775
3776
3777         if(this.tabIndex !== undefined){
3778             this.el.dom.setAttribute('tabIndex', this.tabIndex);
3779         }
3780
3781         this.dialogEl = this.el.select('.modal-dialog',true).first();
3782         this.bodyEl = this.el.select('.modal-body',true).first();
3783         this.closeEl = this.el.select('.modal-header .close', true).first();
3784         this.headerEl = this.el.select('.modal-header',true).first();
3785         this.titleEl = this.el.select('.modal-title',true).first();
3786         this.footerEl = this.el.select('.modal-footer',true).first();
3787
3788         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
3789         
3790         //this.el.addClass("x-dlg-modal");
3791
3792         if (this.buttons.length) {
3793             Roo.each(this.buttons, function(bb) {
3794                 var b = Roo.apply({}, bb);
3795                 b.xns = b.xns || Roo.bootstrap;
3796                 b.xtype = b.xtype || 'Button';
3797                 if (typeof(b.listeners) == 'undefined') {
3798                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
3799                 }
3800
3801                 var btn = Roo.factory(b);
3802
3803                 btn.render(this.getButtonContainer());
3804
3805             },this);
3806         }
3807         // render the children.
3808         var nitems = [];
3809
3810         if(typeof(this.items) != 'undefined'){
3811             var items = this.items;
3812             delete this.items;
3813
3814             for(var i =0;i < items.length;i++) {
3815                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
3816             }
3817         }
3818
3819         this.items = nitems;
3820
3821         // where are these used - they used to be body/close/footer
3822
3823
3824         this.initEvents();
3825         //this.el.addClass([this.fieldClass, this.cls]);
3826
3827     },
3828
3829     getAutoCreate : function()
3830     {
3831         // we will default to modal-body-overflow - might need to remove or make optional later.
3832         var bdy = {
3833                 cls : 'modal-body enable-modal-body-overflow ', 
3834                 html : this.html || ''
3835         };
3836
3837         var title = {
3838             tag: 'h4',
3839             cls : 'modal-title',
3840             html : this.title
3841         };
3842
3843         if(this.specificTitle){
3844             title = this.title;
3845
3846         }
3847
3848         var header = [];
3849         if (this.allow_close && Roo.bootstrap.version == 3) {
3850             header.push({
3851                 tag: 'button',
3852                 cls : 'close',
3853                 html : '&times'
3854             });
3855         }
3856
3857         header.push(title);
3858
3859         if (this.allow_close && Roo.bootstrap.version == 4) {
3860             header.push({
3861                 tag: 'button',
3862                 cls : 'close',
3863                 html : '&times'
3864             });
3865         }
3866         
3867         var size = '';
3868
3869         if(this.size.length){
3870             size = 'modal-' + this.size;
3871         }
3872         
3873         var footer = Roo.bootstrap.version == 3 ?
3874             {
3875                 cls : 'modal-footer',
3876                 cn : [
3877                     {
3878                         tag: 'div',
3879                         cls: 'btn-' + this.buttonPosition
3880                     }
3881                 ]
3882
3883             } :
3884             {  // BS4 uses mr-auto on left buttons....
3885                 cls : 'modal-footer'
3886             };
3887
3888             
3889
3890         
3891         
3892         var modal = {
3893             cls: "modal",
3894              cn : [
3895                 {
3896                     cls: "modal-dialog " + size,
3897                     cn : [
3898                         {
3899                             cls : "modal-content",
3900                             cn : [
3901                                 {
3902                                     cls : 'modal-header',
3903                                     cn : header
3904                                 },
3905                                 bdy,
3906                                 footer
3907                             ]
3908
3909                         }
3910                     ]
3911
3912                 }
3913             ]
3914         };
3915
3916         if(this.animate){
3917             modal.cls += ' fade';
3918         }
3919
3920         return modal;
3921
3922     },
3923     getChildContainer : function() {
3924
3925          return this.bodyEl;
3926
3927     },
3928     getButtonContainer : function() {
3929         
3930          return Roo.bootstrap.version == 4 ?
3931             this.el.select('.modal-footer',true).first()
3932             : this.el.select('.modal-footer div',true).first();
3933
3934     },
3935     initEvents : function()
3936     {
3937         if (this.allow_close) {
3938             this.closeEl.on('click', this.hide, this);
3939         }
3940         Roo.EventManager.onWindowResize(this.resize, this, true);
3941
3942
3943     },
3944   
3945
3946     resize : function()
3947     {
3948         this.maskEl.setSize(
3949             Roo.lib.Dom.getViewWidth(true),
3950             Roo.lib.Dom.getViewHeight(true)
3951         );
3952         
3953         if (this.fitwindow) {
3954             
3955            
3956             this.setSize(
3957                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
3958                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
3959             );
3960             return;
3961         }
3962         
3963         if(this.max_width !== 0) {
3964             
3965             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
3966             
3967             if(this.height) {
3968                 this.setSize(w, this.height);
3969                 return;
3970             }
3971             
3972             if(this.max_height) {
3973                 this.setSize(w,Math.min(
3974                     this.max_height,
3975                     Roo.lib.Dom.getViewportHeight(true) - 60
3976                 ));
3977                 
3978                 return;
3979             }
3980             
3981             if(!this.fit_content) {
3982                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
3983                 return;
3984             }
3985             
3986             this.setSize(w, Math.min(
3987                 60 +
3988                 this.headerEl.getHeight() + 
3989                 this.footerEl.getHeight() + 
3990                 this.getChildHeight(this.bodyEl.dom.childNodes),
3991                 Roo.lib.Dom.getViewportHeight(true) - 60)
3992             );
3993         }
3994         
3995     },
3996
3997     setSize : function(w,h)
3998     {
3999         if (!w && !h) {
4000             return;
4001         }
4002         
4003         this.resizeTo(w,h);
4004     },
4005
4006     show : function() {
4007
4008         if (!this.rendered) {
4009             this.render();
4010         }
4011
4012         //this.el.setStyle('display', 'block');
4013         this.el.removeClass('hideing');
4014         this.el.dom.style.display='block';
4015         
4016         Roo.get(document.body).addClass('modal-open');
4017  
4018         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4019             
4020             (function(){
4021                 this.el.addClass('show');
4022                 this.el.addClass('in');
4023             }).defer(50, this);
4024         }else{
4025             this.el.addClass('show');
4026             this.el.addClass('in');
4027         }
4028
4029         // not sure how we can show data in here..
4030         //if (this.tmpl) {
4031         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4032         //}
4033
4034         Roo.get(document.body).addClass("x-body-masked");
4035         
4036         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4037         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4038         this.maskEl.dom.style.display = 'block';
4039         this.maskEl.addClass('show');
4040         
4041         
4042         this.resize();
4043         
4044         this.fireEvent('show', this);
4045
4046         // set zindex here - otherwise it appears to be ignored...
4047         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4048
4049         (function () {
4050             this.items.forEach( function(e) {
4051                 e.layout ? e.layout() : false;
4052
4053             });
4054         }).defer(100,this);
4055
4056     },
4057     hide : function()
4058     {
4059         if(this.fireEvent("beforehide", this) !== false){
4060             
4061             this.maskEl.removeClass('show');
4062             
4063             this.maskEl.dom.style.display = '';
4064             Roo.get(document.body).removeClass("x-body-masked");
4065             this.el.removeClass('in');
4066             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4067
4068             if(this.animate){ // why
4069                 this.el.addClass('hideing');
4070                 this.el.removeClass('show');
4071                 (function(){
4072                     if (!this.el.hasClass('hideing')) {
4073                         return; // it's been shown again...
4074                     }
4075                     
4076                     this.el.dom.style.display='';
4077
4078                     Roo.get(document.body).removeClass('modal-open');
4079                     this.el.removeClass('hideing');
4080                 }).defer(150,this);
4081                 
4082             }else{
4083                 this.el.removeClass('show');
4084                 this.el.dom.style.display='';
4085                 Roo.get(document.body).removeClass('modal-open');
4086
4087             }
4088             this.fireEvent('hide', this);
4089         }
4090     },
4091     isVisible : function()
4092     {
4093         
4094         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4095         
4096     },
4097
4098     addButton : function(str, cb)
4099     {
4100
4101
4102         var b = Roo.apply({}, { html : str } );
4103         b.xns = b.xns || Roo.bootstrap;
4104         b.xtype = b.xtype || 'Button';
4105         if (typeof(b.listeners) == 'undefined') {
4106             b.listeners = { click : cb.createDelegate(this)  };
4107         }
4108
4109         var btn = Roo.factory(b);
4110
4111         btn.render(this.getButtonContainer());
4112
4113         return btn;
4114
4115     },
4116
4117     setDefaultButton : function(btn)
4118     {
4119         //this.el.select('.modal-footer').()
4120     },
4121
4122     resizeTo: function(w,h)
4123     {
4124         this.dialogEl.setWidth(w);
4125         
4126         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4127
4128         this.bodyEl.setHeight(h - diff);
4129         
4130         this.fireEvent('resize', this);
4131     },
4132     
4133     setContentSize  : function(w, h)
4134     {
4135
4136     },
4137     onButtonClick: function(btn,e)
4138     {
4139         //Roo.log([a,b,c]);
4140         this.fireEvent('btnclick', btn.name, e);
4141     },
4142      /**
4143      * Set the title of the Dialog
4144      * @param {String} str new Title
4145      */
4146     setTitle: function(str) {
4147         this.titleEl.dom.innerHTML = str;
4148     },
4149     /**
4150      * Set the body of the Dialog
4151      * @param {String} str new Title
4152      */
4153     setBody: function(str) {
4154         this.bodyEl.dom.innerHTML = str;
4155     },
4156     /**
4157      * Set the body of the Dialog using the template
4158      * @param {Obj} data - apply this data to the template and replace the body contents.
4159      */
4160     applyBody: function(obj)
4161     {
4162         if (!this.tmpl) {
4163             Roo.log("Error - using apply Body without a template");
4164             //code
4165         }
4166         this.tmpl.overwrite(this.bodyEl, obj);
4167     },
4168     
4169     getChildHeight : function(child_nodes)
4170     {
4171         if(
4172             !child_nodes ||
4173             child_nodes.length == 0
4174         ) {
4175             return;
4176         }
4177         
4178         var child_height = 0;
4179         
4180         for(var i = 0; i < child_nodes.length; i++) {
4181             
4182             /*
4183             * for modal with tabs...
4184             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4185                 
4186                 var layout_childs = child_nodes[i].childNodes;
4187                 
4188                 for(var j = 0; j < layout_childs.length; j++) {
4189                     
4190                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4191                         
4192                         var layout_body_childs = layout_childs[j].childNodes;
4193                         
4194                         for(var k = 0; k < layout_body_childs.length; k++) {
4195                             
4196                             if(layout_body_childs[k].classList.contains('navbar')) {
4197                                 child_height += layout_body_childs[k].offsetHeight;
4198                                 continue;
4199                             }
4200                             
4201                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4202                                 
4203                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4204                                 
4205                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4206                                     
4207                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4208                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4209                                         continue;
4210                                     }
4211                                     
4212                                 }
4213                                 
4214                             }
4215                             
4216                         }
4217                     }
4218                 }
4219                 continue;
4220             }
4221             */
4222             
4223             child_height += child_nodes[i].offsetHeight;
4224             // Roo.log(child_nodes[i].offsetHeight);
4225         }
4226         
4227         return child_height;
4228     }
4229
4230 });
4231
4232
4233 Roo.apply(Roo.bootstrap.Modal,  {
4234     /**
4235          * Button config that displays a single OK button
4236          * @type Object
4237          */
4238         OK :  [{
4239             name : 'ok',
4240             weight : 'primary',
4241             html : 'OK'
4242         }],
4243         /**
4244          * Button config that displays Yes and No buttons
4245          * @type Object
4246          */
4247         YESNO : [
4248             {
4249                 name  : 'no',
4250                 html : 'No'
4251             },
4252             {
4253                 name  :'yes',
4254                 weight : 'primary',
4255                 html : 'Yes'
4256             }
4257         ],
4258
4259         /**
4260          * Button config that displays OK and Cancel buttons
4261          * @type Object
4262          */
4263         OKCANCEL : [
4264             {
4265                name : 'cancel',
4266                 html : 'Cancel'
4267             },
4268             {
4269                 name : 'ok',
4270                 weight : 'primary',
4271                 html : 'OK'
4272             }
4273         ],
4274         /**
4275          * Button config that displays Yes, No and Cancel buttons
4276          * @type Object
4277          */
4278         YESNOCANCEL : [
4279             {
4280                 name : 'yes',
4281                 weight : 'primary',
4282                 html : 'Yes'
4283             },
4284             {
4285                 name : 'no',
4286                 html : 'No'
4287             },
4288             {
4289                 name : 'cancel',
4290                 html : 'Cancel'
4291             }
4292         ],
4293         
4294         zIndex : 10001
4295 });
4296 /*
4297  * - LGPL
4298  *
4299  * messagebox - can be used as a replace
4300  * 
4301  */
4302 /**
4303  * @class Roo.MessageBox
4304  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4305  * Example usage:
4306  *<pre><code>
4307 // Basic alert:
4308 Roo.Msg.alert('Status', 'Changes saved successfully.');
4309
4310 // Prompt for user data:
4311 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4312     if (btn == 'ok'){
4313         // process text value...
4314     }
4315 });
4316
4317 // Show a dialog using config options:
4318 Roo.Msg.show({
4319    title:'Save Changes?',
4320    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4321    buttons: Roo.Msg.YESNOCANCEL,
4322    fn: processResult,
4323    animEl: 'elId'
4324 });
4325 </code></pre>
4326  * @singleton
4327  */
4328 Roo.bootstrap.MessageBox = function(){
4329     var dlg, opt, mask, waitTimer;
4330     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4331     var buttons, activeTextEl, bwidth;
4332
4333     
4334     // private
4335     var handleButton = function(button){
4336         dlg.hide();
4337         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4338     };
4339
4340     // private
4341     var handleHide = function(){
4342         if(opt && opt.cls){
4343             dlg.el.removeClass(opt.cls);
4344         }
4345         //if(waitTimer){
4346         //    Roo.TaskMgr.stop(waitTimer);
4347         //    waitTimer = null;
4348         //}
4349     };
4350
4351     // private
4352     var updateButtons = function(b){
4353         var width = 0;
4354         if(!b){
4355             buttons["ok"].hide();
4356             buttons["cancel"].hide();
4357             buttons["yes"].hide();
4358             buttons["no"].hide();
4359             dlg.footerEl.hide();
4360             
4361             return width;
4362         }
4363         dlg.footerEl.show();
4364         for(var k in buttons){
4365             if(typeof buttons[k] != "function"){
4366                 if(b[k]){
4367                     buttons[k].show();
4368                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4369                     width += buttons[k].el.getWidth()+15;
4370                 }else{
4371                     buttons[k].hide();
4372                 }
4373             }
4374         }
4375         return width;
4376     };
4377
4378     // private
4379     var handleEsc = function(d, k, e){
4380         if(opt && opt.closable !== false){
4381             dlg.hide();
4382         }
4383         if(e){
4384             e.stopEvent();
4385         }
4386     };
4387
4388     return {
4389         /**
4390          * Returns a reference to the underlying {@link Roo.BasicDialog} element
4391          * @return {Roo.BasicDialog} The BasicDialog element
4392          */
4393         getDialog : function(){
4394            if(!dlg){
4395                 dlg = new Roo.bootstrap.Modal( {
4396                     //draggable: true,
4397                     //resizable:false,
4398                     //constraintoviewport:false,
4399                     //fixedcenter:true,
4400                     //collapsible : false,
4401                     //shim:true,
4402                     //modal: true,
4403                 //    width: 'auto',
4404                   //  height:100,
4405                     //buttonAlign:"center",
4406                     closeClick : function(){
4407                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4408                             handleButton("no");
4409                         }else{
4410                             handleButton("cancel");
4411                         }
4412                     }
4413                 });
4414                 dlg.render();
4415                 dlg.on("hide", handleHide);
4416                 mask = dlg.mask;
4417                 //dlg.addKeyListener(27, handleEsc);
4418                 buttons = {};
4419                 this.buttons = buttons;
4420                 var bt = this.buttonText;
4421                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4422                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4423                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4424                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4425                 //Roo.log(buttons);
4426                 bodyEl = dlg.bodyEl.createChild({
4427
4428                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4429                         '<textarea class="roo-mb-textarea"></textarea>' +
4430                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
4431                 });
4432                 msgEl = bodyEl.dom.firstChild;
4433                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4434                 textboxEl.enableDisplayMode();
4435                 textboxEl.addKeyListener([10,13], function(){
4436                     if(dlg.isVisible() && opt && opt.buttons){
4437                         if(opt.buttons.ok){
4438                             handleButton("ok");
4439                         }else if(opt.buttons.yes){
4440                             handleButton("yes");
4441                         }
4442                     }
4443                 });
4444                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4445                 textareaEl.enableDisplayMode();
4446                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4447                 progressEl.enableDisplayMode();
4448                 
4449                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4450                 var pf = progressEl.dom.firstChild;
4451                 if (pf) {
4452                     pp = Roo.get(pf.firstChild);
4453                     pp.setHeight(pf.offsetHeight);
4454                 }
4455                 
4456             }
4457             return dlg;
4458         },
4459
4460         /**
4461          * Updates the message box body text
4462          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4463          * the XHTML-compliant non-breaking space character '&amp;#160;')
4464          * @return {Roo.MessageBox} This message box
4465          */
4466         updateText : function(text)
4467         {
4468             if(!dlg.isVisible() && !opt.width){
4469                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4470                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4471             }
4472             msgEl.innerHTML = text || '&#160;';
4473       
4474             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4475             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4476             var w = Math.max(
4477                     Math.min(opt.width || cw , this.maxWidth), 
4478                     Math.max(opt.minWidth || this.minWidth, bwidth)
4479             );
4480             if(opt.prompt){
4481                 activeTextEl.setWidth(w);
4482             }
4483             if(dlg.isVisible()){
4484                 dlg.fixedcenter = false;
4485             }
4486             // to big, make it scroll. = But as usual stupid IE does not support
4487             // !important..
4488             
4489             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4490                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4491                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4492             } else {
4493                 bodyEl.dom.style.height = '';
4494                 bodyEl.dom.style.overflowY = '';
4495             }
4496             if (cw > w) {
4497                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4498             } else {
4499                 bodyEl.dom.style.overflowX = '';
4500             }
4501             
4502             dlg.setContentSize(w, bodyEl.getHeight());
4503             if(dlg.isVisible()){
4504                 dlg.fixedcenter = true;
4505             }
4506             return this;
4507         },
4508
4509         /**
4510          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
4511          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4512          * @param {Number} value Any number between 0 and 1 (e.g., .5)
4513          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4514          * @return {Roo.MessageBox} This message box
4515          */
4516         updateProgress : function(value, text){
4517             if(text){
4518                 this.updateText(text);
4519             }
4520             
4521             if (pp) { // weird bug on my firefox - for some reason this is not defined
4522                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4523                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4524             }
4525             return this;
4526         },        
4527
4528         /**
4529          * Returns true if the message box is currently displayed
4530          * @return {Boolean} True if the message box is visible, else false
4531          */
4532         isVisible : function(){
4533             return dlg && dlg.isVisible();  
4534         },
4535
4536         /**
4537          * Hides the message box if it is displayed
4538          */
4539         hide : function(){
4540             if(this.isVisible()){
4541                 dlg.hide();
4542             }  
4543         },
4544
4545         /**
4546          * Displays a new message box, or reinitializes an existing message box, based on the config options
4547          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4548          * The following config object properties are supported:
4549          * <pre>
4550 Property    Type             Description
4551 ----------  ---------------  ------------------------------------------------------------------------------------
4552 animEl            String/Element   An id or Element from which the message box should animate as it opens and
4553                                    closes (defaults to undefined)
4554 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4555                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
4556 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
4557                                    progress and wait dialogs will ignore this property and always hide the
4558                                    close button as they can only be closed programmatically.
4559 cls               String           A custom CSS class to apply to the message box element
4560 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
4561                                    displayed (defaults to 75)
4562 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
4563                                    function will be btn (the name of the button that was clicked, if applicable,
4564                                    e.g. "ok"), and text (the value of the active text field, if applicable).
4565                                    Progress and wait dialogs will ignore this option since they do not respond to
4566                                    user actions and can only be closed programmatically, so any required function
4567                                    should be called by the same code after it closes the dialog.
4568 icon              String           A CSS class that provides a background image to be used as an icon for
4569                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4570 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
4571 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
4572 modal             Boolean          False to allow user interaction with the page while the message box is
4573                                    displayed (defaults to true)
4574 msg               String           A string that will replace the existing message box body text (defaults
4575                                    to the XHTML-compliant non-breaking space character '&#160;')
4576 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
4577 progress          Boolean          True to display a progress bar (defaults to false)
4578 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
4579 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
4580 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
4581 title             String           The title text
4582 value             String           The string value to set into the active textbox element if displayed
4583 wait              Boolean          True to display a progress bar (defaults to false)
4584 width             Number           The width of the dialog in pixels
4585 </pre>
4586          *
4587          * Example usage:
4588          * <pre><code>
4589 Roo.Msg.show({
4590    title: 'Address',
4591    msg: 'Please enter your address:',
4592    width: 300,
4593    buttons: Roo.MessageBox.OKCANCEL,
4594    multiline: true,
4595    fn: saveAddress,
4596    animEl: 'addAddressBtn'
4597 });
4598 </code></pre>
4599          * @param {Object} config Configuration options
4600          * @return {Roo.MessageBox} This message box
4601          */
4602         show : function(options)
4603         {
4604             
4605             // this causes nightmares if you show one dialog after another
4606             // especially on callbacks..
4607              
4608             if(this.isVisible()){
4609                 
4610                 this.hide();
4611                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4612                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
4613                 Roo.log("New Dialog Message:" +  options.msg )
4614                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4615                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4616                 
4617             }
4618             var d = this.getDialog();
4619             opt = options;
4620             d.setTitle(opt.title || "&#160;");
4621             d.closeEl.setDisplayed(opt.closable !== false);
4622             activeTextEl = textboxEl;
4623             opt.prompt = opt.prompt || (opt.multiline ? true : false);
4624             if(opt.prompt){
4625                 if(opt.multiline){
4626                     textboxEl.hide();
4627                     textareaEl.show();
4628                     textareaEl.setHeight(typeof opt.multiline == "number" ?
4629                         opt.multiline : this.defaultTextHeight);
4630                     activeTextEl = textareaEl;
4631                 }else{
4632                     textboxEl.show();
4633                     textareaEl.hide();
4634                 }
4635             }else{
4636                 textboxEl.hide();
4637                 textareaEl.hide();
4638             }
4639             progressEl.setDisplayed(opt.progress === true);
4640             if (opt.progress) {
4641                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4642             }
4643             this.updateProgress(0);
4644             activeTextEl.dom.value = opt.value || "";
4645             if(opt.prompt){
4646                 dlg.setDefaultButton(activeTextEl);
4647             }else{
4648                 var bs = opt.buttons;
4649                 var db = null;
4650                 if(bs && bs.ok){
4651                     db = buttons["ok"];
4652                 }else if(bs && bs.yes){
4653                     db = buttons["yes"];
4654                 }
4655                 dlg.setDefaultButton(db);
4656             }
4657             bwidth = updateButtons(opt.buttons);
4658             this.updateText(opt.msg);
4659             if(opt.cls){
4660                 d.el.addClass(opt.cls);
4661             }
4662             d.proxyDrag = opt.proxyDrag === true;
4663             d.modal = opt.modal !== false;
4664             d.mask = opt.modal !== false ? mask : false;
4665             if(!d.isVisible()){
4666                 // force it to the end of the z-index stack so it gets a cursor in FF
4667                 document.body.appendChild(dlg.el.dom);
4668                 d.animateTarget = null;
4669                 d.show(options.animEl);
4670             }
4671             return this;
4672         },
4673
4674         /**
4675          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
4676          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
4677          * and closing the message box when the process is complete.
4678          * @param {String} title The title bar text
4679          * @param {String} msg The message box body text
4680          * @return {Roo.MessageBox} This message box
4681          */
4682         progress : function(title, msg){
4683             this.show({
4684                 title : title,
4685                 msg : msg,
4686                 buttons: false,
4687                 progress:true,
4688                 closable:false,
4689                 minWidth: this.minProgressWidth,
4690                 modal : true
4691             });
4692             return this;
4693         },
4694
4695         /**
4696          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
4697          * If a callback function is passed it will be called after the user clicks the button, and the
4698          * id of the button that was clicked will be passed as the only parameter to the callback
4699          * (could also be the top-right close button).
4700          * @param {String} title The title bar text
4701          * @param {String} msg The message box body text
4702          * @param {Function} fn (optional) The callback function invoked after the message box is closed
4703          * @param {Object} scope (optional) The scope of the callback function
4704          * @return {Roo.MessageBox} This message box
4705          */
4706         alert : function(title, msg, fn, scope)
4707         {
4708             this.show({
4709                 title : title,
4710                 msg : msg,
4711                 buttons: this.OK,
4712                 fn: fn,
4713                 closable : false,
4714                 scope : scope,
4715                 modal : true
4716             });
4717             return this;
4718         },
4719
4720         /**
4721          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
4722          * interaction while waiting for a long-running process to complete that does not have defined intervals.
4723          * You are responsible for closing the message box when the process is complete.
4724          * @param {String} msg The message box body text
4725          * @param {String} title (optional) The title bar text
4726          * @return {Roo.MessageBox} This message box
4727          */
4728         wait : function(msg, title){
4729             this.show({
4730                 title : title,
4731                 msg : msg,
4732                 buttons: false,
4733                 closable:false,
4734                 progress:true,
4735                 modal:true,
4736                 width:300,
4737                 wait:true
4738             });
4739             waitTimer = Roo.TaskMgr.start({
4740                 run: function(i){
4741                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
4742                 },
4743                 interval: 1000
4744             });
4745             return this;
4746         },
4747
4748         /**
4749          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
4750          * If a callback function is passed it will be called after the user clicks either button, and the id of the
4751          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
4752          * @param {String} title The title bar text
4753          * @param {String} msg The message box body text
4754          * @param {Function} fn (optional) The callback function invoked after the message box is closed
4755          * @param {Object} scope (optional) The scope of the callback function
4756          * @return {Roo.MessageBox} This message box
4757          */
4758         confirm : function(title, msg, fn, scope){
4759             this.show({
4760                 title : title,
4761                 msg : msg,
4762                 buttons: this.YESNO,
4763                 fn: fn,
4764                 scope : scope,
4765                 modal : true
4766             });
4767             return this;
4768         },
4769
4770         /**
4771          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
4772          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
4773          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
4774          * (could also be the top-right close button) and the text that was entered will be passed as the two
4775          * parameters to the callback.
4776          * @param {String} title The title bar text
4777          * @param {String} msg The message box body text
4778          * @param {Function} fn (optional) The callback function invoked after the message box is closed
4779          * @param {Object} scope (optional) The scope of the callback function
4780          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
4781          * property, or the height in pixels to create the textbox (defaults to false / single-line)
4782          * @return {Roo.MessageBox} This message box
4783          */
4784         prompt : function(title, msg, fn, scope, multiline){
4785             this.show({
4786                 title : title,
4787                 msg : msg,
4788                 buttons: this.OKCANCEL,
4789                 fn: fn,
4790                 minWidth:250,
4791                 scope : scope,
4792                 prompt:true,
4793                 multiline: multiline,
4794                 modal : true
4795             });
4796             return this;
4797         },
4798
4799         /**
4800          * Button config that displays a single OK button
4801          * @type Object
4802          */
4803         OK : {ok:true},
4804         /**
4805          * Button config that displays Yes and No buttons
4806          * @type Object
4807          */
4808         YESNO : {yes:true, no:true},
4809         /**
4810          * Button config that displays OK and Cancel buttons
4811          * @type Object
4812          */
4813         OKCANCEL : {ok:true, cancel:true},
4814         /**
4815          * Button config that displays Yes, No and Cancel buttons
4816          * @type Object
4817          */
4818         YESNOCANCEL : {yes:true, no:true, cancel:true},
4819
4820         /**
4821          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
4822          * @type Number
4823          */
4824         defaultTextHeight : 75,
4825         /**
4826          * The maximum width in pixels of the message box (defaults to 600)
4827          * @type Number
4828          */
4829         maxWidth : 600,
4830         /**
4831          * The minimum width in pixels of the message box (defaults to 100)
4832          * @type Number
4833          */
4834         minWidth : 100,
4835         /**
4836          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
4837          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
4838          * @type Number
4839          */
4840         minProgressWidth : 250,
4841         /**
4842          * An object containing the default button text strings that can be overriden for localized language support.
4843          * Supported properties are: ok, cancel, yes and no.
4844          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
4845          * @type Object
4846          */
4847         buttonText : {
4848             ok : "OK",
4849             cancel : "Cancel",
4850             yes : "Yes",
4851             no : "No"
4852         }
4853     };
4854 }();
4855
4856 /**
4857  * Shorthand for {@link Roo.MessageBox}
4858  */
4859 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
4860 Roo.Msg = Roo.Msg || Roo.MessageBox;
4861 /*
4862  * - LGPL
4863  *
4864  * navbar
4865  * 
4866  */
4867
4868 /**
4869  * @class Roo.bootstrap.Navbar
4870  * @extends Roo.bootstrap.Component
4871  * Bootstrap Navbar class
4872
4873  * @constructor
4874  * Create a new Navbar
4875  * @param {Object} config The config object
4876  */
4877
4878
4879 Roo.bootstrap.Navbar = function(config){
4880     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
4881     this.addEvents({
4882         // raw events
4883         /**
4884          * @event beforetoggle
4885          * Fire before toggle the menu
4886          * @param {Roo.EventObject} e
4887          */
4888         "beforetoggle" : true
4889     });
4890 };
4891
4892 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
4893     
4894     
4895    
4896     // private
4897     navItems : false,
4898     loadMask : false,
4899     
4900     
4901     getAutoCreate : function(){
4902         
4903         
4904         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
4905         
4906     },
4907     
4908     initEvents :function ()
4909     {
4910         //Roo.log(this.el.select('.navbar-toggle',true));
4911         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
4912         
4913         var mark = {
4914             tag: "div",
4915             cls:"x-dlg-mask"
4916         };
4917         
4918         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
4919         
4920         var size = this.el.getSize();
4921         this.maskEl.setSize(size.width, size.height);
4922         this.maskEl.enableDisplayMode("block");
4923         this.maskEl.hide();
4924         
4925         if(this.loadMask){
4926             this.maskEl.show();
4927         }
4928     },
4929     
4930     
4931     getChildContainer : function()
4932     {
4933         if (this.el && this.el.select('.collapse').getCount()) {
4934             return this.el.select('.collapse',true).first();
4935         }
4936         
4937         return this.el;
4938     },
4939     
4940     mask : function()
4941     {
4942         this.maskEl.show();
4943     },
4944     
4945     unmask : function()
4946     {
4947         this.maskEl.hide();
4948     },
4949     onToggle : function()
4950     {
4951         
4952         if(this.fireEvent('beforetoggle', this) === false){
4953             return;
4954         }
4955         var ce = this.el.select('.navbar-collapse',true).first();
4956       
4957         if (!ce.hasClass('show')) {
4958            this.expand();
4959         } else {
4960             this.collapse();
4961         }
4962         
4963         
4964     
4965     },
4966     /**
4967      * Expand the navbar pulldown 
4968      */
4969     expand : function ()
4970     {
4971        
4972         var ce = this.el.select('.navbar-collapse',true).first();
4973         if (ce.hasClass('collapsing')) {
4974             return;
4975         }
4976         ce.dom.style.height = '';
4977                // show it...
4978         ce.addClass('in'); // old...
4979         ce.removeClass('collapse');
4980         ce.addClass('show');
4981         var h = ce.getHeight();
4982         Roo.log(h);
4983         ce.removeClass('show');
4984         // at this point we should be able to see it..
4985         ce.addClass('collapsing');
4986         
4987         ce.setHeight(0); // resize it ...
4988         ce.on('transitionend', function() {
4989             //Roo.log('done transition');
4990             ce.removeClass('collapsing');
4991             ce.addClass('show');
4992             ce.removeClass('collapse');
4993
4994             ce.dom.style.height = '';
4995         }, this, { single: true} );
4996         ce.setHeight(h);
4997         ce.dom.scrollTop = 0;
4998     },
4999     /**
5000      * Collapse the navbar pulldown 
5001      */
5002     collapse : function()
5003     {
5004          var ce = this.el.select('.navbar-collapse',true).first();
5005        
5006         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5007             // it's collapsed or collapsing..
5008             return;
5009         }
5010         ce.removeClass('in'); // old...
5011         ce.setHeight(ce.getHeight());
5012         ce.removeClass('show');
5013         ce.addClass('collapsing');
5014         
5015         ce.on('transitionend', function() {
5016             ce.dom.style.height = '';
5017             ce.removeClass('collapsing');
5018             ce.addClass('collapse');
5019         }, this, { single: true} );
5020         ce.setHeight(0);
5021     }
5022     
5023     
5024     
5025 });
5026
5027
5028
5029  
5030
5031  /*
5032  * - LGPL
5033  *
5034  * navbar
5035  * 
5036  */
5037
5038 /**
5039  * @class Roo.bootstrap.NavSimplebar
5040  * @extends Roo.bootstrap.Navbar
5041  * Bootstrap Sidebar class
5042  *
5043  * @cfg {Boolean} inverse is inverted color
5044  * 
5045  * @cfg {String} type (nav | pills | tabs)
5046  * @cfg {Boolean} arrangement stacked | justified
5047  * @cfg {String} align (left | right) alignment
5048  * 
5049  * @cfg {Boolean} main (true|false) main nav bar? default false
5050  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5051  * 
5052  * @cfg {String} tag (header|footer|nav|div) default is nav 
5053
5054  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5055  * 
5056  * 
5057  * @constructor
5058  * Create a new Sidebar
5059  * @param {Object} config The config object
5060  */
5061
5062
5063 Roo.bootstrap.NavSimplebar = function(config){
5064     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5065 };
5066
5067 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5068     
5069     inverse: false,
5070     
5071     type: false,
5072     arrangement: '',
5073     align : false,
5074     
5075     weight : 'light',
5076     
5077     main : false,
5078     
5079     
5080     tag : false,
5081     
5082     
5083     getAutoCreate : function(){
5084         
5085         
5086         var cfg = {
5087             tag : this.tag || 'div',
5088             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5089         };
5090         if (['light','white'].indexOf(this.weight) > -1) {
5091             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5092         }
5093         cfg.cls += ' bg-' + this.weight;
5094         
5095         if (this.inverse) {
5096             cfg.cls += ' navbar-inverse';
5097             
5098         }
5099         
5100         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5101         
5102         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5103             return cfg;
5104         }
5105         
5106         
5107     
5108         
5109         cfg.cn = [
5110             {
5111                 cls: 'nav nav-' + this.xtype,
5112                 tag : 'ul'
5113             }
5114         ];
5115         
5116          
5117         this.type = this.type || 'nav';
5118         if (['tabs','pills'].indexOf(this.type) != -1) {
5119             cfg.cn[0].cls += ' nav-' + this.type
5120         
5121         
5122         } else {
5123             if (this.type!=='nav') {
5124                 Roo.log('nav type must be nav/tabs/pills')
5125             }
5126             cfg.cn[0].cls += ' navbar-nav'
5127         }
5128         
5129         
5130         
5131         
5132         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5133             cfg.cn[0].cls += ' nav-' + this.arrangement;
5134         }
5135         
5136         
5137         if (this.align === 'right') {
5138             cfg.cn[0].cls += ' navbar-right';
5139         }
5140         
5141         
5142         
5143         
5144         return cfg;
5145     
5146         
5147     }
5148     
5149     
5150     
5151 });
5152
5153
5154
5155  
5156
5157  
5158        /*
5159  * - LGPL
5160  *
5161  * navbar
5162  * navbar-fixed-top
5163  * navbar-expand-md  fixed-top 
5164  */
5165
5166 /**
5167  * @class Roo.bootstrap.NavHeaderbar
5168  * @extends Roo.bootstrap.NavSimplebar
5169  * Bootstrap Sidebar class
5170  *
5171  * @cfg {String} brand what is brand
5172  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5173  * @cfg {String} brand_href href of the brand
5174  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5175  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5176  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5177  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5178  * 
5179  * @constructor
5180  * Create a new Sidebar
5181  * @param {Object} config The config object
5182  */
5183
5184
5185 Roo.bootstrap.NavHeaderbar = function(config){
5186     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5187       
5188 };
5189
5190 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5191     
5192     position: '',
5193     brand: '',
5194     brand_href: false,
5195     srButton : true,
5196     autohide : false,
5197     desktopCenter : false,
5198    
5199     
5200     getAutoCreate : function(){
5201         
5202         var   cfg = {
5203             tag: this.nav || 'nav',
5204             cls: 'navbar navbar-expand-md',
5205             role: 'navigation',
5206             cn: []
5207         };
5208         
5209         var cn = cfg.cn;
5210         if (this.desktopCenter) {
5211             cn.push({cls : 'container', cn : []});
5212             cn = cn[0].cn;
5213         }
5214         
5215         if(this.srButton){
5216             var btn = {
5217                 tag: 'button',
5218                 type: 'button',
5219                 cls: 'navbar-toggle navbar-toggler',
5220                 'data-toggle': 'collapse',
5221                 cn: [
5222                     {
5223                         tag: 'span',
5224                         cls: 'sr-only',
5225                         html: 'Toggle navigation'
5226                     },
5227                     {
5228                         tag: 'span',
5229                         cls: 'icon-bar navbar-toggler-icon'
5230                     },
5231                     {
5232                         tag: 'span',
5233                         cls: 'icon-bar'
5234                     },
5235                     {
5236                         tag: 'span',
5237                         cls: 'icon-bar'
5238                     }
5239                 ]
5240             };
5241             
5242             cn.push( Roo.bootstrap.version == 4 ? btn : {
5243                 tag: 'div',
5244                 cls: 'navbar-header',
5245                 cn: [
5246                     btn
5247                 ]
5248             });
5249         }
5250         
5251         cn.push({
5252             tag: 'div',
5253             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5254             cn : []
5255         });
5256         
5257         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5258         
5259         if (['light','white'].indexOf(this.weight) > -1) {
5260             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5261         }
5262         cfg.cls += ' bg-' + this.weight;
5263         
5264         
5265         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5266             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5267             
5268             // tag can override this..
5269             
5270             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5271         }
5272         
5273         if (this.brand !== '') {
5274             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5275             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5276                 tag: 'a',
5277                 href: this.brand_href ? this.brand_href : '#',
5278                 cls: 'navbar-brand',
5279                 cn: [
5280                 this.brand
5281                 ]
5282             });
5283         }
5284         
5285         if(this.main){
5286             cfg.cls += ' main-nav';
5287         }
5288         
5289         
5290         return cfg;
5291
5292         
5293     },
5294     getHeaderChildContainer : function()
5295     {
5296         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5297             return this.el.select('.navbar-header',true).first();
5298         }
5299         
5300         return this.getChildContainer();
5301     },
5302     
5303     getChildContainer : function()
5304     {
5305          
5306         return this.el.select('.roo-navbar-collapse',true).first();
5307          
5308         
5309     },
5310     
5311     initEvents : function()
5312     {
5313         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5314         
5315         if (this.autohide) {
5316             
5317             var prevScroll = 0;
5318             var ft = this.el;
5319             
5320             Roo.get(document).on('scroll',function(e) {
5321                 var ns = Roo.get(document).getScroll().top;
5322                 var os = prevScroll;
5323                 prevScroll = ns;
5324                 
5325                 if(ns > os){
5326                     ft.removeClass('slideDown');
5327                     ft.addClass('slideUp');
5328                     return;
5329                 }
5330                 ft.removeClass('slideUp');
5331                 ft.addClass('slideDown');
5332                  
5333               
5334           },this);
5335         }
5336     }    
5337     
5338 });
5339
5340
5341
5342  
5343
5344  /*
5345  * - LGPL
5346  *
5347  * navbar
5348  * 
5349  */
5350
5351 /**
5352  * @class Roo.bootstrap.NavSidebar
5353  * @extends Roo.bootstrap.Navbar
5354  * Bootstrap Sidebar class
5355  * 
5356  * @constructor
5357  * Create a new Sidebar
5358  * @param {Object} config The config object
5359  */
5360
5361
5362 Roo.bootstrap.NavSidebar = function(config){
5363     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5364 };
5365
5366 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
5367     
5368     sidebar : true, // used by Navbar Item and NavbarGroup at present...
5369     
5370     getAutoCreate : function(){
5371         
5372         
5373         return  {
5374             tag: 'div',
5375             cls: 'sidebar sidebar-nav'
5376         };
5377     
5378         
5379     }
5380     
5381     
5382     
5383 });
5384
5385
5386
5387  
5388
5389  /*
5390  * - LGPL
5391  *
5392  * nav group
5393  * 
5394  */
5395
5396 /**
5397  * @class Roo.bootstrap.NavGroup
5398  * @extends Roo.bootstrap.Component
5399  * Bootstrap NavGroup class
5400  * @cfg {String} align (left|right)
5401  * @cfg {Boolean} inverse
5402  * @cfg {String} type (nav|pills|tab) default nav
5403  * @cfg {String} navId - reference Id for navbar.
5404
5405  * 
5406  * @constructor
5407  * Create a new nav group
5408  * @param {Object} config The config object
5409  */
5410
5411 Roo.bootstrap.NavGroup = function(config){
5412     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5413     this.navItems = [];
5414    
5415     Roo.bootstrap.NavGroup.register(this);
5416      this.addEvents({
5417         /**
5418              * @event changed
5419              * Fires when the active item changes
5420              * @param {Roo.bootstrap.NavGroup} this
5421              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5422              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
5423          */
5424         'changed': true
5425      });
5426     
5427 };
5428
5429 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
5430     
5431     align: '',
5432     inverse: false,
5433     form: false,
5434     type: 'nav',
5435     navId : '',
5436     // private
5437     
5438     navItems : false, 
5439     
5440     getAutoCreate : function()
5441     {
5442         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5443         
5444         cfg = {
5445             tag : 'ul',
5446             cls: 'nav' 
5447         };
5448         if (Roo.bootstrap.version == 4) {
5449             if (['tabs','pills'].indexOf(this.type) != -1) {
5450                 cfg.cls += ' nav-' + this.type; 
5451             } else {
5452                 // trying to remove so header bar can right align top?
5453                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5454                     // do not use on header bar... 
5455                     cfg.cls += ' navbar-nav';
5456                 }
5457             }
5458             
5459         } else {
5460             if (['tabs','pills'].indexOf(this.type) != -1) {
5461                 cfg.cls += ' nav-' + this.type
5462             } else {
5463                 if (this.type !== 'nav') {
5464                     Roo.log('nav type must be nav/tabs/pills')
5465                 }
5466                 cfg.cls += ' navbar-nav'
5467             }
5468         }
5469         
5470         if (this.parent() && this.parent().sidebar) {
5471             cfg = {
5472                 tag: 'ul',
5473                 cls: 'dashboard-menu sidebar-menu'
5474             };
5475             
5476             return cfg;
5477         }
5478         
5479         if (this.form === true) {
5480             cfg = {
5481                 tag: 'form',
5482                 cls: 'navbar-form form-inline'
5483             };
5484             //nav navbar-right ml-md-auto
5485             if (this.align === 'right') {
5486                 cfg.cls += ' navbar-right ml-md-auto';
5487             } else {
5488                 cfg.cls += ' navbar-left';
5489             }
5490         }
5491         
5492         if (this.align === 'right') {
5493             cfg.cls += ' navbar-right ml-md-auto';
5494         } else {
5495             cfg.cls += ' mr-auto';
5496         }
5497         
5498         if (this.inverse) {
5499             cfg.cls += ' navbar-inverse';
5500             
5501         }
5502         
5503         
5504         return cfg;
5505     },
5506     /**
5507     * sets the active Navigation item
5508     * @param {Roo.bootstrap.NavItem} the new current navitem
5509     */
5510     setActiveItem : function(item)
5511     {
5512         var prev = false;
5513         Roo.each(this.navItems, function(v){
5514             if (v == item) {
5515                 return ;
5516             }
5517             if (v.isActive()) {
5518                 v.setActive(false, true);
5519                 prev = v;
5520                 
5521             }
5522             
5523         });
5524
5525         item.setActive(true, true);
5526         this.fireEvent('changed', this, item, prev);
5527         
5528         
5529     },
5530     /**
5531     * gets the active Navigation item
5532     * @return {Roo.bootstrap.NavItem} the current navitem
5533     */
5534     getActive : function()
5535     {
5536         
5537         var prev = false;
5538         Roo.each(this.navItems, function(v){
5539             
5540             if (v.isActive()) {
5541                 prev = v;
5542                 
5543             }
5544             
5545         });
5546         return prev;
5547     },
5548     
5549     indexOfNav : function()
5550     {
5551         
5552         var prev = false;
5553         Roo.each(this.navItems, function(v,i){
5554             
5555             if (v.isActive()) {
5556                 prev = i;
5557                 
5558             }
5559             
5560         });
5561         return prev;
5562     },
5563     /**
5564     * adds a Navigation item
5565     * @param {Roo.bootstrap.NavItem} the navitem to add
5566     */
5567     addItem : function(cfg)
5568     {
5569         if (this.form && Roo.bootstrap.version == 4) {
5570             cfg.tag = 'div';
5571         }
5572         var cn = new Roo.bootstrap.NavItem(cfg);
5573         this.register(cn);
5574         cn.parentId = this.id;
5575         cn.onRender(this.el, null);
5576         return cn;
5577     },
5578     /**
5579     * register a Navigation item
5580     * @param {Roo.bootstrap.NavItem} the navitem to add
5581     */
5582     register : function(item)
5583     {
5584         this.navItems.push( item);
5585         item.navId = this.navId;
5586     
5587     },
5588     
5589     /**
5590     * clear all the Navigation item
5591     */
5592    
5593     clearAll : function()
5594     {
5595         this.navItems = [];
5596         this.el.dom.innerHTML = '';
5597     },
5598     
5599     getNavItem: function(tabId)
5600     {
5601         var ret = false;
5602         Roo.each(this.navItems, function(e) {
5603             if (e.tabId == tabId) {
5604                ret =  e;
5605                return false;
5606             }
5607             return true;
5608             
5609         });
5610         return ret;
5611     },
5612     
5613     setActiveNext : function()
5614     {
5615         var i = this.indexOfNav(this.getActive());
5616         if (i > this.navItems.length) {
5617             return;
5618         }
5619         this.setActiveItem(this.navItems[i+1]);
5620     },
5621     setActivePrev : function()
5622     {
5623         var i = this.indexOfNav(this.getActive());
5624         if (i  < 1) {
5625             return;
5626         }
5627         this.setActiveItem(this.navItems[i-1]);
5628     },
5629     clearWasActive : function(except) {
5630         Roo.each(this.navItems, function(e) {
5631             if (e.tabId != except.tabId && e.was_active) {
5632                e.was_active = false;
5633                return false;
5634             }
5635             return true;
5636             
5637         });
5638     },
5639     getWasActive : function ()
5640     {
5641         var r = false;
5642         Roo.each(this.navItems, function(e) {
5643             if (e.was_active) {
5644                r = e;
5645                return false;
5646             }
5647             return true;
5648             
5649         });
5650         return r;
5651     }
5652     
5653     
5654 });
5655
5656  
5657 Roo.apply(Roo.bootstrap.NavGroup, {
5658     
5659     groups: {},
5660      /**
5661     * register a Navigation Group
5662     * @param {Roo.bootstrap.NavGroup} the navgroup to add
5663     */
5664     register : function(navgrp)
5665     {
5666         this.groups[navgrp.navId] = navgrp;
5667         
5668     },
5669     /**
5670     * fetch a Navigation Group based on the navigation ID
5671     * @param {string} the navgroup to add
5672     * @returns {Roo.bootstrap.NavGroup} the navgroup 
5673     */
5674     get: function(navId) {
5675         if (typeof(this.groups[navId]) == 'undefined') {
5676             return false;
5677             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
5678         }
5679         return this.groups[navId] ;
5680     }
5681     
5682     
5683     
5684 });
5685
5686  /*
5687  * - LGPL
5688  *
5689  * row
5690  * 
5691  */
5692
5693 /**
5694  * @class Roo.bootstrap.NavItem
5695  * @extends Roo.bootstrap.Component
5696  * Bootstrap Navbar.NavItem class
5697  * @cfg {String} href  link to
5698  * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
5699
5700  * @cfg {String} html content of button
5701  * @cfg {String} badge text inside badge
5702  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
5703  * @cfg {String} glyphicon DEPRICATED - use fa
5704  * @cfg {String} icon DEPRICATED - use fa
5705  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
5706  * @cfg {Boolean} active Is item active
5707  * @cfg {Boolean} disabled Is item disabled
5708  
5709  * @cfg {Boolean} preventDefault (true | false) default false
5710  * @cfg {String} tabId the tab that this item activates.
5711  * @cfg {String} tagtype (a|span) render as a href or span?
5712  * @cfg {Boolean} animateRef (true|false) link to element default false  
5713   
5714  * @constructor
5715  * Create a new Navbar Item
5716  * @param {Object} config The config object
5717  */
5718 Roo.bootstrap.NavItem = function(config){
5719     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
5720     this.addEvents({
5721         // raw events
5722         /**
5723          * @event click
5724          * The raw click event for the entire grid.
5725          * @param {Roo.EventObject} e
5726          */
5727         "click" : true,
5728          /**
5729             * @event changed
5730             * Fires when the active item active state changes
5731             * @param {Roo.bootstrap.NavItem} this
5732             * @param {boolean} state the new state
5733              
5734          */
5735         'changed': true,
5736         /**
5737             * @event scrollto
5738             * Fires when scroll to element
5739             * @param {Roo.bootstrap.NavItem} this
5740             * @param {Object} options
5741             * @param {Roo.EventObject} e
5742              
5743          */
5744         'scrollto': true
5745     });
5746    
5747 };
5748
5749 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
5750     
5751     href: false,
5752     html: '',
5753     badge: '',
5754     icon: false,
5755     fa : false,
5756     glyphicon: false,
5757     active: false,
5758     preventDefault : false,
5759     tabId : false,
5760     tagtype : 'a',
5761     tag: 'li',
5762     disabled : false,
5763     animateRef : false,
5764     was_active : false,
5765     button_weight : '',
5766     button_outline : false,
5767     
5768     navLink: false,
5769     
5770     getAutoCreate : function(){
5771          
5772         var cfg = {
5773             tag: this.tag,
5774             cls: 'nav-item'
5775         };
5776         
5777         if (this.active) {
5778             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
5779         }
5780         if (this.disabled) {
5781             cfg.cls += ' disabled';
5782         }
5783         
5784         // BS4 only?
5785         if (this.button_weight.length) {
5786             cfg.tag = this.href ? 'a' : 'button';
5787             cfg.html = this.html || '';
5788             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
5789             if (this.href) {
5790                 cfg.href = this.href;
5791             }
5792             if (this.fa) {
5793                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
5794             }
5795             
5796             // menu .. should add dropdown-menu class - so no need for carat..
5797             
5798             if (this.badge !== '') {
5799                  
5800                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
5801             }
5802             return cfg;
5803         }
5804         
5805         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
5806             cfg.cn = [
5807                 {
5808                     tag: this.tagtype,
5809                     href : this.href || "#",
5810                     html: this.html || ''
5811                 }
5812             ];
5813             if (this.tagtype == 'a') {
5814                 cfg.cn[0].cls = 'nav-link';
5815             }
5816             if (this.icon) {
5817                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
5818             }
5819             if (this.fa) {
5820                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
5821             }
5822             if(this.glyphicon) {
5823                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
5824             }
5825             
5826             if (this.menu) {
5827                 
5828                 cfg.cn[0].html += " <span class='caret'></span>";
5829              
5830             }
5831             
5832             if (this.badge !== '') {
5833                  
5834                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
5835             }
5836         }
5837         
5838         
5839         
5840         return cfg;
5841     },
5842     onRender : function(ct, position)
5843     {
5844        // Roo.log("Call onRender: " + this.xtype);
5845         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
5846             this.tag = 'div';
5847         }
5848         
5849         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
5850         this.navLink = this.el.select('.nav-link',true).first();
5851         return ret;
5852     },
5853       
5854     
5855     initEvents: function() 
5856     {
5857         if (typeof (this.menu) != 'undefined') {
5858             this.menu.parentType = this.xtype;
5859             this.menu.triggerEl = this.el;
5860             this.menu = this.addxtype(Roo.apply({}, this.menu));
5861         }
5862         
5863         this.el.select('a',true).on('click', this.onClick, this);
5864         
5865         if(this.tagtype == 'span'){
5866             this.el.select('span',true).on('click', this.onClick, this);
5867         }
5868        
5869         // at this point parent should be available..
5870         this.parent().register(this);
5871     },
5872     
5873     onClick : function(e)
5874     {
5875         if (e.getTarget('.dropdown-menu-item')) {
5876             // did you click on a menu itemm.... - then don't trigger onclick..
5877             return;
5878         }
5879         
5880         if(
5881                 this.preventDefault || 
5882                 this.href == '#' 
5883         ){
5884             Roo.log("NavItem - prevent Default?");
5885             e.preventDefault();
5886         }
5887         
5888         if (this.disabled) {
5889             return;
5890         }
5891         
5892         var tg = Roo.bootstrap.TabGroup.get(this.navId);
5893         if (tg && tg.transition) {
5894             Roo.log("waiting for the transitionend");
5895             return;
5896         }
5897         
5898         
5899         
5900         //Roo.log("fire event clicked");
5901         if(this.fireEvent('click', this, e) === false){
5902             return;
5903         };
5904         
5905         if(this.tagtype == 'span'){
5906             return;
5907         }
5908         
5909         //Roo.log(this.href);
5910         var ael = this.el.select('a',true).first();
5911         //Roo.log(ael);
5912         
5913         if(ael && this.animateRef && this.href.indexOf('#') > -1){
5914             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
5915             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
5916                 return; // ignore... - it's a 'hash' to another page.
5917             }
5918             Roo.log("NavItem - prevent Default?");
5919             e.preventDefault();
5920             this.scrollToElement(e);
5921         }
5922         
5923         
5924         var p =  this.parent();
5925    
5926         if (['tabs','pills'].indexOf(p.type)!==-1) {
5927             if (typeof(p.setActiveItem) !== 'undefined') {
5928                 p.setActiveItem(this);
5929             }
5930         }
5931         
5932         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
5933         if (p.parentType == 'NavHeaderbar' && !this.menu) {
5934             // remove the collapsed menu expand...
5935             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
5936         }
5937     },
5938     
5939     isActive: function () {
5940         return this.active
5941     },
5942     setActive : function(state, fire, is_was_active)
5943     {
5944         if (this.active && !state && this.navId) {
5945             this.was_active = true;
5946             var nv = Roo.bootstrap.NavGroup.get(this.navId);
5947             if (nv) {
5948                 nv.clearWasActive(this);
5949             }
5950             
5951         }
5952         this.active = state;
5953         
5954         if (!state ) {
5955             this.el.removeClass('active');
5956             this.navLink ? this.navLink.removeClass('active') : false;
5957         } else if (!this.el.hasClass('active')) {
5958             
5959             this.el.addClass('active');
5960             if (Roo.bootstrap.version == 4 && this.navLink ) {
5961                 this.navLink.addClass('active');
5962             }
5963             
5964         }
5965         if (fire) {
5966             this.fireEvent('changed', this, state);
5967         }
5968         
5969         // show a panel if it's registered and related..
5970         
5971         if (!this.navId || !this.tabId || !state || is_was_active) {
5972             return;
5973         }
5974         
5975         var tg = Roo.bootstrap.TabGroup.get(this.navId);
5976         if (!tg) {
5977             return;
5978         }
5979         var pan = tg.getPanelByName(this.tabId);
5980         if (!pan) {
5981             return;
5982         }
5983         // if we can not flip to new panel - go back to old nav highlight..
5984         if (false == tg.showPanel(pan)) {
5985             var nv = Roo.bootstrap.NavGroup.get(this.navId);
5986             if (nv) {
5987                 var onav = nv.getWasActive();
5988                 if (onav) {
5989                     onav.setActive(true, false, true);
5990                 }
5991             }
5992             
5993         }
5994         
5995         
5996         
5997     },
5998      // this should not be here...
5999     setDisabled : function(state)
6000     {
6001         this.disabled = state;
6002         if (!state ) {
6003             this.el.removeClass('disabled');
6004         } else if (!this.el.hasClass('disabled')) {
6005             this.el.addClass('disabled');
6006         }
6007         
6008     },
6009     
6010     /**
6011      * Fetch the element to display the tooltip on.
6012      * @return {Roo.Element} defaults to this.el
6013      */
6014     tooltipEl : function()
6015     {
6016         return this.el.select('' + this.tagtype + '', true).first();
6017     },
6018     
6019     scrollToElement : function(e)
6020     {
6021         var c = document.body;
6022         
6023         /*
6024          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6025          */
6026         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6027             c = document.documentElement;
6028         }
6029         
6030         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6031         
6032         if(!target){
6033             return;
6034         }
6035
6036         var o = target.calcOffsetsTo(c);
6037         
6038         var options = {
6039             target : target,
6040             value : o[1]
6041         };
6042         
6043         this.fireEvent('scrollto', this, options, e);
6044         
6045         Roo.get(c).scrollTo('top', options.value, true);
6046         
6047         return;
6048     }
6049 });
6050  
6051
6052  /*
6053  * - LGPL
6054  *
6055  * sidebar item
6056  *
6057  *  li
6058  *    <span> icon </span>
6059  *    <span> text </span>
6060  *    <span>badge </span>
6061  */
6062
6063 /**
6064  * @class Roo.bootstrap.NavSidebarItem
6065  * @extends Roo.bootstrap.NavItem
6066  * Bootstrap Navbar.NavSidebarItem class
6067  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6068  * {Boolean} open is the menu open
6069  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6070  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6071  * {String} buttonSize (sm|md|lg)the extra classes for the button
6072  * {Boolean} showArrow show arrow next to the text (default true)
6073  * @constructor
6074  * Create a new Navbar Button
6075  * @param {Object} config The config object
6076  */
6077 Roo.bootstrap.NavSidebarItem = function(config){
6078     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6079     this.addEvents({
6080         // raw events
6081         /**
6082          * @event click
6083          * The raw click event for the entire grid.
6084          * @param {Roo.EventObject} e
6085          */
6086         "click" : true,
6087          /**
6088             * @event changed
6089             * Fires when the active item active state changes
6090             * @param {Roo.bootstrap.NavSidebarItem} this
6091             * @param {boolean} state the new state
6092              
6093          */
6094         'changed': true
6095     });
6096    
6097 };
6098
6099 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6100     
6101     badgeWeight : 'default',
6102     
6103     open: false,
6104     
6105     buttonView : false,
6106     
6107     buttonWeight : 'default',
6108     
6109     buttonSize : 'md',
6110     
6111     showArrow : true,
6112     
6113     getAutoCreate : function(){
6114         
6115         
6116         var a = {
6117                 tag: 'a',
6118                 href : this.href || '#',
6119                 cls: '',
6120                 html : '',
6121                 cn : []
6122         };
6123         
6124         if(this.buttonView){
6125             a = {
6126                 tag: 'button',
6127                 href : this.href || '#',
6128                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6129                 html : this.html,
6130                 cn : []
6131             };
6132         }
6133         
6134         var cfg = {
6135             tag: 'li',
6136             cls: '',
6137             cn: [ a ]
6138         };
6139         
6140         if (this.active) {
6141             cfg.cls += ' active';
6142         }
6143         
6144         if (this.disabled) {
6145             cfg.cls += ' disabled';
6146         }
6147         if (this.open) {
6148             cfg.cls += ' open x-open';
6149         }
6150         // left icon..
6151         if (this.glyphicon || this.icon) {
6152             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6153             a.cn.push({ tag : 'i', cls : c }) ;
6154         }
6155         
6156         if(!this.buttonView){
6157             var span = {
6158                 tag: 'span',
6159                 html : this.html || ''
6160             };
6161
6162             a.cn.push(span);
6163             
6164         }
6165         
6166         if (this.badge !== '') {
6167             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6168         }
6169         
6170         if (this.menu) {
6171             
6172             if(this.showArrow){
6173                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6174             }
6175             
6176             a.cls += ' dropdown-toggle treeview' ;
6177         }
6178         
6179         return cfg;
6180     },
6181     
6182     initEvents : function()
6183     { 
6184         if (typeof (this.menu) != 'undefined') {
6185             this.menu.parentType = this.xtype;
6186             this.menu.triggerEl = this.el;
6187             this.menu = this.addxtype(Roo.apply({}, this.menu));
6188         }
6189         
6190         this.el.on('click', this.onClick, this);
6191         
6192         if(this.badge !== ''){
6193             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6194         }
6195         
6196     },
6197     
6198     onClick : function(e)
6199     {
6200         if(this.disabled){
6201             e.preventDefault();
6202             return;
6203         }
6204         
6205         if(this.preventDefault){
6206             e.preventDefault();
6207         }
6208         
6209         this.fireEvent('click', this, e);
6210     },
6211     
6212     disable : function()
6213     {
6214         this.setDisabled(true);
6215     },
6216     
6217     enable : function()
6218     {
6219         this.setDisabled(false);
6220     },
6221     
6222     setDisabled : function(state)
6223     {
6224         if(this.disabled == state){
6225             return;
6226         }
6227         
6228         this.disabled = state;
6229         
6230         if (state) {
6231             this.el.addClass('disabled');
6232             return;
6233         }
6234         
6235         this.el.removeClass('disabled');
6236         
6237         return;
6238     },
6239     
6240     setActive : function(state)
6241     {
6242         if(this.active == state){
6243             return;
6244         }
6245         
6246         this.active = state;
6247         
6248         if (state) {
6249             this.el.addClass('active');
6250             return;
6251         }
6252         
6253         this.el.removeClass('active');
6254         
6255         return;
6256     },
6257     
6258     isActive: function () 
6259     {
6260         return this.active;
6261     },
6262     
6263     setBadge : function(str)
6264     {
6265         if(!this.badgeEl){
6266             return;
6267         }
6268         
6269         this.badgeEl.dom.innerHTML = str;
6270     }
6271     
6272    
6273      
6274  
6275 });
6276  
6277
6278  /*
6279  * - LGPL
6280  *
6281  * row
6282  * 
6283  */
6284
6285 /**
6286  * @class Roo.bootstrap.Row
6287  * @extends Roo.bootstrap.Component
6288  * Bootstrap Row class (contains columns...)
6289  * 
6290  * @constructor
6291  * Create a new Row
6292  * @param {Object} config The config object
6293  */
6294
6295 Roo.bootstrap.Row = function(config){
6296     Roo.bootstrap.Row.superclass.constructor.call(this, config);
6297 };
6298
6299 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
6300     
6301     getAutoCreate : function(){
6302        return {
6303             cls: 'row clearfix'
6304        };
6305     }
6306     
6307     
6308 });
6309
6310  
6311
6312  /*
6313  * - LGPL
6314  *
6315  * pagination
6316  * 
6317  */
6318
6319 /**
6320  * @class Roo.bootstrap.Pagination
6321  * @extends Roo.bootstrap.Component
6322  * Bootstrap Pagination class
6323  * @cfg {String} size xs | sm | md | lg
6324  * @cfg {Boolean} inverse false | true
6325  * 
6326  * @constructor
6327  * Create a new Pagination
6328  * @param {Object} config The config object
6329  */
6330
6331 Roo.bootstrap.Pagination = function(config){
6332     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6333 };
6334
6335 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
6336     
6337     cls: false,
6338     size: false,
6339     inverse: false,
6340     
6341     getAutoCreate : function(){
6342         var cfg = {
6343             tag: 'ul',
6344                 cls: 'pagination'
6345         };
6346         if (this.inverse) {
6347             cfg.cls += ' inverse';
6348         }
6349         if (this.html) {
6350             cfg.html=this.html;
6351         }
6352         if (this.cls) {
6353             cfg.cls += " " + this.cls;
6354         }
6355         return cfg;
6356     }
6357    
6358 });
6359
6360  
6361
6362  /*
6363  * - LGPL
6364  *
6365  * Pagination item
6366  * 
6367  */
6368
6369
6370 /**
6371  * @class Roo.bootstrap.PaginationItem
6372  * @extends Roo.bootstrap.Component
6373  * Bootstrap PaginationItem class
6374  * @cfg {String} html text
6375  * @cfg {String} href the link
6376  * @cfg {Boolean} preventDefault (true | false) default true
6377  * @cfg {Boolean} active (true | false) default false
6378  * @cfg {Boolean} disabled default false
6379  * 
6380  * 
6381  * @constructor
6382  * Create a new PaginationItem
6383  * @param {Object} config The config object
6384  */
6385
6386
6387 Roo.bootstrap.PaginationItem = function(config){
6388     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6389     this.addEvents({
6390         // raw events
6391         /**
6392          * @event click
6393          * The raw click event for the entire grid.
6394          * @param {Roo.EventObject} e
6395          */
6396         "click" : true
6397     });
6398 };
6399
6400 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
6401     
6402     href : false,
6403     html : false,
6404     preventDefault: true,
6405     active : false,
6406     cls : false,
6407     disabled: false,
6408     
6409     getAutoCreate : function(){
6410         var cfg= {
6411             tag: 'li',
6412             cn: [
6413                 {
6414                     tag : 'a',
6415                     href : this.href ? this.href : '#',
6416                     html : this.html ? this.html : ''
6417                 }
6418             ]
6419         };
6420         
6421         if(this.cls){
6422             cfg.cls = this.cls;
6423         }
6424         
6425         if(this.disabled){
6426             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6427         }
6428         
6429         if(this.active){
6430             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6431         }
6432         
6433         return cfg;
6434     },
6435     
6436     initEvents: function() {
6437         
6438         this.el.on('click', this.onClick, this);
6439         
6440     },
6441     onClick : function(e)
6442     {
6443         Roo.log('PaginationItem on click ');
6444         if(this.preventDefault){
6445             e.preventDefault();
6446         }
6447         
6448         if(this.disabled){
6449             return;
6450         }
6451         
6452         this.fireEvent('click', this, e);
6453     }
6454    
6455 });
6456
6457  
6458
6459  /*
6460  * - LGPL
6461  *
6462  * slider
6463  * 
6464  */
6465
6466
6467 /**
6468  * @class Roo.bootstrap.Slider
6469  * @extends Roo.bootstrap.Component
6470  * Bootstrap Slider class
6471  *    
6472  * @constructor
6473  * Create a new Slider
6474  * @param {Object} config The config object
6475  */
6476
6477 Roo.bootstrap.Slider = function(config){
6478     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6479 };
6480
6481 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
6482     
6483     getAutoCreate : function(){
6484         
6485         var cfg = {
6486             tag: 'div',
6487             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6488             cn: [
6489                 {
6490                     tag: 'a',
6491                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
6492                 }
6493             ]
6494         };
6495         
6496         return cfg;
6497     }
6498    
6499 });
6500
6501  /*
6502  * Based on:
6503  * Ext JS Library 1.1.1
6504  * Copyright(c) 2006-2007, Ext JS, LLC.
6505  *
6506  * Originally Released Under LGPL - original licence link has changed is not relivant.
6507  *
6508  * Fork - LGPL
6509  * <script type="text/javascript">
6510  */
6511  
6512
6513 /**
6514  * @class Roo.grid.ColumnModel
6515  * @extends Roo.util.Observable
6516  * This is the default implementation of a ColumnModel used by the Grid. It defines
6517  * the columns in the grid.
6518  * <br>Usage:<br>
6519  <pre><code>
6520  var colModel = new Roo.grid.ColumnModel([
6521         {header: "Ticker", width: 60, sortable: true, locked: true},
6522         {header: "Company Name", width: 150, sortable: true},
6523         {header: "Market Cap.", width: 100, sortable: true},
6524         {header: "$ Sales", width: 100, sortable: true, renderer: money},
6525         {header: "Employees", width: 100, sortable: true, resizable: false}
6526  ]);
6527  </code></pre>
6528  * <p>
6529  
6530  * The config options listed for this class are options which may appear in each
6531  * individual column definition.
6532  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
6533  * @constructor
6534  * @param {Object} config An Array of column config objects. See this class's
6535  * config objects for details.
6536 */
6537 Roo.grid.ColumnModel = function(config){
6538         /**
6539      * The config passed into the constructor
6540      */
6541     this.config = config;
6542     this.lookup = {};
6543
6544     // if no id, create one
6545     // if the column does not have a dataIndex mapping,
6546     // map it to the order it is in the config
6547     for(var i = 0, len = config.length; i < len; i++){
6548         var c = config[i];
6549         if(typeof c.dataIndex == "undefined"){
6550             c.dataIndex = i;
6551         }
6552         if(typeof c.renderer == "string"){
6553             c.renderer = Roo.util.Format[c.renderer];
6554         }
6555         if(typeof c.id == "undefined"){
6556             c.id = Roo.id();
6557         }
6558         if(c.editor && c.editor.xtype){
6559             c.editor  = Roo.factory(c.editor, Roo.grid);
6560         }
6561         if(c.editor && c.editor.isFormField){
6562             c.editor = new Roo.grid.GridEditor(c.editor);
6563         }
6564         this.lookup[c.id] = c;
6565     }
6566
6567     /**
6568      * The width of columns which have no width specified (defaults to 100)
6569      * @type Number
6570      */
6571     this.defaultWidth = 100;
6572
6573     /**
6574      * Default sortable of columns which have no sortable specified (defaults to false)
6575      * @type Boolean
6576      */
6577     this.defaultSortable = false;
6578
6579     this.addEvents({
6580         /**
6581              * @event widthchange
6582              * Fires when the width of a column changes.
6583              * @param {ColumnModel} this
6584              * @param {Number} columnIndex The column index
6585              * @param {Number} newWidth The new width
6586              */
6587             "widthchange": true,
6588         /**
6589              * @event headerchange
6590              * Fires when the text of a header changes.
6591              * @param {ColumnModel} this
6592              * @param {Number} columnIndex The column index
6593              * @param {Number} newText The new header text
6594              */
6595             "headerchange": true,
6596         /**
6597              * @event hiddenchange
6598              * Fires when a column is hidden or "unhidden".
6599              * @param {ColumnModel} this
6600              * @param {Number} columnIndex The column index
6601              * @param {Boolean} hidden true if hidden, false otherwise
6602              */
6603             "hiddenchange": true,
6604             /**
6605          * @event columnmoved
6606          * Fires when a column is moved.
6607          * @param {ColumnModel} this
6608          * @param {Number} oldIndex
6609          * @param {Number} newIndex
6610          */
6611         "columnmoved" : true,
6612         /**
6613          * @event columlockchange
6614          * Fires when a column's locked state is changed
6615          * @param {ColumnModel} this
6616          * @param {Number} colIndex
6617          * @param {Boolean} locked true if locked
6618          */
6619         "columnlockchange" : true
6620     });
6621     Roo.grid.ColumnModel.superclass.constructor.call(this);
6622 };
6623 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
6624     /**
6625      * @cfg {String} header The header text to display in the Grid view.
6626      */
6627     /**
6628      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
6629      * {@link Roo.data.Record} definition from which to draw the column's value. If not
6630      * specified, the column's index is used as an index into the Record's data Array.
6631      */
6632     /**
6633      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
6634      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
6635      */
6636     /**
6637      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
6638      * Defaults to the value of the {@link #defaultSortable} property.
6639      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
6640      */
6641     /**
6642      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
6643      */
6644     /**
6645      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
6646      */
6647     /**
6648      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
6649      */
6650     /**
6651      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
6652      */
6653     /**
6654      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
6655      * given the cell's data value. See {@link #setRenderer}. If not specified, the
6656      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
6657      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
6658      */
6659        /**
6660      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
6661      */
6662     /**
6663      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
6664      */
6665     /**
6666      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
6667      */
6668     /**
6669      * @cfg {String} cursor (Optional)
6670      */
6671     /**
6672      * @cfg {String} tooltip (Optional)
6673      */
6674     /**
6675      * @cfg {Number} xs (Optional)
6676      */
6677     /**
6678      * @cfg {Number} sm (Optional)
6679      */
6680     /**
6681      * @cfg {Number} md (Optional)
6682      */
6683     /**
6684      * @cfg {Number} lg (Optional)
6685      */
6686     /**
6687      * Returns the id of the column at the specified index.
6688      * @param {Number} index The column index
6689      * @return {String} the id
6690      */
6691     getColumnId : function(index){
6692         return this.config[index].id;
6693     },
6694
6695     /**
6696      * Returns the column for a specified id.
6697      * @param {String} id The column id
6698      * @return {Object} the column
6699      */
6700     getColumnById : function(id){
6701         return this.lookup[id];
6702     },
6703
6704     
6705     /**
6706      * Returns the column for a specified dataIndex.
6707      * @param {String} dataIndex The column dataIndex
6708      * @return {Object|Boolean} the column or false if not found
6709      */
6710     getColumnByDataIndex: function(dataIndex){
6711         var index = this.findColumnIndex(dataIndex);
6712         return index > -1 ? this.config[index] : false;
6713     },
6714     
6715     /**
6716      * Returns the index for a specified column id.
6717      * @param {String} id The column id
6718      * @return {Number} the index, or -1 if not found
6719      */
6720     getIndexById : function(id){
6721         for(var i = 0, len = this.config.length; i < len; i++){
6722             if(this.config[i].id == id){
6723                 return i;
6724             }
6725         }
6726         return -1;
6727     },
6728     
6729     /**
6730      * Returns the index for a specified column dataIndex.
6731      * @param {String} dataIndex The column dataIndex
6732      * @return {Number} the index, or -1 if not found
6733      */
6734     
6735     findColumnIndex : function(dataIndex){
6736         for(var i = 0, len = this.config.length; i < len; i++){
6737             if(this.config[i].dataIndex == dataIndex){
6738                 return i;
6739             }
6740         }
6741         return -1;
6742     },
6743     
6744     
6745     moveColumn : function(oldIndex, newIndex){
6746         var c = this.config[oldIndex];
6747         this.config.splice(oldIndex, 1);
6748         this.config.splice(newIndex, 0, c);
6749         this.dataMap = null;
6750         this.fireEvent("columnmoved", this, oldIndex, newIndex);
6751     },
6752
6753     isLocked : function(colIndex){
6754         return this.config[colIndex].locked === true;
6755     },
6756
6757     setLocked : function(colIndex, value, suppressEvent){
6758         if(this.isLocked(colIndex) == value){
6759             return;
6760         }
6761         this.config[colIndex].locked = value;
6762         if(!suppressEvent){
6763             this.fireEvent("columnlockchange", this, colIndex, value);
6764         }
6765     },
6766
6767     getTotalLockedWidth : function(){
6768         var totalWidth = 0;
6769         for(var i = 0; i < this.config.length; i++){
6770             if(this.isLocked(i) && !this.isHidden(i)){
6771                 this.totalWidth += this.getColumnWidth(i);
6772             }
6773         }
6774         return totalWidth;
6775     },
6776
6777     getLockedCount : function(){
6778         for(var i = 0, len = this.config.length; i < len; i++){
6779             if(!this.isLocked(i)){
6780                 return i;
6781             }
6782         }
6783         
6784         return this.config.length;
6785     },
6786
6787     /**
6788      * Returns the number of columns.
6789      * @return {Number}
6790      */
6791     getColumnCount : function(visibleOnly){
6792         if(visibleOnly === true){
6793             var c = 0;
6794             for(var i = 0, len = this.config.length; i < len; i++){
6795                 if(!this.isHidden(i)){
6796                     c++;
6797                 }
6798             }
6799             return c;
6800         }
6801         return this.config.length;
6802     },
6803
6804     /**
6805      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
6806      * @param {Function} fn
6807      * @param {Object} scope (optional)
6808      * @return {Array} result
6809      */
6810     getColumnsBy : function(fn, scope){
6811         var r = [];
6812         for(var i = 0, len = this.config.length; i < len; i++){
6813             var c = this.config[i];
6814             if(fn.call(scope||this, c, i) === true){
6815                 r[r.length] = c;
6816             }
6817         }
6818         return r;
6819     },
6820
6821     /**
6822      * Returns true if the specified column is sortable.
6823      * @param {Number} col The column index
6824      * @return {Boolean}
6825      */
6826     isSortable : function(col){
6827         if(typeof this.config[col].sortable == "undefined"){
6828             return this.defaultSortable;
6829         }
6830         return this.config[col].sortable;
6831     },
6832
6833     /**
6834      * Returns the rendering (formatting) function defined for the column.
6835      * @param {Number} col The column index.
6836      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
6837      */
6838     getRenderer : function(col){
6839         if(!this.config[col].renderer){
6840             return Roo.grid.ColumnModel.defaultRenderer;
6841         }
6842         return this.config[col].renderer;
6843     },
6844
6845     /**
6846      * Sets the rendering (formatting) function for a column.
6847      * @param {Number} col The column index
6848      * @param {Function} fn The function to use to process the cell's raw data
6849      * to return HTML markup for the grid view. The render function is called with
6850      * the following parameters:<ul>
6851      * <li>Data value.</li>
6852      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
6853      * <li>css A CSS style string to apply to the table cell.</li>
6854      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
6855      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
6856      * <li>Row index</li>
6857      * <li>Column index</li>
6858      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
6859      */
6860     setRenderer : function(col, fn){
6861         this.config[col].renderer = fn;
6862     },
6863
6864     /**
6865      * Returns the width for the specified column.
6866      * @param {Number} col The column index
6867      * @return {Number}
6868      */
6869     getColumnWidth : function(col){
6870         return this.config[col].width * 1 || this.defaultWidth;
6871     },
6872
6873     /**
6874      * Sets the width for a column.
6875      * @param {Number} col The column index
6876      * @param {Number} width The new width
6877      */
6878     setColumnWidth : function(col, width, suppressEvent){
6879         this.config[col].width = width;
6880         this.totalWidth = null;
6881         if(!suppressEvent){
6882              this.fireEvent("widthchange", this, col, width);
6883         }
6884     },
6885
6886     /**
6887      * Returns the total width of all columns.
6888      * @param {Boolean} includeHidden True to include hidden column widths
6889      * @return {Number}
6890      */
6891     getTotalWidth : function(includeHidden){
6892         if(!this.totalWidth){
6893             this.totalWidth = 0;
6894             for(var i = 0, len = this.config.length; i < len; i++){
6895                 if(includeHidden || !this.isHidden(i)){
6896                     this.totalWidth += this.getColumnWidth(i);
6897                 }
6898             }
6899         }
6900         return this.totalWidth;
6901     },
6902
6903     /**
6904      * Returns the header for the specified column.
6905      * @param {Number} col The column index
6906      * @return {String}
6907      */
6908     getColumnHeader : function(col){
6909         return this.config[col].header;
6910     },
6911
6912     /**
6913      * Sets the header for a column.
6914      * @param {Number} col The column index
6915      * @param {String} header The new header
6916      */
6917     setColumnHeader : function(col, header){
6918         this.config[col].header = header;
6919         this.fireEvent("headerchange", this, col, header);
6920     },
6921
6922     /**
6923      * Returns the tooltip for the specified column.
6924      * @param {Number} col The column index
6925      * @return {String}
6926      */
6927     getColumnTooltip : function(col){
6928             return this.config[col].tooltip;
6929     },
6930     /**
6931      * Sets the tooltip for a column.
6932      * @param {Number} col The column index
6933      * @param {String} tooltip The new tooltip
6934      */
6935     setColumnTooltip : function(col, tooltip){
6936             this.config[col].tooltip = tooltip;
6937     },
6938
6939     /**
6940      * Returns the dataIndex for the specified column.
6941      * @param {Number} col The column index
6942      * @return {Number}
6943      */
6944     getDataIndex : function(col){
6945         return this.config[col].dataIndex;
6946     },
6947
6948     /**
6949      * Sets the dataIndex for a column.
6950      * @param {Number} col The column index
6951      * @param {Number} dataIndex The new dataIndex
6952      */
6953     setDataIndex : function(col, dataIndex){
6954         this.config[col].dataIndex = dataIndex;
6955     },
6956
6957     
6958     
6959     /**
6960      * Returns true if the cell is editable.
6961      * @param {Number} colIndex The column index
6962      * @param {Number} rowIndex The row index - this is nto actually used..?
6963      * @return {Boolean}
6964      */
6965     isCellEditable : function(colIndex, rowIndex){
6966         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
6967     },
6968
6969     /**
6970      * Returns the editor defined for the cell/column.
6971      * return false or null to disable editing.
6972      * @param {Number} colIndex The column index
6973      * @param {Number} rowIndex The row index
6974      * @return {Object}
6975      */
6976     getCellEditor : function(colIndex, rowIndex){
6977         return this.config[colIndex].editor;
6978     },
6979
6980     /**
6981      * Sets if a column is editable.
6982      * @param {Number} col The column index
6983      * @param {Boolean} editable True if the column is editable
6984      */
6985     setEditable : function(col, editable){
6986         this.config[col].editable = editable;
6987     },
6988
6989
6990     /**
6991      * Returns true if the column is hidden.
6992      * @param {Number} colIndex The column index
6993      * @return {Boolean}
6994      */
6995     isHidden : function(colIndex){
6996         return this.config[colIndex].hidden;
6997     },
6998
6999
7000     /**
7001      * Returns true if the column width cannot be changed
7002      */
7003     isFixed : function(colIndex){
7004         return this.config[colIndex].fixed;
7005     },
7006
7007     /**
7008      * Returns true if the column can be resized
7009      * @return {Boolean}
7010      */
7011     isResizable : function(colIndex){
7012         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7013     },
7014     /**
7015      * Sets if a column is hidden.
7016      * @param {Number} colIndex The column index
7017      * @param {Boolean} hidden True if the column is hidden
7018      */
7019     setHidden : function(colIndex, hidden){
7020         this.config[colIndex].hidden = hidden;
7021         this.totalWidth = null;
7022         this.fireEvent("hiddenchange", this, colIndex, hidden);
7023     },
7024
7025     /**
7026      * Sets the editor for a column.
7027      * @param {Number} col The column index
7028      * @param {Object} editor The editor object
7029      */
7030     setEditor : function(col, editor){
7031         this.config[col].editor = editor;
7032     }
7033 });
7034
7035 Roo.grid.ColumnModel.defaultRenderer = function(value)
7036 {
7037     if(typeof value == "object") {
7038         return value;
7039     }
7040         if(typeof value == "string" && value.length < 1){
7041             return "&#160;";
7042         }
7043     
7044         return String.format("{0}", value);
7045 };
7046
7047 // Alias for backwards compatibility
7048 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7049 /*
7050  * Based on:
7051  * Ext JS Library 1.1.1
7052  * Copyright(c) 2006-2007, Ext JS, LLC.
7053  *
7054  * Originally Released Under LGPL - original licence link has changed is not relivant.
7055  *
7056  * Fork - LGPL
7057  * <script type="text/javascript">
7058  */
7059  
7060 /**
7061  * @class Roo.LoadMask
7062  * A simple utility class for generically masking elements while loading data.  If the element being masked has
7063  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7064  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
7065  * element's UpdateManager load indicator and will be destroyed after the initial load.
7066  * @constructor
7067  * Create a new LoadMask
7068  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7069  * @param {Object} config The config object
7070  */
7071 Roo.LoadMask = function(el, config){
7072     this.el = Roo.get(el);
7073     Roo.apply(this, config);
7074     if(this.store){
7075         this.store.on('beforeload', this.onBeforeLoad, this);
7076         this.store.on('load', this.onLoad, this);
7077         this.store.on('loadexception', this.onLoadException, this);
7078         this.removeMask = false;
7079     }else{
7080         var um = this.el.getUpdateManager();
7081         um.showLoadIndicator = false; // disable the default indicator
7082         um.on('beforeupdate', this.onBeforeLoad, this);
7083         um.on('update', this.onLoad, this);
7084         um.on('failure', this.onLoad, this);
7085         this.removeMask = true;
7086     }
7087 };
7088
7089 Roo.LoadMask.prototype = {
7090     /**
7091      * @cfg {Boolean} removeMask
7092      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7093      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7094      */
7095     /**
7096      * @cfg {String} msg
7097      * The text to display in a centered loading message box (defaults to 'Loading...')
7098      */
7099     msg : 'Loading...',
7100     /**
7101      * @cfg {String} msgCls
7102      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7103      */
7104     msgCls : 'x-mask-loading',
7105
7106     /**
7107      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7108      * @type Boolean
7109      */
7110     disabled: false,
7111
7112     /**
7113      * Disables the mask to prevent it from being displayed
7114      */
7115     disable : function(){
7116        this.disabled = true;
7117     },
7118
7119     /**
7120      * Enables the mask so that it can be displayed
7121      */
7122     enable : function(){
7123         this.disabled = false;
7124     },
7125     
7126     onLoadException : function()
7127     {
7128         Roo.log(arguments);
7129         
7130         if (typeof(arguments[3]) != 'undefined') {
7131             Roo.MessageBox.alert("Error loading",arguments[3]);
7132         } 
7133         /*
7134         try {
7135             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7136                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7137             }   
7138         } catch(e) {
7139             
7140         }
7141         */
7142     
7143         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7144     },
7145     // private
7146     onLoad : function()
7147     {
7148         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7149     },
7150
7151     // private
7152     onBeforeLoad : function(){
7153         if(!this.disabled){
7154             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7155         }
7156     },
7157
7158     // private
7159     destroy : function(){
7160         if(this.store){
7161             this.store.un('beforeload', this.onBeforeLoad, this);
7162             this.store.un('load', this.onLoad, this);
7163             this.store.un('loadexception', this.onLoadException, this);
7164         }else{
7165             var um = this.el.getUpdateManager();
7166             um.un('beforeupdate', this.onBeforeLoad, this);
7167             um.un('update', this.onLoad, this);
7168             um.un('failure', this.onLoad, this);
7169         }
7170     }
7171 };/*
7172  * - LGPL
7173  *
7174  * table
7175  * 
7176  */
7177
7178 /**
7179  * @class Roo.bootstrap.Table
7180  * @extends Roo.bootstrap.Component
7181  * Bootstrap Table class
7182  * @cfg {String} cls table class
7183  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7184  * @cfg {String} bgcolor Specifies the background color for a table
7185  * @cfg {Number} border Specifies whether the table cells should have borders or not
7186  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7187  * @cfg {Number} cellspacing Specifies the space between cells
7188  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7189  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7190  * @cfg {String} sortable Specifies that the table should be sortable
7191  * @cfg {String} summary Specifies a summary of the content of a table
7192  * @cfg {Number} width Specifies the width of a table
7193  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7194  * 
7195  * @cfg {boolean} striped Should the rows be alternative striped
7196  * @cfg {boolean} bordered Add borders to the table
7197  * @cfg {boolean} hover Add hover highlighting
7198  * @cfg {boolean} condensed Format condensed
7199  * @cfg {boolean} responsive Format condensed
7200  * @cfg {Boolean} loadMask (true|false) default false
7201  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7202  * @cfg {Boolean} headerShow (true|false) generate thead, default true
7203  * @cfg {Boolean} rowSelection (true|false) default false
7204  * @cfg {Boolean} cellSelection (true|false) default false
7205  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7206  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
7207  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
7208  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
7209  
7210  * 
7211  * @constructor
7212  * Create a new Table
7213  * @param {Object} config The config object
7214  */
7215
7216 Roo.bootstrap.Table = function(config){
7217     Roo.bootstrap.Table.superclass.constructor.call(this, config);
7218     
7219   
7220     
7221     // BC...
7222     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7223     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7224     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7225     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7226     
7227     this.sm = this.sm || {xtype: 'RowSelectionModel'};
7228     if (this.sm) {
7229         this.sm.grid = this;
7230         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7231         this.sm = this.selModel;
7232         this.sm.xmodule = this.xmodule || false;
7233     }
7234     
7235     if (this.cm && typeof(this.cm.config) == 'undefined') {
7236         this.colModel = new Roo.grid.ColumnModel(this.cm);
7237         this.cm = this.colModel;
7238         this.cm.xmodule = this.xmodule || false;
7239     }
7240     if (this.store) {
7241         this.store= Roo.factory(this.store, Roo.data);
7242         this.ds = this.store;
7243         this.ds.xmodule = this.xmodule || false;
7244          
7245     }
7246     if (this.footer && this.store) {
7247         this.footer.dataSource = this.ds;
7248         this.footer = Roo.factory(this.footer);
7249     }
7250     
7251     /** @private */
7252     this.addEvents({
7253         /**
7254          * @event cellclick
7255          * Fires when a cell is clicked
7256          * @param {Roo.bootstrap.Table} this
7257          * @param {Roo.Element} el
7258          * @param {Number} rowIndex
7259          * @param {Number} columnIndex
7260          * @param {Roo.EventObject} e
7261          */
7262         "cellclick" : true,
7263         /**
7264          * @event celldblclick
7265          * Fires when a cell is double clicked
7266          * @param {Roo.bootstrap.Table} this
7267          * @param {Roo.Element} el
7268          * @param {Number} rowIndex
7269          * @param {Number} columnIndex
7270          * @param {Roo.EventObject} e
7271          */
7272         "celldblclick" : true,
7273         /**
7274          * @event rowclick
7275          * Fires when a row is clicked
7276          * @param {Roo.bootstrap.Table} this
7277          * @param {Roo.Element} el
7278          * @param {Number} rowIndex
7279          * @param {Roo.EventObject} e
7280          */
7281         "rowclick" : true,
7282         /**
7283          * @event rowdblclick
7284          * Fires when a row is double clicked
7285          * @param {Roo.bootstrap.Table} this
7286          * @param {Roo.Element} el
7287          * @param {Number} rowIndex
7288          * @param {Roo.EventObject} e
7289          */
7290         "rowdblclick" : true,
7291         /**
7292          * @event mouseover
7293          * Fires when a mouseover occur
7294          * @param {Roo.bootstrap.Table} this
7295          * @param {Roo.Element} el
7296          * @param {Number} rowIndex
7297          * @param {Number} columnIndex
7298          * @param {Roo.EventObject} e
7299          */
7300         "mouseover" : true,
7301         /**
7302          * @event mouseout
7303          * Fires when a mouseout occur
7304          * @param {Roo.bootstrap.Table} this
7305          * @param {Roo.Element} el
7306          * @param {Number} rowIndex
7307          * @param {Number} columnIndex
7308          * @param {Roo.EventObject} e
7309          */
7310         "mouseout" : true,
7311         /**
7312          * @event rowclass
7313          * Fires when a row is rendered, so you can change add a style to it.
7314          * @param {Roo.bootstrap.Table} this
7315          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
7316          */
7317         'rowclass' : true,
7318           /**
7319          * @event rowsrendered
7320          * Fires when all the  rows have been rendered
7321          * @param {Roo.bootstrap.Table} this
7322          */
7323         'rowsrendered' : true,
7324         /**
7325          * @event contextmenu
7326          * The raw contextmenu event for the entire grid.
7327          * @param {Roo.EventObject} e
7328          */
7329         "contextmenu" : true,
7330         /**
7331          * @event rowcontextmenu
7332          * Fires when a row is right clicked
7333          * @param {Roo.bootstrap.Table} this
7334          * @param {Number} rowIndex
7335          * @param {Roo.EventObject} e
7336          */
7337         "rowcontextmenu" : true,
7338         /**
7339          * @event cellcontextmenu
7340          * Fires when a cell is right clicked
7341          * @param {Roo.bootstrap.Table} this
7342          * @param {Number} rowIndex
7343          * @param {Number} cellIndex
7344          * @param {Roo.EventObject} e
7345          */
7346          "cellcontextmenu" : true,
7347          /**
7348          * @event headercontextmenu
7349          * Fires when a header is right clicked
7350          * @param {Roo.bootstrap.Table} this
7351          * @param {Number} columnIndex
7352          * @param {Roo.EventObject} e
7353          */
7354         "headercontextmenu" : true
7355     });
7356 };
7357
7358 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
7359     
7360     cls: false,
7361     align: false,
7362     bgcolor: false,
7363     border: false,
7364     cellpadding: false,
7365     cellspacing: false,
7366     frame: false,
7367     rules: false,
7368     sortable: false,
7369     summary: false,
7370     width: false,
7371     striped : false,
7372     scrollBody : false,
7373     bordered: false,
7374     hover:  false,
7375     condensed : false,
7376     responsive : false,
7377     sm : false,
7378     cm : false,
7379     store : false,
7380     loadMask : false,
7381     footerShow : true,
7382     headerShow : true,
7383   
7384     rowSelection : false,
7385     cellSelection : false,
7386     layout : false,
7387     
7388     // Roo.Element - the tbody
7389     mainBody: false,
7390     // Roo.Element - thead element
7391     mainHead: false,
7392     
7393     container: false, // used by gridpanel...
7394     
7395     lazyLoad : false,
7396     
7397     CSS : Roo.util.CSS,
7398     
7399     auto_hide_footer : false,
7400     
7401     getAutoCreate : function()
7402     {
7403         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7404         
7405         cfg = {
7406             tag: 'table',
7407             cls : 'table',
7408             cn : []
7409         };
7410         if (this.scrollBody) {
7411             cfg.cls += ' table-body-fixed';
7412         }    
7413         if (this.striped) {
7414             cfg.cls += ' table-striped';
7415         }
7416         
7417         if (this.hover) {
7418             cfg.cls += ' table-hover';
7419         }
7420         if (this.bordered) {
7421             cfg.cls += ' table-bordered';
7422         }
7423         if (this.condensed) {
7424             cfg.cls += ' table-condensed';
7425         }
7426         if (this.responsive) {
7427             cfg.cls += ' table-responsive';
7428         }
7429         
7430         if (this.cls) {
7431             cfg.cls+=  ' ' +this.cls;
7432         }
7433         
7434         // this lot should be simplifed...
7435         var _t = this;
7436         var cp = [
7437             'align',
7438             'bgcolor',
7439             'border',
7440             'cellpadding',
7441             'cellspacing',
7442             'frame',
7443             'rules',
7444             'sortable',
7445             'summary',
7446             'width'
7447         ].forEach(function(k) {
7448             if (_t[k]) {
7449                 cfg[k] = _t[k];
7450             }
7451         });
7452         
7453         
7454         if (this.layout) {
7455             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7456         }
7457         
7458         if(this.store || this.cm){
7459             if(this.headerShow){
7460                 cfg.cn.push(this.renderHeader());
7461             }
7462             
7463             cfg.cn.push(this.renderBody());
7464             
7465             if(this.footerShow){
7466                 cfg.cn.push(this.renderFooter());
7467             }
7468             // where does this come from?
7469             //cfg.cls+=  ' TableGrid';
7470         }
7471         
7472         return { cn : [ cfg ] };
7473     },
7474     
7475     initEvents : function()
7476     {   
7477         if(!this.store || !this.cm){
7478             return;
7479         }
7480         if (this.selModel) {
7481             this.selModel.initEvents();
7482         }
7483         
7484         
7485         //Roo.log('initEvents with ds!!!!');
7486         
7487         this.mainBody = this.el.select('tbody', true).first();
7488         this.mainHead = this.el.select('thead', true).first();
7489         this.mainFoot = this.el.select('tfoot', true).first();
7490         
7491         
7492         
7493         var _this = this;
7494         
7495         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7496             e.on('click', _this.sort, _this);
7497         });
7498         
7499         this.mainBody.on("click", this.onClick, this);
7500         this.mainBody.on("dblclick", this.onDblClick, this);
7501         
7502         // why is this done????? = it breaks dialogs??
7503         //this.parent().el.setStyle('position', 'relative');
7504         
7505         
7506         if (this.footer) {
7507             this.footer.parentId = this.id;
7508             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
7509             
7510             if(this.lazyLoad){
7511                 this.el.select('tfoot tr td').first().addClass('hide');
7512             }
7513         } 
7514         
7515         if(this.loadMask) {
7516             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
7517         }
7518         
7519         this.store.on('load', this.onLoad, this);
7520         this.store.on('beforeload', this.onBeforeLoad, this);
7521         this.store.on('update', this.onUpdate, this);
7522         this.store.on('add', this.onAdd, this);
7523         this.store.on("clear", this.clear, this);
7524         
7525         this.el.on("contextmenu", this.onContextMenu, this);
7526         
7527         this.mainBody.on('scroll', this.onBodyScroll, this);
7528         
7529         this.cm.on("headerchange", this.onHeaderChange, this);
7530         
7531         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
7532         
7533     },
7534     
7535     onContextMenu : function(e, t)
7536     {
7537         this.processEvent("contextmenu", e);
7538     },
7539     
7540     processEvent : function(name, e)
7541     {
7542         if (name != 'touchstart' ) {
7543             this.fireEvent(name, e);    
7544         }
7545         
7546         var t = e.getTarget();
7547         
7548         var cell = Roo.get(t);
7549         
7550         if(!cell){
7551             return;
7552         }
7553         
7554         if(cell.findParent('tfoot', false, true)){
7555             return;
7556         }
7557         
7558         if(cell.findParent('thead', false, true)){
7559             
7560             if(e.getTarget().nodeName.toLowerCase() != 'th'){
7561                 cell = Roo.get(t).findParent('th', false, true);
7562                 if (!cell) {
7563                     Roo.log("failed to find th in thead?");
7564                     Roo.log(e.getTarget());
7565                     return;
7566                 }
7567             }
7568             
7569             var cellIndex = cell.dom.cellIndex;
7570             
7571             var ename = name == 'touchstart' ? 'click' : name;
7572             this.fireEvent("header" + ename, this, cellIndex, e);
7573             
7574             return;
7575         }
7576         
7577         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7578             cell = Roo.get(t).findParent('td', false, true);
7579             if (!cell) {
7580                 Roo.log("failed to find th in tbody?");
7581                 Roo.log(e.getTarget());
7582                 return;
7583             }
7584         }
7585         
7586         var row = cell.findParent('tr', false, true);
7587         var cellIndex = cell.dom.cellIndex;
7588         var rowIndex = row.dom.rowIndex - 1;
7589         
7590         if(row !== false){
7591             
7592             this.fireEvent("row" + name, this, rowIndex, e);
7593             
7594             if(cell !== false){
7595             
7596                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
7597             }
7598         }
7599         
7600     },
7601     
7602     onMouseover : function(e, el)
7603     {
7604         var cell = Roo.get(el);
7605         
7606         if(!cell){
7607             return;
7608         }
7609         
7610         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7611             cell = cell.findParent('td', false, true);
7612         }
7613         
7614         var row = cell.findParent('tr', false, true);
7615         var cellIndex = cell.dom.cellIndex;
7616         var rowIndex = row.dom.rowIndex - 1; // start from 0
7617         
7618         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
7619         
7620     },
7621     
7622     onMouseout : function(e, el)
7623     {
7624         var cell = Roo.get(el);
7625         
7626         if(!cell){
7627             return;
7628         }
7629         
7630         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7631             cell = cell.findParent('td', false, true);
7632         }
7633         
7634         var row = cell.findParent('tr', false, true);
7635         var cellIndex = cell.dom.cellIndex;
7636         var rowIndex = row.dom.rowIndex - 1; // start from 0
7637         
7638         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
7639         
7640     },
7641     
7642     onClick : function(e, el)
7643     {
7644         var cell = Roo.get(el);
7645         
7646         if(!cell || (!this.cellSelection && !this.rowSelection)){
7647             return;
7648         }
7649         
7650         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7651             cell = cell.findParent('td', false, true);
7652         }
7653         
7654         if(!cell || typeof(cell) == 'undefined'){
7655             return;
7656         }
7657         
7658         var row = cell.findParent('tr', false, true);
7659         
7660         if(!row || typeof(row) == 'undefined'){
7661             return;
7662         }
7663         
7664         var cellIndex = cell.dom.cellIndex;
7665         var rowIndex = this.getRowIndex(row);
7666         
7667         // why??? - should these not be based on SelectionModel?
7668         if(this.cellSelection){
7669             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
7670         }
7671         
7672         if(this.rowSelection){
7673             this.fireEvent('rowclick', this, row, rowIndex, e);
7674         }
7675         
7676         
7677     },
7678         
7679     onDblClick : function(e,el)
7680     {
7681         var cell = Roo.get(el);
7682         
7683         if(!cell || (!this.cellSelection && !this.rowSelection)){
7684             return;
7685         }
7686         
7687         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7688             cell = cell.findParent('td', false, true);
7689         }
7690         
7691         if(!cell || typeof(cell) == 'undefined'){
7692             return;
7693         }
7694         
7695         var row = cell.findParent('tr', false, true);
7696         
7697         if(!row || typeof(row) == 'undefined'){
7698             return;
7699         }
7700         
7701         var cellIndex = cell.dom.cellIndex;
7702         var rowIndex = this.getRowIndex(row);
7703         
7704         if(this.cellSelection){
7705             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
7706         }
7707         
7708         if(this.rowSelection){
7709             this.fireEvent('rowdblclick', this, row, rowIndex, e);
7710         }
7711     },
7712     
7713     sort : function(e,el)
7714     {
7715         var col = Roo.get(el);
7716         
7717         if(!col.hasClass('sortable')){
7718             return;
7719         }
7720         
7721         var sort = col.attr('sort');
7722         var dir = 'ASC';
7723         
7724         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
7725             dir = 'DESC';
7726         }
7727         
7728         this.store.sortInfo = {field : sort, direction : dir};
7729         
7730         if (this.footer) {
7731             Roo.log("calling footer first");
7732             this.footer.onClick('first');
7733         } else {
7734         
7735             this.store.load({ params : { start : 0 } });
7736         }
7737     },
7738     
7739     renderHeader : function()
7740     {
7741         var header = {
7742             tag: 'thead',
7743             cn : []
7744         };
7745         
7746         var cm = this.cm;
7747         this.totalWidth = 0;
7748         
7749         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7750             
7751             var config = cm.config[i];
7752             
7753             var c = {
7754                 tag: 'th',
7755                 cls : 'x-hcol-' + i,
7756                 style : '',
7757                 html: cm.getColumnHeader(i)
7758             };
7759             
7760             var hh = '';
7761             
7762             if(typeof(config.sortable) != 'undefined' && config.sortable){
7763                 c.cls = 'sortable';
7764                 c.html = '<i class="glyphicon"></i>' + c.html;
7765             }
7766             
7767             // could use BS4 hidden-..-down 
7768             
7769             if(typeof(config.lgHeader) != 'undefined'){
7770                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
7771             }
7772             
7773             if(typeof(config.mdHeader) != 'undefined'){
7774                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
7775             }
7776             
7777             if(typeof(config.smHeader) != 'undefined'){
7778                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
7779             }
7780             
7781             if(typeof(config.xsHeader) != 'undefined'){
7782                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
7783             }
7784             
7785             if(hh.length){
7786                 c.html = hh;
7787             }
7788             
7789             if(typeof(config.tooltip) != 'undefined'){
7790                 c.tooltip = config.tooltip;
7791             }
7792             
7793             if(typeof(config.colspan) != 'undefined'){
7794                 c.colspan = config.colspan;
7795             }
7796             
7797             if(typeof(config.hidden) != 'undefined' && config.hidden){
7798                 c.style += ' display:none;';
7799             }
7800             
7801             if(typeof(config.dataIndex) != 'undefined'){
7802                 c.sort = config.dataIndex;
7803             }
7804             
7805            
7806             
7807             if(typeof(config.align) != 'undefined' && config.align.length){
7808                 c.style += ' text-align:' + config.align + ';';
7809             }
7810             
7811             if(typeof(config.width) != 'undefined'){
7812                 c.style += ' width:' + config.width + 'px;';
7813                 this.totalWidth += config.width;
7814             } else {
7815                 this.totalWidth += 100; // assume minimum of 100 per column?
7816             }
7817             
7818             if(typeof(config.cls) != 'undefined'){
7819                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
7820             }
7821             
7822             ['xs','sm','md','lg'].map(function(size){
7823                 
7824                 if(typeof(config[size]) == 'undefined'){
7825                     return;
7826                 }
7827                  
7828                 if (!config[size]) { // 0 = hidden
7829                     // BS 4 '0' is treated as hide that column and below.
7830                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
7831                     return;
7832                 }
7833                 
7834                 c.cls += ' col-' + size + '-' + config[size] + (
7835                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
7836                 );
7837                 
7838                 
7839             });
7840             
7841             header.cn.push(c)
7842         }
7843         
7844         return header;
7845     },
7846     
7847     renderBody : function()
7848     {
7849         var body = {
7850             tag: 'tbody',
7851             cn : [
7852                 {
7853                     tag: 'tr',
7854                     cn : [
7855                         {
7856                             tag : 'td',
7857                             colspan :  this.cm.getColumnCount()
7858                         }
7859                     ]
7860                 }
7861             ]
7862         };
7863         
7864         return body;
7865     },
7866     
7867     renderFooter : function()
7868     {
7869         var footer = {
7870             tag: 'tfoot',
7871             cn : [
7872                 {
7873                     tag: 'tr',
7874                     cn : [
7875                         {
7876                             tag : 'td',
7877                             colspan :  this.cm.getColumnCount()
7878                         }
7879                     ]
7880                 }
7881             ]
7882         };
7883         
7884         return footer;
7885     },
7886     
7887     
7888     
7889     onLoad : function()
7890     {
7891 //        Roo.log('ds onload');
7892         this.clear();
7893         
7894         var _this = this;
7895         var cm = this.cm;
7896         var ds = this.store;
7897         
7898         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7899             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
7900             if (_this.store.sortInfo) {
7901                     
7902                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
7903                     e.select('i', true).addClass(['glyphicon-arrow-up']);
7904                 }
7905                 
7906                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
7907                     e.select('i', true).addClass(['glyphicon-arrow-down']);
7908                 }
7909             }
7910         });
7911         
7912         var tbody =  this.mainBody;
7913               
7914         if(ds.getCount() > 0){
7915             ds.data.each(function(d,rowIndex){
7916                 var row =  this.renderRow(cm, ds, rowIndex);
7917                 
7918                 tbody.createChild(row);
7919                 
7920                 var _this = this;
7921                 
7922                 if(row.cellObjects.length){
7923                     Roo.each(row.cellObjects, function(r){
7924                         _this.renderCellObject(r);
7925                     })
7926                 }
7927                 
7928             }, this);
7929         }
7930         
7931         var tfoot = this.el.select('tfoot', true).first();
7932         
7933         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
7934             
7935             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
7936             
7937             var total = this.ds.getTotalCount();
7938             
7939             if(this.footer.pageSize < total){
7940                 this.mainFoot.show();
7941             }
7942         }
7943         
7944         Roo.each(this.el.select('tbody td', true).elements, function(e){
7945             e.on('mouseover', _this.onMouseover, _this);
7946         });
7947         
7948         Roo.each(this.el.select('tbody td', true).elements, function(e){
7949             e.on('mouseout', _this.onMouseout, _this);
7950         });
7951         this.fireEvent('rowsrendered', this);
7952         
7953         this.autoSize();
7954     },
7955     
7956     
7957     onUpdate : function(ds,record)
7958     {
7959         this.refreshRow(record);
7960         this.autoSize();
7961     },
7962     
7963     onRemove : function(ds, record, index, isUpdate){
7964         if(isUpdate !== true){
7965             this.fireEvent("beforerowremoved", this, index, record);
7966         }
7967         var bt = this.mainBody.dom;
7968         
7969         var rows = this.el.select('tbody > tr', true).elements;
7970         
7971         if(typeof(rows[index]) != 'undefined'){
7972             bt.removeChild(rows[index].dom);
7973         }
7974         
7975 //        if(bt.rows[index]){
7976 //            bt.removeChild(bt.rows[index]);
7977 //        }
7978         
7979         if(isUpdate !== true){
7980             //this.stripeRows(index);
7981             //this.syncRowHeights(index, index);
7982             //this.layout();
7983             this.fireEvent("rowremoved", this, index, record);
7984         }
7985     },
7986     
7987     onAdd : function(ds, records, rowIndex)
7988     {
7989         //Roo.log('on Add called');
7990         // - note this does not handle multiple adding very well..
7991         var bt = this.mainBody.dom;
7992         for (var i =0 ; i < records.length;i++) {
7993             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
7994             //Roo.log(records[i]);
7995             //Roo.log(this.store.getAt(rowIndex+i));
7996             this.insertRow(this.store, rowIndex + i, false);
7997             return;
7998         }
7999         
8000     },
8001     
8002     
8003     refreshRow : function(record){
8004         var ds = this.store, index;
8005         if(typeof record == 'number'){
8006             index = record;
8007             record = ds.getAt(index);
8008         }else{
8009             index = ds.indexOf(record);
8010         }
8011         this.insertRow(ds, index, true);
8012         this.autoSize();
8013         this.onRemove(ds, record, index+1, true);
8014         this.autoSize();
8015         //this.syncRowHeights(index, index);
8016         //this.layout();
8017         this.fireEvent("rowupdated", this, index, record);
8018     },
8019     
8020     insertRow : function(dm, rowIndex, isUpdate){
8021         
8022         if(!isUpdate){
8023             this.fireEvent("beforerowsinserted", this, rowIndex);
8024         }
8025             //var s = this.getScrollState();
8026         var row = this.renderRow(this.cm, this.store, rowIndex);
8027         // insert before rowIndex..
8028         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8029         
8030         var _this = this;
8031                 
8032         if(row.cellObjects.length){
8033             Roo.each(row.cellObjects, function(r){
8034                 _this.renderCellObject(r);
8035             })
8036         }
8037             
8038         if(!isUpdate){
8039             this.fireEvent("rowsinserted", this, rowIndex);
8040             //this.syncRowHeights(firstRow, lastRow);
8041             //this.stripeRows(firstRow);
8042             //this.layout();
8043         }
8044         
8045     },
8046     
8047     
8048     getRowDom : function(rowIndex)
8049     {
8050         var rows = this.el.select('tbody > tr', true).elements;
8051         
8052         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8053         
8054     },
8055     // returns the object tree for a tr..
8056   
8057     
8058     renderRow : function(cm, ds, rowIndex) 
8059     {
8060         var d = ds.getAt(rowIndex);
8061         
8062         var row = {
8063             tag : 'tr',
8064             cls : 'x-row-' + rowIndex,
8065             cn : []
8066         };
8067             
8068         var cellObjects = [];
8069         
8070         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8071             var config = cm.config[i];
8072             
8073             var renderer = cm.getRenderer(i);
8074             var value = '';
8075             var id = false;
8076             
8077             if(typeof(renderer) !== 'undefined'){
8078                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8079             }
8080             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8081             // and are rendered into the cells after the row is rendered - using the id for the element.
8082             
8083             if(typeof(value) === 'object'){
8084                 id = Roo.id();
8085                 cellObjects.push({
8086                     container : id,
8087                     cfg : value 
8088                 })
8089             }
8090             
8091             var rowcfg = {
8092                 record: d,
8093                 rowIndex : rowIndex,
8094                 colIndex : i,
8095                 rowClass : ''
8096             };
8097
8098             this.fireEvent('rowclass', this, rowcfg);
8099             
8100             var td = {
8101                 tag: 'td',
8102                 cls : rowcfg.rowClass + ' x-col-' + i,
8103                 style: '',
8104                 html: (typeof(value) === 'object') ? '' : value
8105             };
8106             
8107             if (id) {
8108                 td.id = id;
8109             }
8110             
8111             if(typeof(config.colspan) != 'undefined'){
8112                 td.colspan = config.colspan;
8113             }
8114             
8115             if(typeof(config.hidden) != 'undefined' && config.hidden){
8116                 td.style += ' display:none;';
8117             }
8118             
8119             if(typeof(config.align) != 'undefined' && config.align.length){
8120                 td.style += ' text-align:' + config.align + ';';
8121             }
8122             if(typeof(config.valign) != 'undefined' && config.valign.length){
8123                 td.style += ' vertical-align:' + config.valign + ';';
8124             }
8125             
8126             if(typeof(config.width) != 'undefined'){
8127                 td.style += ' width:' +  config.width + 'px;';
8128             }
8129             
8130             if(typeof(config.cursor) != 'undefined'){
8131                 td.style += ' cursor:' +  config.cursor + ';';
8132             }
8133             
8134             if(typeof(config.cls) != 'undefined'){
8135                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8136             }
8137             
8138             ['xs','sm','md','lg'].map(function(size){
8139                 
8140                 if(typeof(config[size]) == 'undefined'){
8141                     return;
8142                 }
8143                 
8144                 
8145                   
8146                 if (!config[size]) { // 0 = hidden
8147                     // BS 4 '0' is treated as hide that column and below.
8148                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8149                     return;
8150                 }
8151                 
8152                 td.cls += ' col-' + size + '-' + config[size] + (
8153                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8154                 );
8155                  
8156
8157             });
8158             
8159             row.cn.push(td);
8160            
8161         }
8162         
8163         row.cellObjects = cellObjects;
8164         
8165         return row;
8166           
8167     },
8168     
8169     
8170     
8171     onBeforeLoad : function()
8172     {
8173         
8174     },
8175      /**
8176      * Remove all rows
8177      */
8178     clear : function()
8179     {
8180         this.el.select('tbody', true).first().dom.innerHTML = '';
8181     },
8182     /**
8183      * Show or hide a row.
8184      * @param {Number} rowIndex to show or hide
8185      * @param {Boolean} state hide
8186      */
8187     setRowVisibility : function(rowIndex, state)
8188     {
8189         var bt = this.mainBody.dom;
8190         
8191         var rows = this.el.select('tbody > tr', true).elements;
8192         
8193         if(typeof(rows[rowIndex]) == 'undefined'){
8194             return;
8195         }
8196         rows[rowIndex].dom.style.display = state ? '' : 'none';
8197     },
8198     
8199     
8200     getSelectionModel : function(){
8201         if(!this.selModel){
8202             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8203         }
8204         return this.selModel;
8205     },
8206     /*
8207      * Render the Roo.bootstrap object from renderder
8208      */
8209     renderCellObject : function(r)
8210     {
8211         var _this = this;
8212         
8213         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8214         
8215         var t = r.cfg.render(r.container);
8216         
8217         if(r.cfg.cn){
8218             Roo.each(r.cfg.cn, function(c){
8219                 var child = {
8220                     container: t.getChildContainer(),
8221                     cfg: c
8222                 };
8223                 _this.renderCellObject(child);
8224             })
8225         }
8226     },
8227     
8228     getRowIndex : function(row)
8229     {
8230         var rowIndex = -1;
8231         
8232         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8233             if(el != row){
8234                 return;
8235             }
8236             
8237             rowIndex = index;
8238         });
8239         
8240         return rowIndex;
8241     },
8242      /**
8243      * Returns the grid's underlying element = used by panel.Grid
8244      * @return {Element} The element
8245      */
8246     getGridEl : function(){
8247         return this.el;
8248     },
8249      /**
8250      * Forces a resize - used by panel.Grid
8251      * @return {Element} The element
8252      */
8253     autoSize : function()
8254     {
8255         //var ctr = Roo.get(this.container.dom.parentElement);
8256         var ctr = Roo.get(this.el.dom);
8257         
8258         var thd = this.getGridEl().select('thead',true).first();
8259         var tbd = this.getGridEl().select('tbody', true).first();
8260         var tfd = this.getGridEl().select('tfoot', true).first();
8261         
8262         var cw = ctr.getWidth();
8263         
8264         if (tbd) {
8265             
8266             tbd.setWidth(ctr.getWidth());
8267             // if the body has a max height - and then scrolls - we should perhaps set up the height here
8268             // this needs fixing for various usage - currently only hydra job advers I think..
8269             //tdb.setHeight(
8270             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8271             //); 
8272             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8273             cw -= barsize;
8274         }
8275         cw = Math.max(cw, this.totalWidth);
8276         this.getGridEl().select('tr',true).setWidth(cw);
8277         // resize 'expandable coloumn?
8278         
8279         return; // we doe not have a view in this design..
8280         
8281     },
8282     onBodyScroll: function()
8283     {
8284         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8285         if(this.mainHead){
8286             this.mainHead.setStyle({
8287                 'position' : 'relative',
8288                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8289             });
8290         }
8291         
8292         if(this.lazyLoad){
8293             
8294             var scrollHeight = this.mainBody.dom.scrollHeight;
8295             
8296             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8297             
8298             var height = this.mainBody.getHeight();
8299             
8300             if(scrollHeight - height == scrollTop) {
8301                 
8302                 var total = this.ds.getTotalCount();
8303                 
8304                 if(this.footer.cursor + this.footer.pageSize < total){
8305                     
8306                     this.footer.ds.load({
8307                         params : {
8308                             start : this.footer.cursor + this.footer.pageSize,
8309                             limit : this.footer.pageSize
8310                         },
8311                         add : true
8312                     });
8313                 }
8314             }
8315             
8316         }
8317     },
8318     
8319     onHeaderChange : function()
8320     {
8321         var header = this.renderHeader();
8322         var table = this.el.select('table', true).first();
8323         
8324         this.mainHead.remove();
8325         this.mainHead = table.createChild(header, this.mainBody, false);
8326     },
8327     
8328     onHiddenChange : function(colModel, colIndex, hidden)
8329     {
8330         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8331         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8332         
8333         this.CSS.updateRule(thSelector, "display", "");
8334         this.CSS.updateRule(tdSelector, "display", "");
8335         
8336         if(hidden){
8337             this.CSS.updateRule(thSelector, "display", "none");
8338             this.CSS.updateRule(tdSelector, "display", "none");
8339         }
8340         
8341         this.onHeaderChange();
8342         this.onLoad();
8343     },
8344     
8345     setColumnWidth: function(col_index, width)
8346     {
8347         // width = "md-2 xs-2..."
8348         if(!this.colModel.config[col_index]) {
8349             return;
8350         }
8351         
8352         var w = width.split(" ");
8353         
8354         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8355         
8356         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8357         
8358         
8359         for(var j = 0; j < w.length; j++) {
8360             
8361             if(!w[j]) {
8362                 continue;
8363             }
8364             
8365             var size_cls = w[j].split("-");
8366             
8367             if(!Number.isInteger(size_cls[1] * 1)) {
8368                 continue;
8369             }
8370             
8371             if(!this.colModel.config[col_index][size_cls[0]]) {
8372                 continue;
8373             }
8374             
8375             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8376                 continue;
8377             }
8378             
8379             h_row[0].classList.replace(
8380                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8381                 "col-"+size_cls[0]+"-"+size_cls[1]
8382             );
8383             
8384             for(var i = 0; i < rows.length; i++) {
8385                 
8386                 var size_cls = w[j].split("-");
8387                 
8388                 if(!Number.isInteger(size_cls[1] * 1)) {
8389                     continue;
8390                 }
8391                 
8392                 if(!this.colModel.config[col_index][size_cls[0]]) {
8393                     continue;
8394                 }
8395                 
8396                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8397                     continue;
8398                 }
8399                 
8400                 rows[i].classList.replace(
8401                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8402                     "col-"+size_cls[0]+"-"+size_cls[1]
8403                 );
8404             }
8405             
8406             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8407         }
8408     }
8409 });
8410
8411  
8412
8413  /*
8414  * - LGPL
8415  *
8416  * table cell
8417  * 
8418  */
8419
8420 /**
8421  * @class Roo.bootstrap.TableCell
8422  * @extends Roo.bootstrap.Component
8423  * Bootstrap TableCell class
8424  * @cfg {String} html cell contain text
8425  * @cfg {String} cls cell class
8426  * @cfg {String} tag cell tag (td|th) default td
8427  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8428  * @cfg {String} align Aligns the content in a cell
8429  * @cfg {String} axis Categorizes cells
8430  * @cfg {String} bgcolor Specifies the background color of a cell
8431  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8432  * @cfg {Number} colspan Specifies the number of columns a cell should span
8433  * @cfg {String} headers Specifies one or more header cells a cell is related to
8434  * @cfg {Number} height Sets the height of a cell
8435  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8436  * @cfg {Number} rowspan Sets the number of rows a cell should span
8437  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8438  * @cfg {String} valign Vertical aligns the content in a cell
8439  * @cfg {Number} width Specifies the width of a cell
8440  * 
8441  * @constructor
8442  * Create a new TableCell
8443  * @param {Object} config The config object
8444  */
8445
8446 Roo.bootstrap.TableCell = function(config){
8447     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8448 };
8449
8450 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
8451     
8452     html: false,
8453     cls: false,
8454     tag: false,
8455     abbr: false,
8456     align: false,
8457     axis: false,
8458     bgcolor: false,
8459     charoff: false,
8460     colspan: false,
8461     headers: false,
8462     height: false,
8463     nowrap: false,
8464     rowspan: false,
8465     scope: false,
8466     valign: false,
8467     width: false,
8468     
8469     
8470     getAutoCreate : function(){
8471         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8472         
8473         cfg = {
8474             tag: 'td'
8475         };
8476         
8477         if(this.tag){
8478             cfg.tag = this.tag;
8479         }
8480         
8481         if (this.html) {
8482             cfg.html=this.html
8483         }
8484         if (this.cls) {
8485             cfg.cls=this.cls
8486         }
8487         if (this.abbr) {
8488             cfg.abbr=this.abbr
8489         }
8490         if (this.align) {
8491             cfg.align=this.align
8492         }
8493         if (this.axis) {
8494             cfg.axis=this.axis
8495         }
8496         if (this.bgcolor) {
8497             cfg.bgcolor=this.bgcolor
8498         }
8499         if (this.charoff) {
8500             cfg.charoff=this.charoff
8501         }
8502         if (this.colspan) {
8503             cfg.colspan=this.colspan
8504         }
8505         if (this.headers) {
8506             cfg.headers=this.headers
8507         }
8508         if (this.height) {
8509             cfg.height=this.height
8510         }
8511         if (this.nowrap) {
8512             cfg.nowrap=this.nowrap
8513         }
8514         if (this.rowspan) {
8515             cfg.rowspan=this.rowspan
8516         }
8517         if (this.scope) {
8518             cfg.scope=this.scope
8519         }
8520         if (this.valign) {
8521             cfg.valign=this.valign
8522         }
8523         if (this.width) {
8524             cfg.width=this.width
8525         }
8526         
8527         
8528         return cfg;
8529     }
8530    
8531 });
8532
8533  
8534
8535  /*
8536  * - LGPL
8537  *
8538  * table row
8539  * 
8540  */
8541
8542 /**
8543  * @class Roo.bootstrap.TableRow
8544  * @extends Roo.bootstrap.Component
8545  * Bootstrap TableRow class
8546  * @cfg {String} cls row class
8547  * @cfg {String} align Aligns the content in a table row
8548  * @cfg {String} bgcolor Specifies a background color for a table row
8549  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8550  * @cfg {String} valign Vertical aligns the content in a table row
8551  * 
8552  * @constructor
8553  * Create a new TableRow
8554  * @param {Object} config The config object
8555  */
8556
8557 Roo.bootstrap.TableRow = function(config){
8558     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
8559 };
8560
8561 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
8562     
8563     cls: false,
8564     align: false,
8565     bgcolor: false,
8566     charoff: false,
8567     valign: false,
8568     
8569     getAutoCreate : function(){
8570         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
8571         
8572         cfg = {
8573             tag: 'tr'
8574         };
8575             
8576         if(this.cls){
8577             cfg.cls = this.cls;
8578         }
8579         if(this.align){
8580             cfg.align = this.align;
8581         }
8582         if(this.bgcolor){
8583             cfg.bgcolor = this.bgcolor;
8584         }
8585         if(this.charoff){
8586             cfg.charoff = this.charoff;
8587         }
8588         if(this.valign){
8589             cfg.valign = this.valign;
8590         }
8591         
8592         return cfg;
8593     }
8594    
8595 });
8596
8597  
8598
8599  /*
8600  * - LGPL
8601  *
8602  * table body
8603  * 
8604  */
8605
8606 /**
8607  * @class Roo.bootstrap.TableBody
8608  * @extends Roo.bootstrap.Component
8609  * Bootstrap TableBody class
8610  * @cfg {String} cls element class
8611  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
8612  * @cfg {String} align Aligns the content inside the element
8613  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
8614  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
8615  * 
8616  * @constructor
8617  * Create a new TableBody
8618  * @param {Object} config The config object
8619  */
8620
8621 Roo.bootstrap.TableBody = function(config){
8622     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
8623 };
8624
8625 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
8626     
8627     cls: false,
8628     tag: false,
8629     align: false,
8630     charoff: false,
8631     valign: false,
8632     
8633     getAutoCreate : function(){
8634         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
8635         
8636         cfg = {
8637             tag: 'tbody'
8638         };
8639             
8640         if (this.cls) {
8641             cfg.cls=this.cls
8642         }
8643         if(this.tag){
8644             cfg.tag = this.tag;
8645         }
8646         
8647         if(this.align){
8648             cfg.align = this.align;
8649         }
8650         if(this.charoff){
8651             cfg.charoff = this.charoff;
8652         }
8653         if(this.valign){
8654             cfg.valign = this.valign;
8655         }
8656         
8657         return cfg;
8658     }
8659     
8660     
8661 //    initEvents : function()
8662 //    {
8663 //        
8664 //        if(!this.store){
8665 //            return;
8666 //        }
8667 //        
8668 //        this.store = Roo.factory(this.store, Roo.data);
8669 //        this.store.on('load', this.onLoad, this);
8670 //        
8671 //        this.store.load();
8672 //        
8673 //    },
8674 //    
8675 //    onLoad: function () 
8676 //    {   
8677 //        this.fireEvent('load', this);
8678 //    }
8679 //    
8680 //   
8681 });
8682
8683  
8684
8685  /*
8686  * Based on:
8687  * Ext JS Library 1.1.1
8688  * Copyright(c) 2006-2007, Ext JS, LLC.
8689  *
8690  * Originally Released Under LGPL - original licence link has changed is not relivant.
8691  *
8692  * Fork - LGPL
8693  * <script type="text/javascript">
8694  */
8695
8696 // as we use this in bootstrap.
8697 Roo.namespace('Roo.form');
8698  /**
8699  * @class Roo.form.Action
8700  * Internal Class used to handle form actions
8701  * @constructor
8702  * @param {Roo.form.BasicForm} el The form element or its id
8703  * @param {Object} config Configuration options
8704  */
8705
8706  
8707  
8708 // define the action interface
8709 Roo.form.Action = function(form, options){
8710     this.form = form;
8711     this.options = options || {};
8712 };
8713 /**
8714  * Client Validation Failed
8715  * @const 
8716  */
8717 Roo.form.Action.CLIENT_INVALID = 'client';
8718 /**
8719  * Server Validation Failed
8720  * @const 
8721  */
8722 Roo.form.Action.SERVER_INVALID = 'server';
8723  /**
8724  * Connect to Server Failed
8725  * @const 
8726  */
8727 Roo.form.Action.CONNECT_FAILURE = 'connect';
8728 /**
8729  * Reading Data from Server Failed
8730  * @const 
8731  */
8732 Roo.form.Action.LOAD_FAILURE = 'load';
8733
8734 Roo.form.Action.prototype = {
8735     type : 'default',
8736     failureType : undefined,
8737     response : undefined,
8738     result : undefined,
8739
8740     // interface method
8741     run : function(options){
8742
8743     },
8744
8745     // interface method
8746     success : function(response){
8747
8748     },
8749
8750     // interface method
8751     handleResponse : function(response){
8752
8753     },
8754
8755     // default connection failure
8756     failure : function(response){
8757         
8758         this.response = response;
8759         this.failureType = Roo.form.Action.CONNECT_FAILURE;
8760         this.form.afterAction(this, false);
8761     },
8762
8763     processResponse : function(response){
8764         this.response = response;
8765         if(!response.responseText){
8766             return true;
8767         }
8768         this.result = this.handleResponse(response);
8769         return this.result;
8770     },
8771
8772     // utility functions used internally
8773     getUrl : function(appendParams){
8774         var url = this.options.url || this.form.url || this.form.el.dom.action;
8775         if(appendParams){
8776             var p = this.getParams();
8777             if(p){
8778                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
8779             }
8780         }
8781         return url;
8782     },
8783
8784     getMethod : function(){
8785         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
8786     },
8787
8788     getParams : function(){
8789         var bp = this.form.baseParams;
8790         var p = this.options.params;
8791         if(p){
8792             if(typeof p == "object"){
8793                 p = Roo.urlEncode(Roo.applyIf(p, bp));
8794             }else if(typeof p == 'string' && bp){
8795                 p += '&' + Roo.urlEncode(bp);
8796             }
8797         }else if(bp){
8798             p = Roo.urlEncode(bp);
8799         }
8800         return p;
8801     },
8802
8803     createCallback : function(){
8804         return {
8805             success: this.success,
8806             failure: this.failure,
8807             scope: this,
8808             timeout: (this.form.timeout*1000),
8809             upload: this.form.fileUpload ? this.success : undefined
8810         };
8811     }
8812 };
8813
8814 Roo.form.Action.Submit = function(form, options){
8815     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
8816 };
8817
8818 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
8819     type : 'submit',
8820
8821     haveProgress : false,
8822     uploadComplete : false,
8823     
8824     // uploadProgress indicator.
8825     uploadProgress : function()
8826     {
8827         if (!this.form.progressUrl) {
8828             return;
8829         }
8830         
8831         if (!this.haveProgress) {
8832             Roo.MessageBox.progress("Uploading", "Uploading");
8833         }
8834         if (this.uploadComplete) {
8835            Roo.MessageBox.hide();
8836            return;
8837         }
8838         
8839         this.haveProgress = true;
8840    
8841         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
8842         
8843         var c = new Roo.data.Connection();
8844         c.request({
8845             url : this.form.progressUrl,
8846             params: {
8847                 id : uid
8848             },
8849             method: 'GET',
8850             success : function(req){
8851                //console.log(data);
8852                 var rdata = false;
8853                 var edata;
8854                 try  {
8855                    rdata = Roo.decode(req.responseText)
8856                 } catch (e) {
8857                     Roo.log("Invalid data from server..");
8858                     Roo.log(edata);
8859                     return;
8860                 }
8861                 if (!rdata || !rdata.success) {
8862                     Roo.log(rdata);
8863                     Roo.MessageBox.alert(Roo.encode(rdata));
8864                     return;
8865                 }
8866                 var data = rdata.data;
8867                 
8868                 if (this.uploadComplete) {
8869                    Roo.MessageBox.hide();
8870                    return;
8871                 }
8872                    
8873                 if (data){
8874                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
8875                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
8876                     );
8877                 }
8878                 this.uploadProgress.defer(2000,this);
8879             },
8880        
8881             failure: function(data) {
8882                 Roo.log('progress url failed ');
8883                 Roo.log(data);
8884             },
8885             scope : this
8886         });
8887            
8888     },
8889     
8890     
8891     run : function()
8892     {
8893         // run get Values on the form, so it syncs any secondary forms.
8894         this.form.getValues();
8895         
8896         var o = this.options;
8897         var method = this.getMethod();
8898         var isPost = method == 'POST';
8899         if(o.clientValidation === false || this.form.isValid()){
8900             
8901             if (this.form.progressUrl) {
8902                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
8903                     (new Date() * 1) + '' + Math.random());
8904                     
8905             } 
8906             
8907             
8908             Roo.Ajax.request(Roo.apply(this.createCallback(), {
8909                 form:this.form.el.dom,
8910                 url:this.getUrl(!isPost),
8911                 method: method,
8912                 params:isPost ? this.getParams() : null,
8913                 isUpload: this.form.fileUpload,
8914                 formData : this.form.formData
8915             }));
8916             
8917             this.uploadProgress();
8918
8919         }else if (o.clientValidation !== false){ // client validation failed
8920             this.failureType = Roo.form.Action.CLIENT_INVALID;
8921             this.form.afterAction(this, false);
8922         }
8923     },
8924
8925     success : function(response)
8926     {
8927         this.uploadComplete= true;
8928         if (this.haveProgress) {
8929             Roo.MessageBox.hide();
8930         }
8931         
8932         
8933         var result = this.processResponse(response);
8934         if(result === true || result.success){
8935             this.form.afterAction(this, true);
8936             return;
8937         }
8938         if(result.errors){
8939             this.form.markInvalid(result.errors);
8940             this.failureType = Roo.form.Action.SERVER_INVALID;
8941         }
8942         this.form.afterAction(this, false);
8943     },
8944     failure : function(response)
8945     {
8946         this.uploadComplete= true;
8947         if (this.haveProgress) {
8948             Roo.MessageBox.hide();
8949         }
8950         
8951         this.response = response;
8952         this.failureType = Roo.form.Action.CONNECT_FAILURE;
8953         this.form.afterAction(this, false);
8954     },
8955     
8956     handleResponse : function(response){
8957         if(this.form.errorReader){
8958             var rs = this.form.errorReader.read(response);
8959             var errors = [];
8960             if(rs.records){
8961                 for(var i = 0, len = rs.records.length; i < len; i++) {
8962                     var r = rs.records[i];
8963                     errors[i] = r.data;
8964                 }
8965             }
8966             if(errors.length < 1){
8967                 errors = null;
8968             }
8969             return {
8970                 success : rs.success,
8971                 errors : errors
8972             };
8973         }
8974         var ret = false;
8975         try {
8976             ret = Roo.decode(response.responseText);
8977         } catch (e) {
8978             ret = {
8979                 success: false,
8980                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
8981                 errors : []
8982             };
8983         }
8984         return ret;
8985         
8986     }
8987 });
8988
8989
8990 Roo.form.Action.Load = function(form, options){
8991     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
8992     this.reader = this.form.reader;
8993 };
8994
8995 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
8996     type : 'load',
8997
8998     run : function(){
8999         
9000         Roo.Ajax.request(Roo.apply(
9001                 this.createCallback(), {
9002                     method:this.getMethod(),
9003                     url:this.getUrl(false),
9004                     params:this.getParams()
9005         }));
9006     },
9007
9008     success : function(response){
9009         
9010         var result = this.processResponse(response);
9011         if(result === true || !result.success || !result.data){
9012             this.failureType = Roo.form.Action.LOAD_FAILURE;
9013             this.form.afterAction(this, false);
9014             return;
9015         }
9016         this.form.clearInvalid();
9017         this.form.setValues(result.data);
9018         this.form.afterAction(this, true);
9019     },
9020
9021     handleResponse : function(response){
9022         if(this.form.reader){
9023             var rs = this.form.reader.read(response);
9024             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9025             return {
9026                 success : rs.success,
9027                 data : data
9028             };
9029         }
9030         return Roo.decode(response.responseText);
9031     }
9032 });
9033
9034 Roo.form.Action.ACTION_TYPES = {
9035     'load' : Roo.form.Action.Load,
9036     'submit' : Roo.form.Action.Submit
9037 };/*
9038  * - LGPL
9039  *
9040  * form
9041  *
9042  */
9043
9044 /**
9045  * @class Roo.bootstrap.Form
9046  * @extends Roo.bootstrap.Component
9047  * Bootstrap Form class
9048  * @cfg {String} method  GET | POST (default POST)
9049  * @cfg {String} labelAlign top | left (default top)
9050  * @cfg {String} align left  | right - for navbars
9051  * @cfg {Boolean} loadMask load mask when submit (default true)
9052
9053  *
9054  * @constructor
9055  * Create a new Form
9056  * @param {Object} config The config object
9057  */
9058
9059
9060 Roo.bootstrap.Form = function(config){
9061     
9062     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9063     
9064     Roo.bootstrap.Form.popover.apply();
9065     
9066     this.addEvents({
9067         /**
9068          * @event clientvalidation
9069          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9070          * @param {Form} this
9071          * @param {Boolean} valid true if the form has passed client-side validation
9072          */
9073         clientvalidation: true,
9074         /**
9075          * @event beforeaction
9076          * Fires before any action is performed. Return false to cancel the action.
9077          * @param {Form} this
9078          * @param {Action} action The action to be performed
9079          */
9080         beforeaction: true,
9081         /**
9082          * @event actionfailed
9083          * Fires when an action fails.
9084          * @param {Form} this
9085          * @param {Action} action The action that failed
9086          */
9087         actionfailed : true,
9088         /**
9089          * @event actioncomplete
9090          * Fires when an action is completed.
9091          * @param {Form} this
9092          * @param {Action} action The action that completed
9093          */
9094         actioncomplete : true
9095     });
9096 };
9097
9098 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9099
9100      /**
9101      * @cfg {String} method
9102      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9103      */
9104     method : 'POST',
9105     /**
9106      * @cfg {String} url
9107      * The URL to use for form actions if one isn't supplied in the action options.
9108      */
9109     /**
9110      * @cfg {Boolean} fileUpload
9111      * Set to true if this form is a file upload.
9112      */
9113
9114     /**
9115      * @cfg {Object} baseParams
9116      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9117      */
9118
9119     /**
9120      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9121      */
9122     timeout: 30,
9123     /**
9124      * @cfg {Sting} align (left|right) for navbar forms
9125      */
9126     align : 'left',
9127
9128     // private
9129     activeAction : null,
9130
9131     /**
9132      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9133      * element by passing it or its id or mask the form itself by passing in true.
9134      * @type Mixed
9135      */
9136     waitMsgTarget : false,
9137
9138     loadMask : true,
9139     
9140     /**
9141      * @cfg {Boolean} errorMask (true|false) default false
9142      */
9143     errorMask : false,
9144     
9145     /**
9146      * @cfg {Number} maskOffset Default 100
9147      */
9148     maskOffset : 100,
9149     
9150     /**
9151      * @cfg {Boolean} maskBody
9152      */
9153     maskBody : false,
9154
9155     getAutoCreate : function(){
9156
9157         var cfg = {
9158             tag: 'form',
9159             method : this.method || 'POST',
9160             id : this.id || Roo.id(),
9161             cls : ''
9162         };
9163         if (this.parent().xtype.match(/^Nav/)) {
9164             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9165
9166         }
9167
9168         if (this.labelAlign == 'left' ) {
9169             cfg.cls += ' form-horizontal';
9170         }
9171
9172
9173         return cfg;
9174     },
9175     initEvents : function()
9176     {
9177         this.el.on('submit', this.onSubmit, this);
9178         // this was added as random key presses on the form where triggering form submit.
9179         this.el.on('keypress', function(e) {
9180             if (e.getCharCode() != 13) {
9181                 return true;
9182             }
9183             // we might need to allow it for textareas.. and some other items.
9184             // check e.getTarget().
9185
9186             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9187                 return true;
9188             }
9189
9190             Roo.log("keypress blocked");
9191
9192             e.preventDefault();
9193             return false;
9194         });
9195         
9196     },
9197     // private
9198     onSubmit : function(e){
9199         e.stopEvent();
9200     },
9201
9202      /**
9203      * Returns true if client-side validation on the form is successful.
9204      * @return Boolean
9205      */
9206     isValid : function(){
9207         var items = this.getItems();
9208         var valid = true;
9209         var target = false;
9210         
9211         items.each(function(f){
9212             
9213             if(f.validate()){
9214                 return;
9215             }
9216             
9217             Roo.log('invalid field: ' + f.name);
9218             
9219             valid = false;
9220
9221             if(!target && f.el.isVisible(true)){
9222                 target = f;
9223             }
9224            
9225         });
9226         
9227         if(this.errorMask && !valid){
9228             Roo.bootstrap.Form.popover.mask(this, target);
9229         }
9230         
9231         return valid;
9232     },
9233     
9234     /**
9235      * Returns true if any fields in this form have changed since their original load.
9236      * @return Boolean
9237      */
9238     isDirty : function(){
9239         var dirty = false;
9240         var items = this.getItems();
9241         items.each(function(f){
9242            if(f.isDirty()){
9243                dirty = true;
9244                return false;
9245            }
9246            return true;
9247         });
9248         return dirty;
9249     },
9250      /**
9251      * Performs a predefined action (submit or load) or custom actions you define on this form.
9252      * @param {String} actionName The name of the action type
9253      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
9254      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9255      * accept other config options):
9256      * <pre>
9257 Property          Type             Description
9258 ----------------  ---------------  ----------------------------------------------------------------------------------
9259 url               String           The url for the action (defaults to the form's url)
9260 method            String           The form method to use (defaults to the form's method, or POST if not defined)
9261 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
9262 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
9263                                    validate the form on the client (defaults to false)
9264      * </pre>
9265      * @return {BasicForm} this
9266      */
9267     doAction : function(action, options){
9268         if(typeof action == 'string'){
9269             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9270         }
9271         if(this.fireEvent('beforeaction', this, action) !== false){
9272             this.beforeAction(action);
9273             action.run.defer(100, action);
9274         }
9275         return this;
9276     },
9277
9278     // private
9279     beforeAction : function(action){
9280         var o = action.options;
9281         
9282         if(this.loadMask){
9283             
9284             if(this.maskBody){
9285                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9286             } else {
9287                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9288             }
9289         }
9290         // not really supported yet.. ??
9291
9292         //if(this.waitMsgTarget === true){
9293         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9294         //}else if(this.waitMsgTarget){
9295         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9296         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9297         //}else {
9298         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9299        // }
9300
9301     },
9302
9303     // private
9304     afterAction : function(action, success){
9305         this.activeAction = null;
9306         var o = action.options;
9307
9308         if(this.loadMask){
9309             
9310             if(this.maskBody){
9311                 Roo.get(document.body).unmask();
9312             } else {
9313                 this.el.unmask();
9314             }
9315         }
9316         
9317         //if(this.waitMsgTarget === true){
9318 //            this.el.unmask();
9319         //}else if(this.waitMsgTarget){
9320         //    this.waitMsgTarget.unmask();
9321         //}else{
9322         //    Roo.MessageBox.updateProgress(1);
9323         //    Roo.MessageBox.hide();
9324        // }
9325         //
9326         if(success){
9327             if(o.reset){
9328                 this.reset();
9329             }
9330             Roo.callback(o.success, o.scope, [this, action]);
9331             this.fireEvent('actioncomplete', this, action);
9332
9333         }else{
9334
9335             // failure condition..
9336             // we have a scenario where updates need confirming.
9337             // eg. if a locking scenario exists..
9338             // we look for { errors : { needs_confirm : true }} in the response.
9339             if (
9340                 (typeof(action.result) != 'undefined')  &&
9341                 (typeof(action.result.errors) != 'undefined')  &&
9342                 (typeof(action.result.errors.needs_confirm) != 'undefined')
9343            ){
9344                 var _t = this;
9345                 Roo.log("not supported yet");
9346                  /*
9347
9348                 Roo.MessageBox.confirm(
9349                     "Change requires confirmation",
9350                     action.result.errorMsg,
9351                     function(r) {
9352                         if (r != 'yes') {
9353                             return;
9354                         }
9355                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
9356                     }
9357
9358                 );
9359                 */
9360
9361
9362                 return;
9363             }
9364
9365             Roo.callback(o.failure, o.scope, [this, action]);
9366             // show an error message if no failed handler is set..
9367             if (!this.hasListener('actionfailed')) {
9368                 Roo.log("need to add dialog support");
9369                 /*
9370                 Roo.MessageBox.alert("Error",
9371                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9372                         action.result.errorMsg :
9373                         "Saving Failed, please check your entries or try again"
9374                 );
9375                 */
9376             }
9377
9378             this.fireEvent('actionfailed', this, action);
9379         }
9380
9381     },
9382     /**
9383      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9384      * @param {String} id The value to search for
9385      * @return Field
9386      */
9387     findField : function(id){
9388         var items = this.getItems();
9389         var field = items.get(id);
9390         if(!field){
9391              items.each(function(f){
9392                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9393                     field = f;
9394                     return false;
9395                 }
9396                 return true;
9397             });
9398         }
9399         return field || null;
9400     },
9401      /**
9402      * Mark fields in this form invalid in bulk.
9403      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9404      * @return {BasicForm} this
9405      */
9406     markInvalid : function(errors){
9407         if(errors instanceof Array){
9408             for(var i = 0, len = errors.length; i < len; i++){
9409                 var fieldError = errors[i];
9410                 var f = this.findField(fieldError.id);
9411                 if(f){
9412                     f.markInvalid(fieldError.msg);
9413                 }
9414             }
9415         }else{
9416             var field, id;
9417             for(id in errors){
9418                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9419                     field.markInvalid(errors[id]);
9420                 }
9421             }
9422         }
9423         //Roo.each(this.childForms || [], function (f) {
9424         //    f.markInvalid(errors);
9425         //});
9426
9427         return this;
9428     },
9429
9430     /**
9431      * Set values for fields in this form in bulk.
9432      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9433      * @return {BasicForm} this
9434      */
9435     setValues : function(values){
9436         if(values instanceof Array){ // array of objects
9437             for(var i = 0, len = values.length; i < len; i++){
9438                 var v = values[i];
9439                 var f = this.findField(v.id);
9440                 if(f){
9441                     f.setValue(v.value);
9442                     if(this.trackResetOnLoad){
9443                         f.originalValue = f.getValue();
9444                     }
9445                 }
9446             }
9447         }else{ // object hash
9448             var field, id;
9449             for(id in values){
9450                 if(typeof values[id] != 'function' && (field = this.findField(id))){
9451
9452                     if (field.setFromData &&
9453                         field.valueField &&
9454                         field.displayField &&
9455                         // combos' with local stores can
9456                         // be queried via setValue()
9457                         // to set their value..
9458                         (field.store && !field.store.isLocal)
9459                         ) {
9460                         // it's a combo
9461                         var sd = { };
9462                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9463                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9464                         field.setFromData(sd);
9465
9466                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9467                         
9468                         field.setFromData(values);
9469                         
9470                     } else {
9471                         field.setValue(values[id]);
9472                     }
9473
9474
9475                     if(this.trackResetOnLoad){
9476                         field.originalValue = field.getValue();
9477                     }
9478                 }
9479             }
9480         }
9481
9482         //Roo.each(this.childForms || [], function (f) {
9483         //    f.setValues(values);
9484         //});
9485
9486         return this;
9487     },
9488
9489     /**
9490      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9491      * they are returned as an array.
9492      * @param {Boolean} asString
9493      * @return {Object}
9494      */
9495     getValues : function(asString){
9496         //if (this.childForms) {
9497             // copy values from the child forms
9498         //    Roo.each(this.childForms, function (f) {
9499         //        this.setValues(f.getValues());
9500         //    }, this);
9501         //}
9502
9503
9504
9505         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
9506         if(asString === true){
9507             return fs;
9508         }
9509         return Roo.urlDecode(fs);
9510     },
9511
9512     /**
9513      * Returns the fields in this form as an object with key/value pairs.
9514      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
9515      * @return {Object}
9516      */
9517     getFieldValues : function(with_hidden)
9518     {
9519         var items = this.getItems();
9520         var ret = {};
9521         items.each(function(f){
9522             
9523             if (!f.getName()) {
9524                 return;
9525             }
9526             
9527             var v = f.getValue();
9528             
9529             if (f.inputType =='radio') {
9530                 if (typeof(ret[f.getName()]) == 'undefined') {
9531                     ret[f.getName()] = ''; // empty..
9532                 }
9533
9534                 if (!f.el.dom.checked) {
9535                     return;
9536
9537                 }
9538                 v = f.el.dom.value;
9539
9540             }
9541             
9542             if(f.xtype == 'MoneyField'){
9543                 ret[f.currencyName] = f.getCurrency();
9544             }
9545
9546             // not sure if this supported any more..
9547             if ((typeof(v) == 'object') && f.getRawValue) {
9548                 v = f.getRawValue() ; // dates..
9549             }
9550             // combo boxes where name != hiddenName...
9551             if (f.name !== false && f.name != '' && f.name != f.getName()) {
9552                 ret[f.name] = f.getRawValue();
9553             }
9554             ret[f.getName()] = v;
9555         });
9556
9557         return ret;
9558     },
9559
9560     /**
9561      * Clears all invalid messages in this form.
9562      * @return {BasicForm} this
9563      */
9564     clearInvalid : function(){
9565         var items = this.getItems();
9566
9567         items.each(function(f){
9568            f.clearInvalid();
9569         });
9570
9571         return this;
9572     },
9573
9574     /**
9575      * Resets this form.
9576      * @return {BasicForm} this
9577      */
9578     reset : function(){
9579         var items = this.getItems();
9580         items.each(function(f){
9581             f.reset();
9582         });
9583
9584         Roo.each(this.childForms || [], function (f) {
9585             f.reset();
9586         });
9587
9588
9589         return this;
9590     },
9591     
9592     getItems : function()
9593     {
9594         var r=new Roo.util.MixedCollection(false, function(o){
9595             return o.id || (o.id = Roo.id());
9596         });
9597         var iter = function(el) {
9598             if (el.inputEl) {
9599                 r.add(el);
9600             }
9601             if (!el.items) {
9602                 return;
9603             }
9604             Roo.each(el.items,function(e) {
9605                 iter(e);
9606             });
9607         };
9608
9609         iter(this);
9610         return r;
9611     },
9612     
9613     hideFields : function(items)
9614     {
9615         Roo.each(items, function(i){
9616             
9617             var f = this.findField(i);
9618             
9619             if(!f){
9620                 return;
9621             }
9622             
9623             f.hide();
9624             
9625         }, this);
9626     },
9627     
9628     showFields : function(items)
9629     {
9630         Roo.each(items, function(i){
9631             
9632             var f = this.findField(i);
9633             
9634             if(!f){
9635                 return;
9636             }
9637             
9638             f.show();
9639             
9640         }, this);
9641     }
9642
9643 });
9644
9645 Roo.apply(Roo.bootstrap.Form, {
9646     
9647     popover : {
9648         
9649         padding : 5,
9650         
9651         isApplied : false,
9652         
9653         isMasked : false,
9654         
9655         form : false,
9656         
9657         target : false,
9658         
9659         toolTip : false,
9660         
9661         intervalID : false,
9662         
9663         maskEl : false,
9664         
9665         apply : function()
9666         {
9667             if(this.isApplied){
9668                 return;
9669             }
9670             
9671             this.maskEl = {
9672                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
9673                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
9674                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
9675                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
9676             };
9677             
9678             this.maskEl.top.enableDisplayMode("block");
9679             this.maskEl.left.enableDisplayMode("block");
9680             this.maskEl.bottom.enableDisplayMode("block");
9681             this.maskEl.right.enableDisplayMode("block");
9682             
9683             this.toolTip = new Roo.bootstrap.Tooltip({
9684                 cls : 'roo-form-error-popover',
9685                 alignment : {
9686                     'left' : ['r-l', [-2,0], 'right'],
9687                     'right' : ['l-r', [2,0], 'left'],
9688                     'bottom' : ['tl-bl', [0,2], 'top'],
9689                     'top' : [ 'bl-tl', [0,-2], 'bottom']
9690                 }
9691             });
9692             
9693             this.toolTip.render(Roo.get(document.body));
9694
9695             this.toolTip.el.enableDisplayMode("block");
9696             
9697             Roo.get(document.body).on('click', function(){
9698                 this.unmask();
9699             }, this);
9700             
9701             Roo.get(document.body).on('touchstart', function(){
9702                 this.unmask();
9703             }, this);
9704             
9705             this.isApplied = true
9706         },
9707         
9708         mask : function(form, target)
9709         {
9710             this.form = form;
9711             
9712             this.target = target;
9713             
9714             if(!this.form.errorMask || !target.el){
9715                 return;
9716             }
9717             
9718             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
9719             
9720             Roo.log(scrollable);
9721             
9722             var ot = this.target.el.calcOffsetsTo(scrollable);
9723             
9724             var scrollTo = ot[1] - this.form.maskOffset;
9725             
9726             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
9727             
9728             scrollable.scrollTo('top', scrollTo);
9729             
9730             var box = this.target.el.getBox();
9731             Roo.log(box);
9732             var zIndex = Roo.bootstrap.Modal.zIndex++;
9733
9734             
9735             this.maskEl.top.setStyle('position', 'absolute');
9736             this.maskEl.top.setStyle('z-index', zIndex);
9737             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
9738             this.maskEl.top.setLeft(0);
9739             this.maskEl.top.setTop(0);
9740             this.maskEl.top.show();
9741             
9742             this.maskEl.left.setStyle('position', 'absolute');
9743             this.maskEl.left.setStyle('z-index', zIndex);
9744             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
9745             this.maskEl.left.setLeft(0);
9746             this.maskEl.left.setTop(box.y - this.padding);
9747             this.maskEl.left.show();
9748
9749             this.maskEl.bottom.setStyle('position', 'absolute');
9750             this.maskEl.bottom.setStyle('z-index', zIndex);
9751             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
9752             this.maskEl.bottom.setLeft(0);
9753             this.maskEl.bottom.setTop(box.bottom + this.padding);
9754             this.maskEl.bottom.show();
9755
9756             this.maskEl.right.setStyle('position', 'absolute');
9757             this.maskEl.right.setStyle('z-index', zIndex);
9758             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
9759             this.maskEl.right.setLeft(box.right + this.padding);
9760             this.maskEl.right.setTop(box.y - this.padding);
9761             this.maskEl.right.show();
9762
9763             this.toolTip.bindEl = this.target.el;
9764
9765             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
9766
9767             var tip = this.target.blankText;
9768
9769             if(this.target.getValue() !== '' ) {
9770                 
9771                 if (this.target.invalidText.length) {
9772                     tip = this.target.invalidText;
9773                 } else if (this.target.regexText.length){
9774                     tip = this.target.regexText;
9775                 }
9776             }
9777
9778             this.toolTip.show(tip);
9779
9780             this.intervalID = window.setInterval(function() {
9781                 Roo.bootstrap.Form.popover.unmask();
9782             }, 10000);
9783
9784             window.onwheel = function(){ return false;};
9785             
9786             (function(){ this.isMasked = true; }).defer(500, this);
9787             
9788         },
9789         
9790         unmask : function()
9791         {
9792             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
9793                 return;
9794             }
9795             
9796             this.maskEl.top.setStyle('position', 'absolute');
9797             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
9798             this.maskEl.top.hide();
9799
9800             this.maskEl.left.setStyle('position', 'absolute');
9801             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
9802             this.maskEl.left.hide();
9803
9804             this.maskEl.bottom.setStyle('position', 'absolute');
9805             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
9806             this.maskEl.bottom.hide();
9807
9808             this.maskEl.right.setStyle('position', 'absolute');
9809             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
9810             this.maskEl.right.hide();
9811             
9812             this.toolTip.hide();
9813             
9814             this.toolTip.el.hide();
9815             
9816             window.onwheel = function(){ return true;};
9817             
9818             if(this.intervalID){
9819                 window.clearInterval(this.intervalID);
9820                 this.intervalID = false;
9821             }
9822             
9823             this.isMasked = false;
9824             
9825         }
9826         
9827     }
9828     
9829 });
9830
9831 /*
9832  * Based on:
9833  * Ext JS Library 1.1.1
9834  * Copyright(c) 2006-2007, Ext JS, LLC.
9835  *
9836  * Originally Released Under LGPL - original licence link has changed is not relivant.
9837  *
9838  * Fork - LGPL
9839  * <script type="text/javascript">
9840  */
9841 /**
9842  * @class Roo.form.VTypes
9843  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
9844  * @singleton
9845  */
9846 Roo.form.VTypes = function(){
9847     // closure these in so they are only created once.
9848     var alpha = /^[a-zA-Z_]+$/;
9849     var alphanum = /^[a-zA-Z0-9_]+$/;
9850     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
9851     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
9852
9853     // All these messages and functions are configurable
9854     return {
9855         /**
9856          * The function used to validate email addresses
9857          * @param {String} value The email address
9858          */
9859         'email' : function(v){
9860             return email.test(v);
9861         },
9862         /**
9863          * The error text to display when the email validation function returns false
9864          * @type String
9865          */
9866         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
9867         /**
9868          * The keystroke filter mask to be applied on email input
9869          * @type RegExp
9870          */
9871         'emailMask' : /[a-z0-9_\.\-@]/i,
9872
9873         /**
9874          * The function used to validate URLs
9875          * @param {String} value The URL
9876          */
9877         'url' : function(v){
9878             return url.test(v);
9879         },
9880         /**
9881          * The error text to display when the url validation function returns false
9882          * @type String
9883          */
9884         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
9885         
9886         /**
9887          * The function used to validate alpha values
9888          * @param {String} value The value
9889          */
9890         'alpha' : function(v){
9891             return alpha.test(v);
9892         },
9893         /**
9894          * The error text to display when the alpha validation function returns false
9895          * @type String
9896          */
9897         'alphaText' : 'This field should only contain letters and _',
9898         /**
9899          * The keystroke filter mask to be applied on alpha input
9900          * @type RegExp
9901          */
9902         'alphaMask' : /[a-z_]/i,
9903
9904         /**
9905          * The function used to validate alphanumeric values
9906          * @param {String} value The value
9907          */
9908         'alphanum' : function(v){
9909             return alphanum.test(v);
9910         },
9911         /**
9912          * The error text to display when the alphanumeric validation function returns false
9913          * @type String
9914          */
9915         'alphanumText' : 'This field should only contain letters, numbers and _',
9916         /**
9917          * The keystroke filter mask to be applied on alphanumeric input
9918          * @type RegExp
9919          */
9920         'alphanumMask' : /[a-z0-9_]/i
9921     };
9922 }();/*
9923  * - LGPL
9924  *
9925  * Input
9926  * 
9927  */
9928
9929 /**
9930  * @class Roo.bootstrap.Input
9931  * @extends Roo.bootstrap.Component
9932  * Bootstrap Input class
9933  * @cfg {Boolean} disabled is it disabled
9934  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
9935  * @cfg {String} name name of the input
9936  * @cfg {string} fieldLabel - the label associated
9937  * @cfg {string} placeholder - placeholder to put in text.
9938  * @cfg {string}  before - input group add on before
9939  * @cfg {string} after - input group add on after
9940  * @cfg {string} size - (lg|sm) or leave empty..
9941  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
9942  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
9943  * @cfg {Number} md colspan out of 12 for computer-sized screens
9944  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
9945  * @cfg {string} value default value of the input
9946  * @cfg {Number} labelWidth set the width of label 
9947  * @cfg {Number} labellg set the width of label (1-12)
9948  * @cfg {Number} labelmd set the width of label (1-12)
9949  * @cfg {Number} labelsm set the width of label (1-12)
9950  * @cfg {Number} labelxs set the width of label (1-12)
9951  * @cfg {String} labelAlign (top|left)
9952  * @cfg {Boolean} readOnly Specifies that the field should be read-only
9953  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
9954  * @cfg {String} indicatorpos (left|right) default left
9955  * @cfg {String} capture (user|camera) use for file input only. (default empty)
9956  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
9957
9958  * @cfg {String} align (left|center|right) Default left
9959  * @cfg {Boolean} forceFeedback (true|false) Default false
9960  * 
9961  * @constructor
9962  * Create a new Input
9963  * @param {Object} config The config object
9964  */
9965
9966 Roo.bootstrap.Input = function(config){
9967     
9968     Roo.bootstrap.Input.superclass.constructor.call(this, config);
9969     
9970     this.addEvents({
9971         /**
9972          * @event focus
9973          * Fires when this field receives input focus.
9974          * @param {Roo.form.Field} this
9975          */
9976         focus : true,
9977         /**
9978          * @event blur
9979          * Fires when this field loses input focus.
9980          * @param {Roo.form.Field} this
9981          */
9982         blur : true,
9983         /**
9984          * @event specialkey
9985          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
9986          * {@link Roo.EventObject#getKey} to determine which key was pressed.
9987          * @param {Roo.form.Field} this
9988          * @param {Roo.EventObject} e The event object
9989          */
9990         specialkey : true,
9991         /**
9992          * @event change
9993          * Fires just before the field blurs if the field value has changed.
9994          * @param {Roo.form.Field} this
9995          * @param {Mixed} newValue The new value
9996          * @param {Mixed} oldValue The original value
9997          */
9998         change : true,
9999         /**
10000          * @event invalid
10001          * Fires after the field has been marked as invalid.
10002          * @param {Roo.form.Field} this
10003          * @param {String} msg The validation message
10004          */
10005         invalid : true,
10006         /**
10007          * @event valid
10008          * Fires after the field has been validated with no errors.
10009          * @param {Roo.form.Field} this
10010          */
10011         valid : true,
10012          /**
10013          * @event keyup
10014          * Fires after the key up
10015          * @param {Roo.form.Field} this
10016          * @param {Roo.EventObject}  e The event Object
10017          */
10018         keyup : true
10019     });
10020 };
10021
10022 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10023      /**
10024      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10025       automatic validation (defaults to "keyup").
10026      */
10027     validationEvent : "keyup",
10028      /**
10029      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10030      */
10031     validateOnBlur : true,
10032     /**
10033      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10034      */
10035     validationDelay : 250,
10036      /**
10037      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10038      */
10039     focusClass : "x-form-focus",  // not needed???
10040     
10041        
10042     /**
10043      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10044      */
10045     invalidClass : "has-warning",
10046     
10047     /**
10048      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10049      */
10050     validClass : "has-success",
10051     
10052     /**
10053      * @cfg {Boolean} hasFeedback (true|false) default true
10054      */
10055     hasFeedback : true,
10056     
10057     /**
10058      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10059      */
10060     invalidFeedbackClass : "glyphicon-warning-sign",
10061     
10062     /**
10063      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10064      */
10065     validFeedbackClass : "glyphicon-ok",
10066     
10067     /**
10068      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10069      */
10070     selectOnFocus : false,
10071     
10072      /**
10073      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10074      */
10075     maskRe : null,
10076        /**
10077      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10078      */
10079     vtype : null,
10080     
10081       /**
10082      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10083      */
10084     disableKeyFilter : false,
10085     
10086        /**
10087      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10088      */
10089     disabled : false,
10090      /**
10091      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10092      */
10093     allowBlank : true,
10094     /**
10095      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10096      */
10097     blankText : "Please complete this mandatory field",
10098     
10099      /**
10100      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10101      */
10102     minLength : 0,
10103     /**
10104      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10105      */
10106     maxLength : Number.MAX_VALUE,
10107     /**
10108      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10109      */
10110     minLengthText : "The minimum length for this field is {0}",
10111     /**
10112      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10113      */
10114     maxLengthText : "The maximum length for this field is {0}",
10115   
10116     
10117     /**
10118      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10119      * If available, this function will be called only after the basic validators all return true, and will be passed the
10120      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10121      */
10122     validator : null,
10123     /**
10124      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10125      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10126      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10127      */
10128     regex : null,
10129     /**
10130      * @cfg {String} regexText -- Depricated - use Invalid Text
10131      */
10132     regexText : "",
10133     
10134     /**
10135      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10136      */
10137     invalidText : "",
10138     
10139     
10140     
10141     autocomplete: false,
10142     
10143     
10144     fieldLabel : '',
10145     inputType : 'text',
10146     
10147     name : false,
10148     placeholder: false,
10149     before : false,
10150     after : false,
10151     size : false,
10152     hasFocus : false,
10153     preventMark: false,
10154     isFormField : true,
10155     value : '',
10156     labelWidth : 2,
10157     labelAlign : false,
10158     readOnly : false,
10159     align : false,
10160     formatedValue : false,
10161     forceFeedback : false,
10162     
10163     indicatorpos : 'left',
10164     
10165     labellg : 0,
10166     labelmd : 0,
10167     labelsm : 0,
10168     labelxs : 0,
10169     
10170     capture : '',
10171     accept : '',
10172     
10173     parentLabelAlign : function()
10174     {
10175         var parent = this;
10176         while (parent.parent()) {
10177             parent = parent.parent();
10178             if (typeof(parent.labelAlign) !='undefined') {
10179                 return parent.labelAlign;
10180             }
10181         }
10182         return 'left';
10183         
10184     },
10185     
10186     getAutoCreate : function()
10187     {
10188         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10189         
10190         var id = Roo.id();
10191         
10192         var cfg = {};
10193         
10194         if(this.inputType != 'hidden'){
10195             cfg.cls = 'form-group' //input-group
10196         }
10197         
10198         var input =  {
10199             tag: 'input',
10200             id : id,
10201             type : this.inputType,
10202             value : this.value,
10203             cls : 'form-control',
10204             placeholder : this.placeholder || '',
10205             autocomplete : this.autocomplete || 'new-password'
10206         };
10207         
10208         if(this.capture.length){
10209             input.capture = this.capture;
10210         }
10211         
10212         if(this.accept.length){
10213             input.accept = this.accept + "/*";
10214         }
10215         
10216         if(this.align){
10217             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10218         }
10219         
10220         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10221             input.maxLength = this.maxLength;
10222         }
10223         
10224         if (this.disabled) {
10225             input.disabled=true;
10226         }
10227         
10228         if (this.readOnly) {
10229             input.readonly=true;
10230         }
10231         
10232         if (this.name) {
10233             input.name = this.name;
10234         }
10235         
10236         if (this.size) {
10237             input.cls += ' input-' + this.size;
10238         }
10239         
10240         var settings=this;
10241         ['xs','sm','md','lg'].map(function(size){
10242             if (settings[size]) {
10243                 cfg.cls += ' col-' + size + '-' + settings[size];
10244             }
10245         });
10246         
10247         var inputblock = input;
10248         
10249         var feedback = {
10250             tag: 'span',
10251             cls: 'glyphicon form-control-feedback'
10252         };
10253             
10254         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10255             
10256             inputblock = {
10257                 cls : 'has-feedback',
10258                 cn :  [
10259                     input,
10260                     feedback
10261                 ] 
10262             };  
10263         }
10264         
10265         if (this.before || this.after) {
10266             
10267             inputblock = {
10268                 cls : 'input-group',
10269                 cn :  [] 
10270             };
10271             
10272             if (this.before && typeof(this.before) == 'string') {
10273                 
10274                 inputblock.cn.push({
10275                     tag :'span',
10276                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10277                     html : this.before
10278                 });
10279             }
10280             if (this.before && typeof(this.before) == 'object') {
10281                 this.before = Roo.factory(this.before);
10282                 
10283                 inputblock.cn.push({
10284                     tag :'span',
10285                     cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
10286                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10287                 });
10288             }
10289             
10290             inputblock.cn.push(input);
10291             
10292             if (this.after && typeof(this.after) == 'string') {
10293                 inputblock.cn.push({
10294                     tag :'span',
10295                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10296                     html : this.after
10297                 });
10298             }
10299             if (this.after && typeof(this.after) == 'object') {
10300                 this.after = Roo.factory(this.after);
10301                 
10302                 inputblock.cn.push({
10303                     tag :'span',
10304                     cls : 'roo-input-after input-group-append input-group-text input-group-' +
10305                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10306                 });
10307             }
10308             
10309             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10310                 inputblock.cls += ' has-feedback';
10311                 inputblock.cn.push(feedback);
10312             }
10313         };
10314         var indicator = {
10315             tag : 'i',
10316             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10317             tooltip : 'This field is required'
10318         };
10319         if (Roo.bootstrap.version == 4) {
10320             indicator = {
10321                 tag : 'i',
10322                 style : 'display-none'
10323             };
10324         }
10325         if (align ==='left' && this.fieldLabel.length) {
10326             
10327             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10328             
10329             cfg.cn = [
10330                 indicator,
10331                 {
10332                     tag: 'label',
10333                     'for' :  id,
10334                     cls : 'control-label col-form-label',
10335                     html : this.fieldLabel
10336
10337                 },
10338                 {
10339                     cls : "", 
10340                     cn: [
10341                         inputblock
10342                     ]
10343                 }
10344             ];
10345             
10346             var labelCfg = cfg.cn[1];
10347             var contentCfg = cfg.cn[2];
10348             
10349             if(this.indicatorpos == 'right'){
10350                 cfg.cn = [
10351                     {
10352                         tag: 'label',
10353                         'for' :  id,
10354                         cls : 'control-label col-form-label',
10355                         cn : [
10356                             {
10357                                 tag : 'span',
10358                                 html : this.fieldLabel
10359                             },
10360                             indicator
10361                         ]
10362                     },
10363                     {
10364                         cls : "",
10365                         cn: [
10366                             inputblock
10367                         ]
10368                     }
10369
10370                 ];
10371                 
10372                 labelCfg = cfg.cn[0];
10373                 contentCfg = cfg.cn[1];
10374             
10375             }
10376             
10377             if(this.labelWidth > 12){
10378                 labelCfg.style = "width: " + this.labelWidth + 'px';
10379             }
10380             
10381             if(this.labelWidth < 13 && this.labelmd == 0){
10382                 this.labelmd = this.labelWidth;
10383             }
10384             
10385             if(this.labellg > 0){
10386                 labelCfg.cls += ' col-lg-' + this.labellg;
10387                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10388             }
10389             
10390             if(this.labelmd > 0){
10391                 labelCfg.cls += ' col-md-' + this.labelmd;
10392                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10393             }
10394             
10395             if(this.labelsm > 0){
10396                 labelCfg.cls += ' col-sm-' + this.labelsm;
10397                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10398             }
10399             
10400             if(this.labelxs > 0){
10401                 labelCfg.cls += ' col-xs-' + this.labelxs;
10402                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10403             }
10404             
10405             
10406         } else if ( this.fieldLabel.length) {
10407                 
10408             cfg.cn = [
10409                 {
10410                     tag : 'i',
10411                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10412                     tooltip : 'This field is required'
10413                 },
10414                 {
10415                     tag: 'label',
10416                    //cls : 'input-group-addon',
10417                     html : this.fieldLabel
10418
10419                 },
10420
10421                inputblock
10422
10423            ];
10424            
10425            if(this.indicatorpos == 'right'){
10426                 
10427                 cfg.cn = [
10428                     {
10429                         tag: 'label',
10430                        //cls : 'input-group-addon',
10431                         html : this.fieldLabel
10432
10433                     },
10434                     {
10435                         tag : 'i',
10436                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10437                         tooltip : 'This field is required'
10438                     },
10439
10440                    inputblock
10441
10442                ];
10443
10444             }
10445
10446         } else {
10447             
10448             cfg.cn = [
10449
10450                     inputblock
10451
10452             ];
10453                 
10454                 
10455         };
10456         
10457         if (this.parentType === 'Navbar' &&  this.parent().bar) {
10458            cfg.cls += ' navbar-form';
10459         }
10460         
10461         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10462             // on BS4 we do this only if not form 
10463             cfg.cls += ' navbar-form';
10464             cfg.tag = 'li';
10465         }
10466         
10467         return cfg;
10468         
10469     },
10470     /**
10471      * return the real input element.
10472      */
10473     inputEl: function ()
10474     {
10475         return this.el.select('input.form-control',true).first();
10476     },
10477     
10478     tooltipEl : function()
10479     {
10480         return this.inputEl();
10481     },
10482     
10483     indicatorEl : function()
10484     {
10485         if (Roo.bootstrap.version == 4) {
10486             return false; // not enabled in v4 yet.
10487         }
10488         
10489         var indicator = this.el.select('i.roo-required-indicator',true).first();
10490         
10491         if(!indicator){
10492             return false;
10493         }
10494         
10495         return indicator;
10496         
10497     },
10498     
10499     setDisabled : function(v)
10500     {
10501         var i  = this.inputEl().dom;
10502         if (!v) {
10503             i.removeAttribute('disabled');
10504             return;
10505             
10506         }
10507         i.setAttribute('disabled','true');
10508     },
10509     initEvents : function()
10510     {
10511           
10512         this.inputEl().on("keydown" , this.fireKey,  this);
10513         this.inputEl().on("focus", this.onFocus,  this);
10514         this.inputEl().on("blur", this.onBlur,  this);
10515         
10516         this.inputEl().relayEvent('keyup', this);
10517         
10518         this.indicator = this.indicatorEl();
10519         
10520         if(this.indicator){
10521             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
10522         }
10523  
10524         // reference to original value for reset
10525         this.originalValue = this.getValue();
10526         //Roo.form.TextField.superclass.initEvents.call(this);
10527         if(this.validationEvent == 'keyup'){
10528             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
10529             this.inputEl().on('keyup', this.filterValidation, this);
10530         }
10531         else if(this.validationEvent !== false){
10532             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
10533         }
10534         
10535         if(this.selectOnFocus){
10536             this.on("focus", this.preFocus, this);
10537             
10538         }
10539         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
10540             this.inputEl().on("keypress", this.filterKeys, this);
10541         } else {
10542             this.inputEl().relayEvent('keypress', this);
10543         }
10544        /* if(this.grow){
10545             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
10546             this.el.on("click", this.autoSize,  this);
10547         }
10548         */
10549         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
10550             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
10551         }
10552         
10553         if (typeof(this.before) == 'object') {
10554             this.before.render(this.el.select('.roo-input-before',true).first());
10555         }
10556         if (typeof(this.after) == 'object') {
10557             this.after.render(this.el.select('.roo-input-after',true).first());
10558         }
10559         
10560         this.inputEl().on('change', this.onChange, this);
10561         
10562     },
10563     filterValidation : function(e){
10564         if(!e.isNavKeyPress()){
10565             this.validationTask.delay(this.validationDelay);
10566         }
10567     },
10568      /**
10569      * Validates the field value
10570      * @return {Boolean} True if the value is valid, else false
10571      */
10572     validate : function(){
10573         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
10574         if(this.disabled || this.validateValue(this.getRawValue())){
10575             this.markValid();
10576             return true;
10577         }
10578         
10579         this.markInvalid();
10580         return false;
10581     },
10582     
10583     
10584     /**
10585      * Validates a value according to the field's validation rules and marks the field as invalid
10586      * if the validation fails
10587      * @param {Mixed} value The value to validate
10588      * @return {Boolean} True if the value is valid, else false
10589      */
10590     validateValue : function(value)
10591     {
10592         if(this.getVisibilityEl().hasClass('hidden')){
10593             return true;
10594         }
10595         
10596         if(value.length < 1)  { // if it's blank
10597             if(this.allowBlank){
10598                 return true;
10599             }
10600             return false;
10601         }
10602         
10603         if(value.length < this.minLength){
10604             return false;
10605         }
10606         if(value.length > this.maxLength){
10607             return false;
10608         }
10609         if(this.vtype){
10610             var vt = Roo.form.VTypes;
10611             if(!vt[this.vtype](value, this)){
10612                 return false;
10613             }
10614         }
10615         if(typeof this.validator == "function"){
10616             var msg = this.validator(value);
10617             if(msg !== true){
10618                 return false;
10619             }
10620             if (typeof(msg) == 'string') {
10621                 this.invalidText = msg;
10622             }
10623         }
10624         
10625         if(this.regex && !this.regex.test(value)){
10626             return false;
10627         }
10628         
10629         return true;
10630     },
10631     
10632      // private
10633     fireKey : function(e){
10634         //Roo.log('field ' + e.getKey());
10635         if(e.isNavKeyPress()){
10636             this.fireEvent("specialkey", this, e);
10637         }
10638     },
10639     focus : function (selectText){
10640         if(this.rendered){
10641             this.inputEl().focus();
10642             if(selectText === true){
10643                 this.inputEl().dom.select();
10644             }
10645         }
10646         return this;
10647     } ,
10648     
10649     onFocus : function(){
10650         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10651            // this.el.addClass(this.focusClass);
10652         }
10653         if(!this.hasFocus){
10654             this.hasFocus = true;
10655             this.startValue = this.getValue();
10656             this.fireEvent("focus", this);
10657         }
10658     },
10659     
10660     beforeBlur : Roo.emptyFn,
10661
10662     
10663     // private
10664     onBlur : function(){
10665         this.beforeBlur();
10666         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10667             //this.el.removeClass(this.focusClass);
10668         }
10669         this.hasFocus = false;
10670         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
10671             this.validate();
10672         }
10673         var v = this.getValue();
10674         if(String(v) !== String(this.startValue)){
10675             this.fireEvent('change', this, v, this.startValue);
10676         }
10677         this.fireEvent("blur", this);
10678     },
10679     
10680     onChange : function(e)
10681     {
10682         var v = this.getValue();
10683         if(String(v) !== String(this.startValue)){
10684             this.fireEvent('change', this, v, this.startValue);
10685         }
10686         
10687     },
10688     
10689     /**
10690      * Resets the current field value to the originally loaded value and clears any validation messages
10691      */
10692     reset : function(){
10693         this.setValue(this.originalValue);
10694         this.validate();
10695     },
10696      /**
10697      * Returns the name of the field
10698      * @return {Mixed} name The name field
10699      */
10700     getName: function(){
10701         return this.name;
10702     },
10703      /**
10704      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
10705      * @return {Mixed} value The field value
10706      */
10707     getValue : function(){
10708         
10709         var v = this.inputEl().getValue();
10710         
10711         return v;
10712     },
10713     /**
10714      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
10715      * @return {Mixed} value The field value
10716      */
10717     getRawValue : function(){
10718         var v = this.inputEl().getValue();
10719         
10720         return v;
10721     },
10722     
10723     /**
10724      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
10725      * @param {Mixed} value The value to set
10726      */
10727     setRawValue : function(v){
10728         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
10729     },
10730     
10731     selectText : function(start, end){
10732         var v = this.getRawValue();
10733         if(v.length > 0){
10734             start = start === undefined ? 0 : start;
10735             end = end === undefined ? v.length : end;
10736             var d = this.inputEl().dom;
10737             if(d.setSelectionRange){
10738                 d.setSelectionRange(start, end);
10739             }else if(d.createTextRange){
10740                 var range = d.createTextRange();
10741                 range.moveStart("character", start);
10742                 range.moveEnd("character", v.length-end);
10743                 range.select();
10744             }
10745         }
10746     },
10747     
10748     /**
10749      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
10750      * @param {Mixed} value The value to set
10751      */
10752     setValue : function(v){
10753         this.value = v;
10754         if(this.rendered){
10755             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
10756             this.validate();
10757         }
10758     },
10759     
10760     /*
10761     processValue : function(value){
10762         if(this.stripCharsRe){
10763             var newValue = value.replace(this.stripCharsRe, '');
10764             if(newValue !== value){
10765                 this.setRawValue(newValue);
10766                 return newValue;
10767             }
10768         }
10769         return value;
10770     },
10771   */
10772     preFocus : function(){
10773         
10774         if(this.selectOnFocus){
10775             this.inputEl().dom.select();
10776         }
10777     },
10778     filterKeys : function(e){
10779         var k = e.getKey();
10780         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
10781             return;
10782         }
10783         var c = e.getCharCode(), cc = String.fromCharCode(c);
10784         if(Roo.isIE && (e.isSpecialKey() || !cc)){
10785             return;
10786         }
10787         if(!this.maskRe.test(cc)){
10788             e.stopEvent();
10789         }
10790     },
10791      /**
10792      * Clear any invalid styles/messages for this field
10793      */
10794     clearInvalid : function(){
10795         
10796         if(!this.el || this.preventMark){ // not rendered
10797             return;
10798         }
10799         
10800         
10801         this.el.removeClass([this.invalidClass, 'is-invalid']);
10802         
10803         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10804             
10805             var feedback = this.el.select('.form-control-feedback', true).first();
10806             
10807             if(feedback){
10808                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10809             }
10810             
10811         }
10812         
10813         if(this.indicator){
10814             this.indicator.removeClass('visible');
10815             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
10816         }
10817         
10818         this.fireEvent('valid', this);
10819     },
10820     
10821      /**
10822      * Mark this field as valid
10823      */
10824     markValid : function()
10825     {
10826         if(!this.el  || this.preventMark){ // not rendered...
10827             return;
10828         }
10829         
10830         this.el.removeClass([this.invalidClass, this.validClass]);
10831         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10832
10833         var feedback = this.el.select('.form-control-feedback', true).first();
10834             
10835         if(feedback){
10836             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10837         }
10838         
10839         if(this.indicator){
10840             this.indicator.removeClass('visible');
10841             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
10842         }
10843         
10844         if(this.disabled){
10845             return;
10846         }
10847         
10848         if(this.allowBlank && !this.getRawValue().length){
10849             return;
10850         }
10851         if (Roo.bootstrap.version == 3) {
10852             this.el.addClass(this.validClass);
10853         } else {
10854             this.inputEl().addClass('is-valid');
10855         }
10856
10857         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10858             
10859             var feedback = this.el.select('.form-control-feedback', true).first();
10860             
10861             if(feedback){
10862                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10863                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10864             }
10865             
10866         }
10867         
10868         this.fireEvent('valid', this);
10869     },
10870     
10871      /**
10872      * Mark this field as invalid
10873      * @param {String} msg The validation message
10874      */
10875     markInvalid : function(msg)
10876     {
10877         if(!this.el  || this.preventMark){ // not rendered
10878             return;
10879         }
10880         
10881         this.el.removeClass([this.invalidClass, this.validClass]);
10882         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10883         
10884         var feedback = this.el.select('.form-control-feedback', true).first();
10885             
10886         if(feedback){
10887             this.el.select('.form-control-feedback', true).first().removeClass(
10888                     [this.invalidFeedbackClass, this.validFeedbackClass]);
10889         }
10890
10891         if(this.disabled){
10892             return;
10893         }
10894         
10895         if(this.allowBlank && !this.getRawValue().length){
10896             return;
10897         }
10898         
10899         if(this.indicator){
10900             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
10901             this.indicator.addClass('visible');
10902         }
10903         if (Roo.bootstrap.version == 3) {
10904             this.el.addClass(this.invalidClass);
10905         } else {
10906             this.inputEl().addClass('is-invalid');
10907         }
10908         
10909         
10910         
10911         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10912             
10913             var feedback = this.el.select('.form-control-feedback', true).first();
10914             
10915             if(feedback){
10916                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10917                 
10918                 if(this.getValue().length || this.forceFeedback){
10919                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10920                 }
10921                 
10922             }
10923             
10924         }
10925         
10926         this.fireEvent('invalid', this, msg);
10927     },
10928     // private
10929     SafariOnKeyDown : function(event)
10930     {
10931         // this is a workaround for a password hang bug on chrome/ webkit.
10932         if (this.inputEl().dom.type != 'password') {
10933             return;
10934         }
10935         
10936         var isSelectAll = false;
10937         
10938         if(this.inputEl().dom.selectionEnd > 0){
10939             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
10940         }
10941         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
10942             event.preventDefault();
10943             this.setValue('');
10944             return;
10945         }
10946         
10947         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
10948             
10949             event.preventDefault();
10950             // this is very hacky as keydown always get's upper case.
10951             //
10952             var cc = String.fromCharCode(event.getCharCode());
10953             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
10954             
10955         }
10956     },
10957     adjustWidth : function(tag, w){
10958         tag = tag.toLowerCase();
10959         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
10960             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
10961                 if(tag == 'input'){
10962                     return w + 2;
10963                 }
10964                 if(tag == 'textarea'){
10965                     return w-2;
10966                 }
10967             }else if(Roo.isOpera){
10968                 if(tag == 'input'){
10969                     return w + 2;
10970                 }
10971                 if(tag == 'textarea'){
10972                     return w-2;
10973                 }
10974             }
10975         }
10976         return w;
10977     },
10978     
10979     setFieldLabel : function(v)
10980     {
10981         if(!this.rendered){
10982             return;
10983         }
10984         
10985         if(this.indicatorEl()){
10986             var ar = this.el.select('label > span',true);
10987             
10988             if (ar.elements.length) {
10989                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10990                 this.fieldLabel = v;
10991                 return;
10992             }
10993             
10994             var br = this.el.select('label',true);
10995             
10996             if(br.elements.length) {
10997                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10998                 this.fieldLabel = v;
10999                 return;
11000             }
11001             
11002             Roo.log('Cannot Found any of label > span || label in input');
11003             return;
11004         }
11005         
11006         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11007         this.fieldLabel = v;
11008         
11009         
11010     }
11011 });
11012
11013  
11014 /*
11015  * - LGPL
11016  *
11017  * Input
11018  * 
11019  */
11020
11021 /**
11022  * @class Roo.bootstrap.TextArea
11023  * @extends Roo.bootstrap.Input
11024  * Bootstrap TextArea class
11025  * @cfg {Number} cols Specifies the visible width of a text area
11026  * @cfg {Number} rows Specifies the visible number of lines in a text area
11027  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11028  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11029  * @cfg {string} html text
11030  * 
11031  * @constructor
11032  * Create a new TextArea
11033  * @param {Object} config The config object
11034  */
11035
11036 Roo.bootstrap.TextArea = function(config){
11037     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11038    
11039 };
11040
11041 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11042      
11043     cols : false,
11044     rows : 5,
11045     readOnly : false,
11046     warp : 'soft',
11047     resize : false,
11048     value: false,
11049     html: false,
11050     
11051     getAutoCreate : function(){
11052         
11053         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11054         
11055         var id = Roo.id();
11056         
11057         var cfg = {};
11058         
11059         if(this.inputType != 'hidden'){
11060             cfg.cls = 'form-group' //input-group
11061         }
11062         
11063         var input =  {
11064             tag: 'textarea',
11065             id : id,
11066             warp : this.warp,
11067             rows : this.rows,
11068             value : this.value || '',
11069             html: this.html || '',
11070             cls : 'form-control',
11071             placeholder : this.placeholder || '' 
11072             
11073         };
11074         
11075         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11076             input.maxLength = this.maxLength;
11077         }
11078         
11079         if(this.resize){
11080             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11081         }
11082         
11083         if(this.cols){
11084             input.cols = this.cols;
11085         }
11086         
11087         if (this.readOnly) {
11088             input.readonly = true;
11089         }
11090         
11091         if (this.name) {
11092             input.name = this.name;
11093         }
11094         
11095         if (this.size) {
11096             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11097         }
11098         
11099         var settings=this;
11100         ['xs','sm','md','lg'].map(function(size){
11101             if (settings[size]) {
11102                 cfg.cls += ' col-' + size + '-' + settings[size];
11103             }
11104         });
11105         
11106         var inputblock = input;
11107         
11108         if(this.hasFeedback && !this.allowBlank){
11109             
11110             var feedback = {
11111                 tag: 'span',
11112                 cls: 'glyphicon form-control-feedback'
11113             };
11114
11115             inputblock = {
11116                 cls : 'has-feedback',
11117                 cn :  [
11118                     input,
11119                     feedback
11120                 ] 
11121             };  
11122         }
11123         
11124         
11125         if (this.before || this.after) {
11126             
11127             inputblock = {
11128                 cls : 'input-group',
11129                 cn :  [] 
11130             };
11131             if (this.before) {
11132                 inputblock.cn.push({
11133                     tag :'span',
11134                     cls : 'input-group-addon',
11135                     html : this.before
11136                 });
11137             }
11138             
11139             inputblock.cn.push(input);
11140             
11141             if(this.hasFeedback && !this.allowBlank){
11142                 inputblock.cls += ' has-feedback';
11143                 inputblock.cn.push(feedback);
11144             }
11145             
11146             if (this.after) {
11147                 inputblock.cn.push({
11148                     tag :'span',
11149                     cls : 'input-group-addon',
11150                     html : this.after
11151                 });
11152             }
11153             
11154         }
11155         
11156         if (align ==='left' && this.fieldLabel.length) {
11157             cfg.cn = [
11158                 {
11159                     tag: 'label',
11160                     'for' :  id,
11161                     cls : 'control-label',
11162                     html : this.fieldLabel
11163                 },
11164                 {
11165                     cls : "",
11166                     cn: [
11167                         inputblock
11168                     ]
11169                 }
11170
11171             ];
11172             
11173             if(this.labelWidth > 12){
11174                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11175             }
11176
11177             if(this.labelWidth < 13 && this.labelmd == 0){
11178                 this.labelmd = this.labelWidth;
11179             }
11180
11181             if(this.labellg > 0){
11182                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11183                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11184             }
11185
11186             if(this.labelmd > 0){
11187                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11188                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11189             }
11190
11191             if(this.labelsm > 0){
11192                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11193                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11194             }
11195
11196             if(this.labelxs > 0){
11197                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11198                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11199             }
11200             
11201         } else if ( this.fieldLabel.length) {
11202             cfg.cn = [
11203
11204                {
11205                    tag: 'label',
11206                    //cls : 'input-group-addon',
11207                    html : this.fieldLabel
11208
11209                },
11210
11211                inputblock
11212
11213            ];
11214
11215         } else {
11216
11217             cfg.cn = [
11218
11219                 inputblock
11220
11221             ];
11222                 
11223         }
11224         
11225         if (this.disabled) {
11226             input.disabled=true;
11227         }
11228         
11229         return cfg;
11230         
11231     },
11232     /**
11233      * return the real textarea element.
11234      */
11235     inputEl: function ()
11236     {
11237         return this.el.select('textarea.form-control',true).first();
11238     },
11239     
11240     /**
11241      * Clear any invalid styles/messages for this field
11242      */
11243     clearInvalid : function()
11244     {
11245         
11246         if(!this.el || this.preventMark){ // not rendered
11247             return;
11248         }
11249         
11250         var label = this.el.select('label', true).first();
11251         var icon = this.el.select('i.fa-star', true).first();
11252         
11253         if(label && icon){
11254             icon.remove();
11255         }
11256         this.el.removeClass( this.validClass);
11257         this.inputEl().removeClass('is-invalid');
11258          
11259         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11260             
11261             var feedback = this.el.select('.form-control-feedback', true).first();
11262             
11263             if(feedback){
11264                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11265             }
11266             
11267         }
11268         
11269         this.fireEvent('valid', this);
11270     },
11271     
11272      /**
11273      * Mark this field as valid
11274      */
11275     markValid : function()
11276     {
11277         if(!this.el  || this.preventMark){ // not rendered
11278             return;
11279         }
11280         
11281         this.el.removeClass([this.invalidClass, this.validClass]);
11282         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11283         
11284         var feedback = this.el.select('.form-control-feedback', true).first();
11285             
11286         if(feedback){
11287             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11288         }
11289
11290         if(this.disabled || this.allowBlank){
11291             return;
11292         }
11293         
11294         var label = this.el.select('label', true).first();
11295         var icon = this.el.select('i.fa-star', true).first();
11296         
11297         if(label && icon){
11298             icon.remove();
11299         }
11300         if (Roo.bootstrap.version == 3) {
11301             this.el.addClass(this.validClass);
11302         } else {
11303             this.inputEl().addClass('is-valid');
11304         }
11305         
11306         
11307         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11308             
11309             var feedback = this.el.select('.form-control-feedback', true).first();
11310             
11311             if(feedback){
11312                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11313                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11314             }
11315             
11316         }
11317         
11318         this.fireEvent('valid', this);
11319     },
11320     
11321      /**
11322      * Mark this field as invalid
11323      * @param {String} msg The validation message
11324      */
11325     markInvalid : function(msg)
11326     {
11327         if(!this.el  || this.preventMark){ // not rendered
11328             return;
11329         }
11330         
11331         this.el.removeClass([this.invalidClass, this.validClass]);
11332         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11333         
11334         var feedback = this.el.select('.form-control-feedback', true).first();
11335             
11336         if(feedback){
11337             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11338         }
11339
11340         if(this.disabled || this.allowBlank){
11341             return;
11342         }
11343         
11344         var label = this.el.select('label', true).first();
11345         var icon = this.el.select('i.fa-star', true).first();
11346         
11347         if(!this.getValue().length && label && !icon){
11348             this.el.createChild({
11349                 tag : 'i',
11350                 cls : 'text-danger fa fa-lg fa-star',
11351                 tooltip : 'This field is required',
11352                 style : 'margin-right:5px;'
11353             }, label, true);
11354         }
11355         
11356         if (Roo.bootstrap.version == 3) {
11357             this.el.addClass(this.invalidClass);
11358         } else {
11359             this.inputEl().addClass('is-invalid');
11360         }
11361         
11362         // fixme ... this may be depricated need to test..
11363         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11364             
11365             var feedback = this.el.select('.form-control-feedback', true).first();
11366             
11367             if(feedback){
11368                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11369                 
11370                 if(this.getValue().length || this.forceFeedback){
11371                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11372                 }
11373                 
11374             }
11375             
11376         }
11377         
11378         this.fireEvent('invalid', this, msg);
11379     }
11380 });
11381
11382  
11383 /*
11384  * - LGPL
11385  *
11386  * trigger field - base class for combo..
11387  * 
11388  */
11389  
11390 /**
11391  * @class Roo.bootstrap.TriggerField
11392  * @extends Roo.bootstrap.Input
11393  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11394  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11395  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11396  * for which you can provide a custom implementation.  For example:
11397  * <pre><code>
11398 var trigger = new Roo.bootstrap.TriggerField();
11399 trigger.onTriggerClick = myTriggerFn;
11400 trigger.applyTo('my-field');
11401 </code></pre>
11402  *
11403  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11404  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11405  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
11406  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11407  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11408
11409  * @constructor
11410  * Create a new TriggerField.
11411  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11412  * to the base TextField)
11413  */
11414 Roo.bootstrap.TriggerField = function(config){
11415     this.mimicing = false;
11416     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11417 };
11418
11419 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
11420     /**
11421      * @cfg {String} triggerClass A CSS class to apply to the trigger
11422      */
11423      /**
11424      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11425      */
11426     hideTrigger:false,
11427
11428     /**
11429      * @cfg {Boolean} removable (true|false) special filter default false
11430      */
11431     removable : false,
11432     
11433     /** @cfg {Boolean} grow @hide */
11434     /** @cfg {Number} growMin @hide */
11435     /** @cfg {Number} growMax @hide */
11436
11437     /**
11438      * @hide 
11439      * @method
11440      */
11441     autoSize: Roo.emptyFn,
11442     // private
11443     monitorTab : true,
11444     // private
11445     deferHeight : true,
11446
11447     
11448     actionMode : 'wrap',
11449     
11450     caret : false,
11451     
11452     
11453     getAutoCreate : function(){
11454        
11455         var align = this.labelAlign || this.parentLabelAlign();
11456         
11457         var id = Roo.id();
11458         
11459         var cfg = {
11460             cls: 'form-group' //input-group
11461         };
11462         
11463         
11464         var input =  {
11465             tag: 'input',
11466             id : id,
11467             type : this.inputType,
11468             cls : 'form-control',
11469             autocomplete: 'new-password',
11470             placeholder : this.placeholder || '' 
11471             
11472         };
11473         if (this.name) {
11474             input.name = this.name;
11475         }
11476         if (this.size) {
11477             input.cls += ' input-' + this.size;
11478         }
11479         
11480         if (this.disabled) {
11481             input.disabled=true;
11482         }
11483         
11484         var inputblock = input;
11485         
11486         if(this.hasFeedback && !this.allowBlank){
11487             
11488             var feedback = {
11489                 tag: 'span',
11490                 cls: 'glyphicon form-control-feedback'
11491             };
11492             
11493             if(this.removable && !this.editable && !this.tickable){
11494                 inputblock = {
11495                     cls : 'has-feedback',
11496                     cn :  [
11497                         inputblock,
11498                         {
11499                             tag: 'button',
11500                             html : 'x',
11501                             cls : 'roo-combo-removable-btn close'
11502                         },
11503                         feedback
11504                     ] 
11505                 };
11506             } else {
11507                 inputblock = {
11508                     cls : 'has-feedback',
11509                     cn :  [
11510                         inputblock,
11511                         feedback
11512                     ] 
11513                 };
11514             }
11515
11516         } else {
11517             if(this.removable && !this.editable && !this.tickable){
11518                 inputblock = {
11519                     cls : 'roo-removable',
11520                     cn :  [
11521                         inputblock,
11522                         {
11523                             tag: 'button',
11524                             html : 'x',
11525                             cls : 'roo-combo-removable-btn close'
11526                         }
11527                     ] 
11528                 };
11529             }
11530         }
11531         
11532         if (this.before || this.after) {
11533             
11534             inputblock = {
11535                 cls : 'input-group',
11536                 cn :  [] 
11537             };
11538             if (this.before) {
11539                 inputblock.cn.push({
11540                     tag :'span',
11541                     cls : 'input-group-addon input-group-prepend input-group-text',
11542                     html : this.before
11543                 });
11544             }
11545             
11546             inputblock.cn.push(input);
11547             
11548             if(this.hasFeedback && !this.allowBlank){
11549                 inputblock.cls += ' has-feedback';
11550                 inputblock.cn.push(feedback);
11551             }
11552             
11553             if (this.after) {
11554                 inputblock.cn.push({
11555                     tag :'span',
11556                     cls : 'input-group-addon input-group-append input-group-text',
11557                     html : this.after
11558                 });
11559             }
11560             
11561         };
11562         
11563       
11564         
11565         var ibwrap = inputblock;
11566         
11567         if(this.multiple){
11568             ibwrap = {
11569                 tag: 'ul',
11570                 cls: 'roo-select2-choices',
11571                 cn:[
11572                     {
11573                         tag: 'li',
11574                         cls: 'roo-select2-search-field',
11575                         cn: [
11576
11577                             inputblock
11578                         ]
11579                     }
11580                 ]
11581             };
11582                 
11583         }
11584         
11585         var combobox = {
11586             cls: 'roo-select2-container input-group',
11587             cn: [
11588                  {
11589                     tag: 'input',
11590                     type : 'hidden',
11591                     cls: 'form-hidden-field'
11592                 },
11593                 ibwrap
11594             ]
11595         };
11596         
11597         if(!this.multiple && this.showToggleBtn){
11598             
11599             var caret = {
11600                         tag: 'span',
11601                         cls: 'caret'
11602              };
11603             if (this.caret != false) {
11604                 caret = {
11605                      tag: 'i',
11606                      cls: 'fa fa-' + this.caret
11607                 };
11608                 
11609             }
11610             
11611             combobox.cn.push({
11612                 tag :'span',
11613                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
11614                 cn : [
11615                     Roo.bootstrap.version == 3 ? caret : '',
11616                     {
11617                         tag: 'span',
11618                         cls: 'combobox-clear',
11619                         cn  : [
11620                             {
11621                                 tag : 'i',
11622                                 cls: 'icon-remove'
11623                             }
11624                         ]
11625                     }
11626                 ]
11627
11628             })
11629         }
11630         
11631         if(this.multiple){
11632             combobox.cls += ' roo-select2-container-multi';
11633         }
11634          var indicator = {
11635             tag : 'i',
11636             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11637             tooltip : 'This field is required'
11638         };
11639         if (Roo.bootstrap.version == 4) {
11640             indicator = {
11641                 tag : 'i',
11642                 style : 'display:none'
11643             };
11644         }
11645         
11646         
11647         if (align ==='left' && this.fieldLabel.length) {
11648             
11649             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
11650
11651             cfg.cn = [
11652                 indicator,
11653                 {
11654                     tag: 'label',
11655                     'for' :  id,
11656                     cls : 'control-label',
11657                     html : this.fieldLabel
11658
11659                 },
11660                 {
11661                     cls : "", 
11662                     cn: [
11663                         combobox
11664                     ]
11665                 }
11666
11667             ];
11668             
11669             var labelCfg = cfg.cn[1];
11670             var contentCfg = cfg.cn[2];
11671             
11672             if(this.indicatorpos == 'right'){
11673                 cfg.cn = [
11674                     {
11675                         tag: 'label',
11676                         'for' :  id,
11677                         cls : 'control-label',
11678                         cn : [
11679                             {
11680                                 tag : 'span',
11681                                 html : this.fieldLabel
11682                             },
11683                             indicator
11684                         ]
11685                     },
11686                     {
11687                         cls : "", 
11688                         cn: [
11689                             combobox
11690                         ]
11691                     }
11692
11693                 ];
11694                 
11695                 labelCfg = cfg.cn[0];
11696                 contentCfg = cfg.cn[1];
11697             }
11698             
11699             if(this.labelWidth > 12){
11700                 labelCfg.style = "width: " + this.labelWidth + 'px';
11701             }
11702             
11703             if(this.labelWidth < 13 && this.labelmd == 0){
11704                 this.labelmd = this.labelWidth;
11705             }
11706             
11707             if(this.labellg > 0){
11708                 labelCfg.cls += ' col-lg-' + this.labellg;
11709                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11710             }
11711             
11712             if(this.labelmd > 0){
11713                 labelCfg.cls += ' col-md-' + this.labelmd;
11714                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11715             }
11716             
11717             if(this.labelsm > 0){
11718                 labelCfg.cls += ' col-sm-' + this.labelsm;
11719                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11720             }
11721             
11722             if(this.labelxs > 0){
11723                 labelCfg.cls += ' col-xs-' + this.labelxs;
11724                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11725             }
11726             
11727         } else if ( this.fieldLabel.length) {
11728 //                Roo.log(" label");
11729             cfg.cn = [
11730                 indicator,
11731                {
11732                    tag: 'label',
11733                    //cls : 'input-group-addon',
11734                    html : this.fieldLabel
11735
11736                },
11737
11738                combobox
11739
11740             ];
11741             
11742             if(this.indicatorpos == 'right'){
11743                 
11744                 cfg.cn = [
11745                     {
11746                        tag: 'label',
11747                        cn : [
11748                            {
11749                                tag : 'span',
11750                                html : this.fieldLabel
11751                            },
11752                            indicator
11753                        ]
11754
11755                     },
11756                     combobox
11757
11758                 ];
11759
11760             }
11761
11762         } else {
11763             
11764 //                Roo.log(" no label && no align");
11765                 cfg = combobox
11766                      
11767                 
11768         }
11769         
11770         var settings=this;
11771         ['xs','sm','md','lg'].map(function(size){
11772             if (settings[size]) {
11773                 cfg.cls += ' col-' + size + '-' + settings[size];
11774             }
11775         });
11776         
11777         return cfg;
11778         
11779     },
11780     
11781     
11782     
11783     // private
11784     onResize : function(w, h){
11785 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
11786 //        if(typeof w == 'number'){
11787 //            var x = w - this.trigger.getWidth();
11788 //            this.inputEl().setWidth(this.adjustWidth('input', x));
11789 //            this.trigger.setStyle('left', x+'px');
11790 //        }
11791     },
11792
11793     // private
11794     adjustSize : Roo.BoxComponent.prototype.adjustSize,
11795
11796     // private
11797     getResizeEl : function(){
11798         return this.inputEl();
11799     },
11800
11801     // private
11802     getPositionEl : function(){
11803         return this.inputEl();
11804     },
11805
11806     // private
11807     alignErrorIcon : function(){
11808         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
11809     },
11810
11811     // private
11812     initEvents : function(){
11813         
11814         this.createList();
11815         
11816         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
11817         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
11818         if(!this.multiple && this.showToggleBtn){
11819             this.trigger = this.el.select('span.dropdown-toggle',true).first();
11820             if(this.hideTrigger){
11821                 this.trigger.setDisplayed(false);
11822             }
11823             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
11824         }
11825         
11826         if(this.multiple){
11827             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
11828         }
11829         
11830         if(this.removable && !this.editable && !this.tickable){
11831             var close = this.closeTriggerEl();
11832             
11833             if(close){
11834                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
11835                 close.on('click', this.removeBtnClick, this, close);
11836             }
11837         }
11838         
11839         //this.trigger.addClassOnOver('x-form-trigger-over');
11840         //this.trigger.addClassOnClick('x-form-trigger-click');
11841         
11842         //if(!this.width){
11843         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
11844         //}
11845     },
11846     
11847     closeTriggerEl : function()
11848     {
11849         var close = this.el.select('.roo-combo-removable-btn', true).first();
11850         return close ? close : false;
11851     },
11852     
11853     removeBtnClick : function(e, h, el)
11854     {
11855         e.preventDefault();
11856         
11857         if(this.fireEvent("remove", this) !== false){
11858             this.reset();
11859             this.fireEvent("afterremove", this)
11860         }
11861     },
11862     
11863     createList : function()
11864     {
11865         this.list = Roo.get(document.body).createChild({
11866             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
11867             cls: 'typeahead typeahead-long dropdown-menu',
11868             style: 'display:none'
11869         });
11870         
11871         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
11872         
11873     },
11874
11875     // private
11876     initTrigger : function(){
11877        
11878     },
11879
11880     // private
11881     onDestroy : function(){
11882         if(this.trigger){
11883             this.trigger.removeAllListeners();
11884           //  this.trigger.remove();
11885         }
11886         //if(this.wrap){
11887         //    this.wrap.remove();
11888         //}
11889         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
11890     },
11891
11892     // private
11893     onFocus : function(){
11894         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
11895         /*
11896         if(!this.mimicing){
11897             this.wrap.addClass('x-trigger-wrap-focus');
11898             this.mimicing = true;
11899             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
11900             if(this.monitorTab){
11901                 this.el.on("keydown", this.checkTab, this);
11902             }
11903         }
11904         */
11905     },
11906
11907     // private
11908     checkTab : function(e){
11909         if(e.getKey() == e.TAB){
11910             this.triggerBlur();
11911         }
11912     },
11913
11914     // private
11915     onBlur : function(){
11916         // do nothing
11917     },
11918
11919     // private
11920     mimicBlur : function(e, t){
11921         /*
11922         if(!this.wrap.contains(t) && this.validateBlur()){
11923             this.triggerBlur();
11924         }
11925         */
11926     },
11927
11928     // private
11929     triggerBlur : function(){
11930         this.mimicing = false;
11931         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
11932         if(this.monitorTab){
11933             this.el.un("keydown", this.checkTab, this);
11934         }
11935         //this.wrap.removeClass('x-trigger-wrap-focus');
11936         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
11937     },
11938
11939     // private
11940     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
11941     validateBlur : function(e, t){
11942         return true;
11943     },
11944
11945     // private
11946     onDisable : function(){
11947         this.inputEl().dom.disabled = true;
11948         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
11949         //if(this.wrap){
11950         //    this.wrap.addClass('x-item-disabled');
11951         //}
11952     },
11953
11954     // private
11955     onEnable : function(){
11956         this.inputEl().dom.disabled = false;
11957         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
11958         //if(this.wrap){
11959         //    this.el.removeClass('x-item-disabled');
11960         //}
11961     },
11962
11963     // private
11964     onShow : function(){
11965         var ae = this.getActionEl();
11966         
11967         if(ae){
11968             ae.dom.style.display = '';
11969             ae.dom.style.visibility = 'visible';
11970         }
11971     },
11972
11973     // private
11974     
11975     onHide : function(){
11976         var ae = this.getActionEl();
11977         ae.dom.style.display = 'none';
11978     },
11979
11980     /**
11981      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
11982      * by an implementing function.
11983      * @method
11984      * @param {EventObject} e
11985      */
11986     onTriggerClick : Roo.emptyFn
11987 });
11988  /*
11989  * Based on:
11990  * Ext JS Library 1.1.1
11991  * Copyright(c) 2006-2007, Ext JS, LLC.
11992  *
11993  * Originally Released Under LGPL - original licence link has changed is not relivant.
11994  *
11995  * Fork - LGPL
11996  * <script type="text/javascript">
11997  */
11998
11999
12000 /**
12001  * @class Roo.data.SortTypes
12002  * @singleton
12003  * Defines the default sorting (casting?) comparison functions used when sorting data.
12004  */
12005 Roo.data.SortTypes = {
12006     /**
12007      * Default sort that does nothing
12008      * @param {Mixed} s The value being converted
12009      * @return {Mixed} The comparison value
12010      */
12011     none : function(s){
12012         return s;
12013     },
12014     
12015     /**
12016      * The regular expression used to strip tags
12017      * @type {RegExp}
12018      * @property
12019      */
12020     stripTagsRE : /<\/?[^>]+>/gi,
12021     
12022     /**
12023      * Strips all HTML tags to sort on text only
12024      * @param {Mixed} s The value being converted
12025      * @return {String} The comparison value
12026      */
12027     asText : function(s){
12028         return String(s).replace(this.stripTagsRE, "");
12029     },
12030     
12031     /**
12032      * Strips all HTML tags to sort on text only - Case insensitive
12033      * @param {Mixed} s The value being converted
12034      * @return {String} The comparison value
12035      */
12036     asUCText : function(s){
12037         return String(s).toUpperCase().replace(this.stripTagsRE, "");
12038     },
12039     
12040     /**
12041      * Case insensitive string
12042      * @param {Mixed} s The value being converted
12043      * @return {String} The comparison value
12044      */
12045     asUCString : function(s) {
12046         return String(s).toUpperCase();
12047     },
12048     
12049     /**
12050      * Date sorting
12051      * @param {Mixed} s The value being converted
12052      * @return {Number} The comparison value
12053      */
12054     asDate : function(s) {
12055         if(!s){
12056             return 0;
12057         }
12058         if(s instanceof Date){
12059             return s.getTime();
12060         }
12061         return Date.parse(String(s));
12062     },
12063     
12064     /**
12065      * Float sorting
12066      * @param {Mixed} s The value being converted
12067      * @return {Float} The comparison value
12068      */
12069     asFloat : function(s) {
12070         var val = parseFloat(String(s).replace(/,/g, ""));
12071         if(isNaN(val)) {
12072             val = 0;
12073         }
12074         return val;
12075     },
12076     
12077     /**
12078      * Integer sorting
12079      * @param {Mixed} s The value being converted
12080      * @return {Number} The comparison value
12081      */
12082     asInt : function(s) {
12083         var val = parseInt(String(s).replace(/,/g, ""));
12084         if(isNaN(val)) {
12085             val = 0;
12086         }
12087         return val;
12088     }
12089 };/*
12090  * Based on:
12091  * Ext JS Library 1.1.1
12092  * Copyright(c) 2006-2007, Ext JS, LLC.
12093  *
12094  * Originally Released Under LGPL - original licence link has changed is not relivant.
12095  *
12096  * Fork - LGPL
12097  * <script type="text/javascript">
12098  */
12099
12100 /**
12101 * @class Roo.data.Record
12102  * Instances of this class encapsulate both record <em>definition</em> information, and record
12103  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12104  * to access Records cached in an {@link Roo.data.Store} object.<br>
12105  * <p>
12106  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12107  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12108  * objects.<br>
12109  * <p>
12110  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12111  * @constructor
12112  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12113  * {@link #create}. The parameters are the same.
12114  * @param {Array} data An associative Array of data values keyed by the field name.
12115  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12116  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12117  * not specified an integer id is generated.
12118  */
12119 Roo.data.Record = function(data, id){
12120     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12121     this.data = data;
12122 };
12123
12124 /**
12125  * Generate a constructor for a specific record layout.
12126  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12127  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12128  * Each field definition object may contain the following properties: <ul>
12129  * <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,
12130  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12131  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12132  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12133  * is being used, then this is a string containing the javascript expression to reference the data relative to 
12134  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12135  * to the data item relative to the record element. If the mapping expression is the same as the field name,
12136  * this may be omitted.</p></li>
12137  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12138  * <ul><li>auto (Default, implies no conversion)</li>
12139  * <li>string</li>
12140  * <li>int</li>
12141  * <li>float</li>
12142  * <li>boolean</li>
12143  * <li>date</li></ul></p></li>
12144  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12145  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12146  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12147  * by the Reader into an object that will be stored in the Record. It is passed the
12148  * following parameters:<ul>
12149  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12150  * </ul></p></li>
12151  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12152  * </ul>
12153  * <br>usage:<br><pre><code>
12154 var TopicRecord = Roo.data.Record.create(
12155     {name: 'title', mapping: 'topic_title'},
12156     {name: 'author', mapping: 'username'},
12157     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12158     {name: 'lastPost', mapping: 'post_time', type: 'date'},
12159     {name: 'lastPoster', mapping: 'user2'},
12160     {name: 'excerpt', mapping: 'post_text'}
12161 );
12162
12163 var myNewRecord = new TopicRecord({
12164     title: 'Do my job please',
12165     author: 'noobie',
12166     totalPosts: 1,
12167     lastPost: new Date(),
12168     lastPoster: 'Animal',
12169     excerpt: 'No way dude!'
12170 });
12171 myStore.add(myNewRecord);
12172 </code></pre>
12173  * @method create
12174  * @static
12175  */
12176 Roo.data.Record.create = function(o){
12177     var f = function(){
12178         f.superclass.constructor.apply(this, arguments);
12179     };
12180     Roo.extend(f, Roo.data.Record);
12181     var p = f.prototype;
12182     p.fields = new Roo.util.MixedCollection(false, function(field){
12183         return field.name;
12184     });
12185     for(var i = 0, len = o.length; i < len; i++){
12186         p.fields.add(new Roo.data.Field(o[i]));
12187     }
12188     f.getField = function(name){
12189         return p.fields.get(name);  
12190     };
12191     return f;
12192 };
12193
12194 Roo.data.Record.AUTO_ID = 1000;
12195 Roo.data.Record.EDIT = 'edit';
12196 Roo.data.Record.REJECT = 'reject';
12197 Roo.data.Record.COMMIT = 'commit';
12198
12199 Roo.data.Record.prototype = {
12200     /**
12201      * Readonly flag - true if this record has been modified.
12202      * @type Boolean
12203      */
12204     dirty : false,
12205     editing : false,
12206     error: null,
12207     modified: null,
12208
12209     // private
12210     join : function(store){
12211         this.store = store;
12212     },
12213
12214     /**
12215      * Set the named field to the specified value.
12216      * @param {String} name The name of the field to set.
12217      * @param {Object} value The value to set the field to.
12218      */
12219     set : function(name, value){
12220         if(this.data[name] == value){
12221             return;
12222         }
12223         this.dirty = true;
12224         if(!this.modified){
12225             this.modified = {};
12226         }
12227         if(typeof this.modified[name] == 'undefined'){
12228             this.modified[name] = this.data[name];
12229         }
12230         this.data[name] = value;
12231         if(!this.editing && this.store){
12232             this.store.afterEdit(this);
12233         }       
12234     },
12235
12236     /**
12237      * Get the value of the named field.
12238      * @param {String} name The name of the field to get the value of.
12239      * @return {Object} The value of the field.
12240      */
12241     get : function(name){
12242         return this.data[name]; 
12243     },
12244
12245     // private
12246     beginEdit : function(){
12247         this.editing = true;
12248         this.modified = {}; 
12249     },
12250
12251     // private
12252     cancelEdit : function(){
12253         this.editing = false;
12254         delete this.modified;
12255     },
12256
12257     // private
12258     endEdit : function(){
12259         this.editing = false;
12260         if(this.dirty && this.store){
12261             this.store.afterEdit(this);
12262         }
12263     },
12264
12265     /**
12266      * Usually called by the {@link Roo.data.Store} which owns the Record.
12267      * Rejects all changes made to the Record since either creation, or the last commit operation.
12268      * Modified fields are reverted to their original values.
12269      * <p>
12270      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
12271      * of reject operations.
12272      */
12273     reject : function(){
12274         var m = this.modified;
12275         for(var n in m){
12276             if(typeof m[n] != "function"){
12277                 this.data[n] = m[n];
12278             }
12279         }
12280         this.dirty = false;
12281         delete this.modified;
12282         this.editing = false;
12283         if(this.store){
12284             this.store.afterReject(this);
12285         }
12286     },
12287
12288     /**
12289      * Usually called by the {@link Roo.data.Store} which owns the Record.
12290      * Commits all changes made to the Record since either creation, or the last commit operation.
12291      * <p>
12292      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
12293      * of commit operations.
12294      */
12295     commit : function(){
12296         this.dirty = false;
12297         delete this.modified;
12298         this.editing = false;
12299         if(this.store){
12300             this.store.afterCommit(this);
12301         }
12302     },
12303
12304     // private
12305     hasError : function(){
12306         return this.error != null;
12307     },
12308
12309     // private
12310     clearError : function(){
12311         this.error = null;
12312     },
12313
12314     /**
12315      * Creates a copy of this record.
12316      * @param {String} id (optional) A new record id if you don't want to use this record's id
12317      * @return {Record}
12318      */
12319     copy : function(newId) {
12320         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
12321     }
12322 };/*
12323  * Based on:
12324  * Ext JS Library 1.1.1
12325  * Copyright(c) 2006-2007, Ext JS, LLC.
12326  *
12327  * Originally Released Under LGPL - original licence link has changed is not relivant.
12328  *
12329  * Fork - LGPL
12330  * <script type="text/javascript">
12331  */
12332
12333
12334
12335 /**
12336  * @class Roo.data.Store
12337  * @extends Roo.util.Observable
12338  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
12339  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
12340  * <p>
12341  * 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
12342  * has no knowledge of the format of the data returned by the Proxy.<br>
12343  * <p>
12344  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
12345  * instances from the data object. These records are cached and made available through accessor functions.
12346  * @constructor
12347  * Creates a new Store.
12348  * @param {Object} config A config object containing the objects needed for the Store to access data,
12349  * and read the data into Records.
12350  */
12351 Roo.data.Store = function(config){
12352     this.data = new Roo.util.MixedCollection(false);
12353     this.data.getKey = function(o){
12354         return o.id;
12355     };
12356     this.baseParams = {};
12357     // private
12358     this.paramNames = {
12359         "start" : "start",
12360         "limit" : "limit",
12361         "sort" : "sort",
12362         "dir" : "dir",
12363         "multisort" : "_multisort"
12364     };
12365
12366     if(config && config.data){
12367         this.inlineData = config.data;
12368         delete config.data;
12369     }
12370
12371     Roo.apply(this, config);
12372     
12373     if(this.reader){ // reader passed
12374         this.reader = Roo.factory(this.reader, Roo.data);
12375         this.reader.xmodule = this.xmodule || false;
12376         if(!this.recordType){
12377             this.recordType = this.reader.recordType;
12378         }
12379         if(this.reader.onMetaChange){
12380             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
12381         }
12382     }
12383
12384     if(this.recordType){
12385         this.fields = this.recordType.prototype.fields;
12386     }
12387     this.modified = [];
12388
12389     this.addEvents({
12390         /**
12391          * @event datachanged
12392          * Fires when the data cache has changed, and a widget which is using this Store
12393          * as a Record cache should refresh its view.
12394          * @param {Store} this
12395          */
12396         datachanged : true,
12397         /**
12398          * @event metachange
12399          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
12400          * @param {Store} this
12401          * @param {Object} meta The JSON metadata
12402          */
12403         metachange : true,
12404         /**
12405          * @event add
12406          * Fires when Records have been added to the Store
12407          * @param {Store} this
12408          * @param {Roo.data.Record[]} records The array of Records added
12409          * @param {Number} index The index at which the record(s) were added
12410          */
12411         add : true,
12412         /**
12413          * @event remove
12414          * Fires when a Record has been removed from the Store
12415          * @param {Store} this
12416          * @param {Roo.data.Record} record The Record that was removed
12417          * @param {Number} index The index at which the record was removed
12418          */
12419         remove : true,
12420         /**
12421          * @event update
12422          * Fires when a Record has been updated
12423          * @param {Store} this
12424          * @param {Roo.data.Record} record The Record that was updated
12425          * @param {String} operation The update operation being performed.  Value may be one of:
12426          * <pre><code>
12427  Roo.data.Record.EDIT
12428  Roo.data.Record.REJECT
12429  Roo.data.Record.COMMIT
12430          * </code></pre>
12431          */
12432         update : true,
12433         /**
12434          * @event clear
12435          * Fires when the data cache has been cleared.
12436          * @param {Store} this
12437          */
12438         clear : true,
12439         /**
12440          * @event beforeload
12441          * Fires before a request is made for a new data object.  If the beforeload handler returns false
12442          * the load action will be canceled.
12443          * @param {Store} this
12444          * @param {Object} options The loading options that were specified (see {@link #load} for details)
12445          */
12446         beforeload : true,
12447         /**
12448          * @event beforeloadadd
12449          * Fires after a new set of Records has been loaded.
12450          * @param {Store} this
12451          * @param {Roo.data.Record[]} records The Records that were loaded
12452          * @param {Object} options The loading options that were specified (see {@link #load} for details)
12453          */
12454         beforeloadadd : true,
12455         /**
12456          * @event load
12457          * Fires after a new set of Records has been loaded, before they are added to the store.
12458          * @param {Store} this
12459          * @param {Roo.data.Record[]} records The Records that were loaded
12460          * @param {Object} options The loading options that were specified (see {@link #load} for details)
12461          * @params {Object} return from reader
12462          */
12463         load : true,
12464         /**
12465          * @event loadexception
12466          * Fires if an exception occurs in the Proxy during loading.
12467          * Called with the signature of the Proxy's "loadexception" event.
12468          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
12469          * 
12470          * @param {Proxy} 
12471          * @param {Object} return from JsonData.reader() - success, totalRecords, records
12472          * @param {Object} load options 
12473          * @param {Object} jsonData from your request (normally this contains the Exception)
12474          */
12475         loadexception : true
12476     });
12477     
12478     if(this.proxy){
12479         this.proxy = Roo.factory(this.proxy, Roo.data);
12480         this.proxy.xmodule = this.xmodule || false;
12481         this.relayEvents(this.proxy,  ["loadexception"]);
12482     }
12483     this.sortToggle = {};
12484     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
12485
12486     Roo.data.Store.superclass.constructor.call(this);
12487
12488     if(this.inlineData){
12489         this.loadData(this.inlineData);
12490         delete this.inlineData;
12491     }
12492 };
12493
12494 Roo.extend(Roo.data.Store, Roo.util.Observable, {
12495      /**
12496     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
12497     * without a remote query - used by combo/forms at present.
12498     */
12499     
12500     /**
12501     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
12502     */
12503     /**
12504     * @cfg {Array} data Inline data to be loaded when the store is initialized.
12505     */
12506     /**
12507     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
12508     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
12509     */
12510     /**
12511     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
12512     * on any HTTP request
12513     */
12514     /**
12515     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
12516     */
12517     /**
12518     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
12519     */
12520     multiSort: false,
12521     /**
12522     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
12523     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
12524     */
12525     remoteSort : false,
12526
12527     /**
12528     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
12529      * loaded or when a record is removed. (defaults to false).
12530     */
12531     pruneModifiedRecords : false,
12532
12533     // private
12534     lastOptions : null,
12535
12536     /**
12537      * Add Records to the Store and fires the add event.
12538      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
12539      */
12540     add : function(records){
12541         records = [].concat(records);
12542         for(var i = 0, len = records.length; i < len; i++){
12543             records[i].join(this);
12544         }
12545         var index = this.data.length;
12546         this.data.addAll(records);
12547         this.fireEvent("add", this, records, index);
12548     },
12549
12550     /**
12551      * Remove a Record from the Store and fires the remove event.
12552      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
12553      */
12554     remove : function(record){
12555         var index = this.data.indexOf(record);
12556         this.data.removeAt(index);
12557  
12558         if(this.pruneModifiedRecords){
12559             this.modified.remove(record);
12560         }
12561         this.fireEvent("remove", this, record, index);
12562     },
12563
12564     /**
12565      * Remove all Records from the Store and fires the clear event.
12566      */
12567     removeAll : function(){
12568         this.data.clear();
12569         if(this.pruneModifiedRecords){
12570             this.modified = [];
12571         }
12572         this.fireEvent("clear", this);
12573     },
12574
12575     /**
12576      * Inserts Records to the Store at the given index and fires the add event.
12577      * @param {Number} index The start index at which to insert the passed Records.
12578      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
12579      */
12580     insert : function(index, records){
12581         records = [].concat(records);
12582         for(var i = 0, len = records.length; i < len; i++){
12583             this.data.insert(index, records[i]);
12584             records[i].join(this);
12585         }
12586         this.fireEvent("add", this, records, index);
12587     },
12588
12589     /**
12590      * Get the index within the cache of the passed Record.
12591      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
12592      * @return {Number} The index of the passed Record. Returns -1 if not found.
12593      */
12594     indexOf : function(record){
12595         return this.data.indexOf(record);
12596     },
12597
12598     /**
12599      * Get the index within the cache of the Record with the passed id.
12600      * @param {String} id The id of the Record to find.
12601      * @return {Number} The index of the Record. Returns -1 if not found.
12602      */
12603     indexOfId : function(id){
12604         return this.data.indexOfKey(id);
12605     },
12606
12607     /**
12608      * Get the Record with the specified id.
12609      * @param {String} id The id of the Record to find.
12610      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
12611      */
12612     getById : function(id){
12613         return this.data.key(id);
12614     },
12615
12616     /**
12617      * Get the Record at the specified index.
12618      * @param {Number} index The index of the Record to find.
12619      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
12620      */
12621     getAt : function(index){
12622         return this.data.itemAt(index);
12623     },
12624
12625     /**
12626      * Returns a range of Records between specified indices.
12627      * @param {Number} startIndex (optional) The starting index (defaults to 0)
12628      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
12629      * @return {Roo.data.Record[]} An array of Records
12630      */
12631     getRange : function(start, end){
12632         return this.data.getRange(start, end);
12633     },
12634
12635     // private
12636     storeOptions : function(o){
12637         o = Roo.apply({}, o);
12638         delete o.callback;
12639         delete o.scope;
12640         this.lastOptions = o;
12641     },
12642
12643     /**
12644      * Loads the Record cache from the configured Proxy using the configured Reader.
12645      * <p>
12646      * If using remote paging, then the first load call must specify the <em>start</em>
12647      * and <em>limit</em> properties in the options.params property to establish the initial
12648      * position within the dataset, and the number of Records to cache on each read from the Proxy.
12649      * <p>
12650      * <strong>It is important to note that for remote data sources, loading is asynchronous,
12651      * and this call will return before the new data has been loaded. Perform any post-processing
12652      * in a callback function, or in a "load" event handler.</strong>
12653      * <p>
12654      * @param {Object} options An object containing properties which control loading options:<ul>
12655      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
12656      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
12657      * passed the following arguments:<ul>
12658      * <li>r : Roo.data.Record[]</li>
12659      * <li>options: Options object from the load call</li>
12660      * <li>success: Boolean success indicator</li></ul></li>
12661      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
12662      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
12663      * </ul>
12664      */
12665     load : function(options){
12666         options = options || {};
12667         if(this.fireEvent("beforeload", this, options) !== false){
12668             this.storeOptions(options);
12669             var p = Roo.apply(options.params || {}, this.baseParams);
12670             // if meta was not loaded from remote source.. try requesting it.
12671             if (!this.reader.metaFromRemote) {
12672                 p._requestMeta = 1;
12673             }
12674             if(this.sortInfo && this.remoteSort){
12675                 var pn = this.paramNames;
12676                 p[pn["sort"]] = this.sortInfo.field;
12677                 p[pn["dir"]] = this.sortInfo.direction;
12678             }
12679             if (this.multiSort) {
12680                 var pn = this.paramNames;
12681                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
12682             }
12683             
12684             this.proxy.load(p, this.reader, this.loadRecords, this, options);
12685         }
12686     },
12687
12688     /**
12689      * Reloads the Record cache from the configured Proxy using the configured Reader and
12690      * the options from the last load operation performed.
12691      * @param {Object} options (optional) An object containing properties which may override the options
12692      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
12693      * the most recently used options are reused).
12694      */
12695     reload : function(options){
12696         this.load(Roo.applyIf(options||{}, this.lastOptions));
12697     },
12698
12699     // private
12700     // Called as a callback by the Reader during a load operation.
12701     loadRecords : function(o, options, success){
12702         if(!o || success === false){
12703             if(success !== false){
12704                 this.fireEvent("load", this, [], options, o);
12705             }
12706             if(options.callback){
12707                 options.callback.call(options.scope || this, [], options, false);
12708             }
12709             return;
12710         }
12711         // if data returned failure - throw an exception.
12712         if (o.success === false) {
12713             // show a message if no listener is registered.
12714             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
12715                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
12716             }
12717             // loadmask wil be hooked into this..
12718             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
12719             return;
12720         }
12721         var r = o.records, t = o.totalRecords || r.length;
12722         
12723         this.fireEvent("beforeloadadd", this, r, options, o);
12724         
12725         if(!options || options.add !== true){
12726             if(this.pruneModifiedRecords){
12727                 this.modified = [];
12728             }
12729             for(var i = 0, len = r.length; i < len; i++){
12730                 r[i].join(this);
12731             }
12732             if(this.snapshot){
12733                 this.data = this.snapshot;
12734                 delete this.snapshot;
12735             }
12736             this.data.clear();
12737             this.data.addAll(r);
12738             this.totalLength = t;
12739             this.applySort();
12740             this.fireEvent("datachanged", this);
12741         }else{
12742             this.totalLength = Math.max(t, this.data.length+r.length);
12743             this.add(r);
12744         }
12745         
12746         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
12747                 
12748             var e = new Roo.data.Record({});
12749
12750             e.set(this.parent.displayField, this.parent.emptyTitle);
12751             e.set(this.parent.valueField, '');
12752
12753             this.insert(0, e);
12754         }
12755             
12756         this.fireEvent("load", this, r, options, o);
12757         if(options.callback){
12758             options.callback.call(options.scope || this, r, options, true);
12759         }
12760     },
12761
12762
12763     /**
12764      * Loads data from a passed data block. A Reader which understands the format of the data
12765      * must have been configured in the constructor.
12766      * @param {Object} data The data block from which to read the Records.  The format of the data expected
12767      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
12768      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
12769      */
12770     loadData : function(o, append){
12771         var r = this.reader.readRecords(o);
12772         this.loadRecords(r, {add: append}, true);
12773     },
12774     
12775      /**
12776      * using 'cn' the nested child reader read the child array into it's child stores.
12777      * @param {Object} rec The record with a 'children array
12778      */
12779     loadDataFromChildren : function(rec)
12780     {
12781         this.loadData(this.reader.toLoadData(rec));
12782     },
12783     
12784
12785     /**
12786      * Gets the number of cached records.
12787      * <p>
12788      * <em>If using paging, this may not be the total size of the dataset. If the data object
12789      * used by the Reader contains the dataset size, then the getTotalCount() function returns
12790      * the data set size</em>
12791      */
12792     getCount : function(){
12793         return this.data.length || 0;
12794     },
12795
12796     /**
12797      * Gets the total number of records in the dataset as returned by the server.
12798      * <p>
12799      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
12800      * the dataset size</em>
12801      */
12802     getTotalCount : function(){
12803         return this.totalLength || 0;
12804     },
12805
12806     /**
12807      * Returns the sort state of the Store as an object with two properties:
12808      * <pre><code>
12809  field {String} The name of the field by which the Records are sorted
12810  direction {String} The sort order, "ASC" or "DESC"
12811      * </code></pre>
12812      */
12813     getSortState : function(){
12814         return this.sortInfo;
12815     },
12816
12817     // private
12818     applySort : function(){
12819         if(this.sortInfo && !this.remoteSort){
12820             var s = this.sortInfo, f = s.field;
12821             var st = this.fields.get(f).sortType;
12822             var fn = function(r1, r2){
12823                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
12824                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
12825             };
12826             this.data.sort(s.direction, fn);
12827             if(this.snapshot && this.snapshot != this.data){
12828                 this.snapshot.sort(s.direction, fn);
12829             }
12830         }
12831     },
12832
12833     /**
12834      * Sets the default sort column and order to be used by the next load operation.
12835      * @param {String} fieldName The name of the field to sort by.
12836      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
12837      */
12838     setDefaultSort : function(field, dir){
12839         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
12840     },
12841
12842     /**
12843      * Sort the Records.
12844      * If remote sorting is used, the sort is performed on the server, and the cache is
12845      * reloaded. If local sorting is used, the cache is sorted internally.
12846      * @param {String} fieldName The name of the field to sort by.
12847      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
12848      */
12849     sort : function(fieldName, dir){
12850         var f = this.fields.get(fieldName);
12851         if(!dir){
12852             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
12853             
12854             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
12855                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
12856             }else{
12857                 dir = f.sortDir;
12858             }
12859         }
12860         this.sortToggle[f.name] = dir;
12861         this.sortInfo = {field: f.name, direction: dir};
12862         if(!this.remoteSort){
12863             this.applySort();
12864             this.fireEvent("datachanged", this);
12865         }else{
12866             this.load(this.lastOptions);
12867         }
12868     },
12869
12870     /**
12871      * Calls the specified function for each of the Records in the cache.
12872      * @param {Function} fn The function to call. The Record is passed as the first parameter.
12873      * Returning <em>false</em> aborts and exits the iteration.
12874      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
12875      */
12876     each : function(fn, scope){
12877         this.data.each(fn, scope);
12878     },
12879
12880     /**
12881      * Gets all records modified since the last commit.  Modified records are persisted across load operations
12882      * (e.g., during paging).
12883      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
12884      */
12885     getModifiedRecords : function(){
12886         return this.modified;
12887     },
12888
12889     // private
12890     createFilterFn : function(property, value, anyMatch){
12891         if(!value.exec){ // not a regex
12892             value = String(value);
12893             if(value.length == 0){
12894                 return false;
12895             }
12896             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
12897         }
12898         return function(r){
12899             return value.test(r.data[property]);
12900         };
12901     },
12902
12903     /**
12904      * Sums the value of <i>property</i> for each record between start and end and returns the result.
12905      * @param {String} property A field on your records
12906      * @param {Number} start The record index to start at (defaults to 0)
12907      * @param {Number} end The last record index to include (defaults to length - 1)
12908      * @return {Number} The sum
12909      */
12910     sum : function(property, start, end){
12911         var rs = this.data.items, v = 0;
12912         start = start || 0;
12913         end = (end || end === 0) ? end : rs.length-1;
12914
12915         for(var i = start; i <= end; i++){
12916             v += (rs[i].data[property] || 0);
12917         }
12918         return v;
12919     },
12920
12921     /**
12922      * Filter the records by a specified property.
12923      * @param {String} field A field on your records
12924      * @param {String/RegExp} value Either a string that the field
12925      * should start with or a RegExp to test against the field
12926      * @param {Boolean} anyMatch True to match any part not just the beginning
12927      */
12928     filter : function(property, value, anyMatch){
12929         var fn = this.createFilterFn(property, value, anyMatch);
12930         return fn ? this.filterBy(fn) : this.clearFilter();
12931     },
12932
12933     /**
12934      * Filter by a function. The specified function will be called with each
12935      * record in this data source. If the function returns true the record is included,
12936      * otherwise it is filtered.
12937      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
12938      * @param {Object} scope (optional) The scope of the function (defaults to this)
12939      */
12940     filterBy : function(fn, scope){
12941         this.snapshot = this.snapshot || this.data;
12942         this.data = this.queryBy(fn, scope||this);
12943         this.fireEvent("datachanged", this);
12944     },
12945
12946     /**
12947      * Query the records by a specified property.
12948      * @param {String} field A field on your records
12949      * @param {String/RegExp} value Either a string that the field
12950      * should start with or a RegExp to test against the field
12951      * @param {Boolean} anyMatch True to match any part not just the beginning
12952      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
12953      */
12954     query : function(property, value, anyMatch){
12955         var fn = this.createFilterFn(property, value, anyMatch);
12956         return fn ? this.queryBy(fn) : this.data.clone();
12957     },
12958
12959     /**
12960      * Query by a function. The specified function will be called with each
12961      * record in this data source. If the function returns true the record is included
12962      * in the results.
12963      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
12964      * @param {Object} scope (optional) The scope of the function (defaults to this)
12965       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
12966      **/
12967     queryBy : function(fn, scope){
12968         var data = this.snapshot || this.data;
12969         return data.filterBy(fn, scope||this);
12970     },
12971
12972     /**
12973      * Collects unique values for a particular dataIndex from this store.
12974      * @param {String} dataIndex The property to collect
12975      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
12976      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
12977      * @return {Array} An array of the unique values
12978      **/
12979     collect : function(dataIndex, allowNull, bypassFilter){
12980         var d = (bypassFilter === true && this.snapshot) ?
12981                 this.snapshot.items : this.data.items;
12982         var v, sv, r = [], l = {};
12983         for(var i = 0, len = d.length; i < len; i++){
12984             v = d[i].data[dataIndex];
12985             sv = String(v);
12986             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
12987                 l[sv] = true;
12988                 r[r.length] = v;
12989             }
12990         }
12991         return r;
12992     },
12993
12994     /**
12995      * Revert to a view of the Record cache with no filtering applied.
12996      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
12997      */
12998     clearFilter : function(suppressEvent){
12999         if(this.snapshot && this.snapshot != this.data){
13000             this.data = this.snapshot;
13001             delete this.snapshot;
13002             if(suppressEvent !== true){
13003                 this.fireEvent("datachanged", this);
13004             }
13005         }
13006     },
13007
13008     // private
13009     afterEdit : function(record){
13010         if(this.modified.indexOf(record) == -1){
13011             this.modified.push(record);
13012         }
13013         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13014     },
13015     
13016     // private
13017     afterReject : function(record){
13018         this.modified.remove(record);
13019         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13020     },
13021
13022     // private
13023     afterCommit : function(record){
13024         this.modified.remove(record);
13025         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13026     },
13027
13028     /**
13029      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13030      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13031      */
13032     commitChanges : function(){
13033         var m = this.modified.slice(0);
13034         this.modified = [];
13035         for(var i = 0, len = m.length; i < len; i++){
13036             m[i].commit();
13037         }
13038     },
13039
13040     /**
13041      * Cancel outstanding changes on all changed records.
13042      */
13043     rejectChanges : function(){
13044         var m = this.modified.slice(0);
13045         this.modified = [];
13046         for(var i = 0, len = m.length; i < len; i++){
13047             m[i].reject();
13048         }
13049     },
13050
13051     onMetaChange : function(meta, rtype, o){
13052         this.recordType = rtype;
13053         this.fields = rtype.prototype.fields;
13054         delete this.snapshot;
13055         this.sortInfo = meta.sortInfo || this.sortInfo;
13056         this.modified = [];
13057         this.fireEvent('metachange', this, this.reader.meta);
13058     },
13059     
13060     moveIndex : function(data, type)
13061     {
13062         var index = this.indexOf(data);
13063         
13064         var newIndex = index + type;
13065         
13066         this.remove(data);
13067         
13068         this.insert(newIndex, data);
13069         
13070     }
13071 });/*
13072  * Based on:
13073  * Ext JS Library 1.1.1
13074  * Copyright(c) 2006-2007, Ext JS, LLC.
13075  *
13076  * Originally Released Under LGPL - original licence link has changed is not relivant.
13077  *
13078  * Fork - LGPL
13079  * <script type="text/javascript">
13080  */
13081
13082 /**
13083  * @class Roo.data.SimpleStore
13084  * @extends Roo.data.Store
13085  * Small helper class to make creating Stores from Array data easier.
13086  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13087  * @cfg {Array} fields An array of field definition objects, or field name strings.
13088  * @cfg {Object} an existing reader (eg. copied from another store)
13089  * @cfg {Array} data The multi-dimensional array of data
13090  * @constructor
13091  * @param {Object} config
13092  */
13093 Roo.data.SimpleStore = function(config)
13094 {
13095     Roo.data.SimpleStore.superclass.constructor.call(this, {
13096         isLocal : true,
13097         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13098                 id: config.id
13099             },
13100             Roo.data.Record.create(config.fields)
13101         ),
13102         proxy : new Roo.data.MemoryProxy(config.data)
13103     });
13104     this.load();
13105 };
13106 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13107  * Based on:
13108  * Ext JS Library 1.1.1
13109  * Copyright(c) 2006-2007, Ext JS, LLC.
13110  *
13111  * Originally Released Under LGPL - original licence link has changed is not relivant.
13112  *
13113  * Fork - LGPL
13114  * <script type="text/javascript">
13115  */
13116
13117 /**
13118 /**
13119  * @extends Roo.data.Store
13120  * @class Roo.data.JsonStore
13121  * Small helper class to make creating Stores for JSON data easier. <br/>
13122 <pre><code>
13123 var store = new Roo.data.JsonStore({
13124     url: 'get-images.php',
13125     root: 'images',
13126     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13127 });
13128 </code></pre>
13129  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13130  * JsonReader and HttpProxy (unless inline data is provided).</b>
13131  * @cfg {Array} fields An array of field definition objects, or field name strings.
13132  * @constructor
13133  * @param {Object} config
13134  */
13135 Roo.data.JsonStore = function(c){
13136     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13137         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13138         reader: new Roo.data.JsonReader(c, c.fields)
13139     }));
13140 };
13141 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13142  * Based on:
13143  * Ext JS Library 1.1.1
13144  * Copyright(c) 2006-2007, Ext JS, LLC.
13145  *
13146  * Originally Released Under LGPL - original licence link has changed is not relivant.
13147  *
13148  * Fork - LGPL
13149  * <script type="text/javascript">
13150  */
13151
13152  
13153 Roo.data.Field = function(config){
13154     if(typeof config == "string"){
13155         config = {name: config};
13156     }
13157     Roo.apply(this, config);
13158     
13159     if(!this.type){
13160         this.type = "auto";
13161     }
13162     
13163     var st = Roo.data.SortTypes;
13164     // named sortTypes are supported, here we look them up
13165     if(typeof this.sortType == "string"){
13166         this.sortType = st[this.sortType];
13167     }
13168     
13169     // set default sortType for strings and dates
13170     if(!this.sortType){
13171         switch(this.type){
13172             case "string":
13173                 this.sortType = st.asUCString;
13174                 break;
13175             case "date":
13176                 this.sortType = st.asDate;
13177                 break;
13178             default:
13179                 this.sortType = st.none;
13180         }
13181     }
13182
13183     // define once
13184     var stripRe = /[\$,%]/g;
13185
13186     // prebuilt conversion function for this field, instead of
13187     // switching every time we're reading a value
13188     if(!this.convert){
13189         var cv, dateFormat = this.dateFormat;
13190         switch(this.type){
13191             case "":
13192             case "auto":
13193             case undefined:
13194                 cv = function(v){ return v; };
13195                 break;
13196             case "string":
13197                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
13198                 break;
13199             case "int":
13200                 cv = function(v){
13201                     return v !== undefined && v !== null && v !== '' ?
13202                            parseInt(String(v).replace(stripRe, ""), 10) : '';
13203                     };
13204                 break;
13205             case "float":
13206                 cv = function(v){
13207                     return v !== undefined && v !== null && v !== '' ?
13208                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
13209                     };
13210                 break;
13211             case "bool":
13212             case "boolean":
13213                 cv = function(v){ return v === true || v === "true" || v == 1; };
13214                 break;
13215             case "date":
13216                 cv = function(v){
13217                     if(!v){
13218                         return '';
13219                     }
13220                     if(v instanceof Date){
13221                         return v;
13222                     }
13223                     if(dateFormat){
13224                         if(dateFormat == "timestamp"){
13225                             return new Date(v*1000);
13226                         }
13227                         return Date.parseDate(v, dateFormat);
13228                     }
13229                     var parsed = Date.parse(v);
13230                     return parsed ? new Date(parsed) : null;
13231                 };
13232              break;
13233             
13234         }
13235         this.convert = cv;
13236     }
13237 };
13238
13239 Roo.data.Field.prototype = {
13240     dateFormat: null,
13241     defaultValue: "",
13242     mapping: null,
13243     sortType : null,
13244     sortDir : "ASC"
13245 };/*
13246  * Based on:
13247  * Ext JS Library 1.1.1
13248  * Copyright(c) 2006-2007, Ext JS, LLC.
13249  *
13250  * Originally Released Under LGPL - original licence link has changed is not relivant.
13251  *
13252  * Fork - LGPL
13253  * <script type="text/javascript">
13254  */
13255  
13256 // Base class for reading structured data from a data source.  This class is intended to be
13257 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
13258
13259 /**
13260  * @class Roo.data.DataReader
13261  * Base class for reading structured data from a data source.  This class is intended to be
13262  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
13263  */
13264
13265 Roo.data.DataReader = function(meta, recordType){
13266     
13267     this.meta = meta;
13268     
13269     this.recordType = recordType instanceof Array ? 
13270         Roo.data.Record.create(recordType) : recordType;
13271 };
13272
13273 Roo.data.DataReader.prototype = {
13274     
13275     
13276     readerType : 'Data',
13277      /**
13278      * Create an empty record
13279      * @param {Object} data (optional) - overlay some values
13280      * @return {Roo.data.Record} record created.
13281      */
13282     newRow :  function(d) {
13283         var da =  {};
13284         this.recordType.prototype.fields.each(function(c) {
13285             switch( c.type) {
13286                 case 'int' : da[c.name] = 0; break;
13287                 case 'date' : da[c.name] = new Date(); break;
13288                 case 'float' : da[c.name] = 0.0; break;
13289                 case 'boolean' : da[c.name] = false; break;
13290                 default : da[c.name] = ""; break;
13291             }
13292             
13293         });
13294         return new this.recordType(Roo.apply(da, d));
13295     }
13296     
13297     
13298 };/*
13299  * Based on:
13300  * Ext JS Library 1.1.1
13301  * Copyright(c) 2006-2007, Ext JS, LLC.
13302  *
13303  * Originally Released Under LGPL - original licence link has changed is not relivant.
13304  *
13305  * Fork - LGPL
13306  * <script type="text/javascript">
13307  */
13308
13309 /**
13310  * @class Roo.data.DataProxy
13311  * @extends Roo.data.Observable
13312  * This class is an abstract base class for implementations which provide retrieval of
13313  * unformatted data objects.<br>
13314  * <p>
13315  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
13316  * (of the appropriate type which knows how to parse the data object) to provide a block of
13317  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
13318  * <p>
13319  * Custom implementations must implement the load method as described in
13320  * {@link Roo.data.HttpProxy#load}.
13321  */
13322 Roo.data.DataProxy = function(){
13323     this.addEvents({
13324         /**
13325          * @event beforeload
13326          * Fires before a network request is made to retrieve a data object.
13327          * @param {Object} This DataProxy object.
13328          * @param {Object} params The params parameter to the load function.
13329          */
13330         beforeload : true,
13331         /**
13332          * @event load
13333          * Fires before the load method's callback is called.
13334          * @param {Object} This DataProxy object.
13335          * @param {Object} o The data object.
13336          * @param {Object} arg The callback argument object passed to the load function.
13337          */
13338         load : true,
13339         /**
13340          * @event loadexception
13341          * Fires if an Exception occurs during data retrieval.
13342          * @param {Object} This DataProxy object.
13343          * @param {Object} o The data object.
13344          * @param {Object} arg The callback argument object passed to the load function.
13345          * @param {Object} e The Exception.
13346          */
13347         loadexception : true
13348     });
13349     Roo.data.DataProxy.superclass.constructor.call(this);
13350 };
13351
13352 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
13353
13354     /**
13355      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
13356      */
13357 /*
13358  * Based on:
13359  * Ext JS Library 1.1.1
13360  * Copyright(c) 2006-2007, Ext JS, LLC.
13361  *
13362  * Originally Released Under LGPL - original licence link has changed is not relivant.
13363  *
13364  * Fork - LGPL
13365  * <script type="text/javascript">
13366  */
13367 /**
13368  * @class Roo.data.MemoryProxy
13369  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
13370  * to the Reader when its load method is called.
13371  * @constructor
13372  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
13373  */
13374 Roo.data.MemoryProxy = function(data){
13375     if (data.data) {
13376         data = data.data;
13377     }
13378     Roo.data.MemoryProxy.superclass.constructor.call(this);
13379     this.data = data;
13380 };
13381
13382 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
13383     
13384     /**
13385      * Load data from the requested source (in this case an in-memory
13386      * data object passed to the constructor), read the data object into
13387      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
13388      * process that block using the passed callback.
13389      * @param {Object} params This parameter is not used by the MemoryProxy class.
13390      * @param {Roo.data.DataReader} reader The Reader object which converts the data
13391      * object into a block of Roo.data.Records.
13392      * @param {Function} callback The function into which to pass the block of Roo.data.records.
13393      * The function must be passed <ul>
13394      * <li>The Record block object</li>
13395      * <li>The "arg" argument from the load function</li>
13396      * <li>A boolean success indicator</li>
13397      * </ul>
13398      * @param {Object} scope The scope in which to call the callback
13399      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
13400      */
13401     load : function(params, reader, callback, scope, arg){
13402         params = params || {};
13403         var result;
13404         try {
13405             result = reader.readRecords(params.data ? params.data :this.data);
13406         }catch(e){
13407             this.fireEvent("loadexception", this, arg, null, e);
13408             callback.call(scope, null, arg, false);
13409             return;
13410         }
13411         callback.call(scope, result, arg, true);
13412     },
13413     
13414     // private
13415     update : function(params, records){
13416         
13417     }
13418 });/*
13419  * Based on:
13420  * Ext JS Library 1.1.1
13421  * Copyright(c) 2006-2007, Ext JS, LLC.
13422  *
13423  * Originally Released Under LGPL - original licence link has changed is not relivant.
13424  *
13425  * Fork - LGPL
13426  * <script type="text/javascript">
13427  */
13428 /**
13429  * @class Roo.data.HttpProxy
13430  * @extends Roo.data.DataProxy
13431  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
13432  * configured to reference a certain URL.<br><br>
13433  * <p>
13434  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
13435  * from which the running page was served.<br><br>
13436  * <p>
13437  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
13438  * <p>
13439  * Be aware that to enable the browser to parse an XML document, the server must set
13440  * the Content-Type header in the HTTP response to "text/xml".
13441  * @constructor
13442  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
13443  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
13444  * will be used to make the request.
13445  */
13446 Roo.data.HttpProxy = function(conn){
13447     Roo.data.HttpProxy.superclass.constructor.call(this);
13448     // is conn a conn config or a real conn?
13449     this.conn = conn;
13450     this.useAjax = !conn || !conn.events;
13451   
13452 };
13453
13454 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
13455     // thse are take from connection...
13456     
13457     /**
13458      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
13459      */
13460     /**
13461      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
13462      * extra parameters to each request made by this object. (defaults to undefined)
13463      */
13464     /**
13465      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
13466      *  to each request made by this object. (defaults to undefined)
13467      */
13468     /**
13469      * @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)
13470      */
13471     /**
13472      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
13473      */
13474      /**
13475      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
13476      * @type Boolean
13477      */
13478   
13479
13480     /**
13481      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
13482      * @type Boolean
13483      */
13484     /**
13485      * Return the {@link Roo.data.Connection} object being used by this Proxy.
13486      * @return {Connection} The Connection object. This object may be used to subscribe to events on
13487      * a finer-grained basis than the DataProxy events.
13488      */
13489     getConnection : function(){
13490         return this.useAjax ? Roo.Ajax : this.conn;
13491     },
13492
13493     /**
13494      * Load data from the configured {@link Roo.data.Connection}, read the data object into
13495      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
13496      * process that block using the passed callback.
13497      * @param {Object} params An object containing properties which are to be used as HTTP parameters
13498      * for the request to the remote server.
13499      * @param {Roo.data.DataReader} reader The Reader object which converts the data
13500      * object into a block of Roo.data.Records.
13501      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
13502      * The function must be passed <ul>
13503      * <li>The Record block object</li>
13504      * <li>The "arg" argument from the load function</li>
13505      * <li>A boolean success indicator</li>
13506      * </ul>
13507      * @param {Object} scope The scope in which to call the callback
13508      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
13509      */
13510     load : function(params, reader, callback, scope, arg){
13511         if(this.fireEvent("beforeload", this, params) !== false){
13512             var  o = {
13513                 params : params || {},
13514                 request: {
13515                     callback : callback,
13516                     scope : scope,
13517                     arg : arg
13518                 },
13519                 reader: reader,
13520                 callback : this.loadResponse,
13521                 scope: this
13522             };
13523             if(this.useAjax){
13524                 Roo.applyIf(o, this.conn);
13525                 if(this.activeRequest){
13526                     Roo.Ajax.abort(this.activeRequest);
13527                 }
13528                 this.activeRequest = Roo.Ajax.request(o);
13529             }else{
13530                 this.conn.request(o);
13531             }
13532         }else{
13533             callback.call(scope||this, null, arg, false);
13534         }
13535     },
13536
13537     // private
13538     loadResponse : function(o, success, response){
13539         delete this.activeRequest;
13540         if(!success){
13541             this.fireEvent("loadexception", this, o, response);
13542             o.request.callback.call(o.request.scope, null, o.request.arg, false);
13543             return;
13544         }
13545         var result;
13546         try {
13547             result = o.reader.read(response);
13548         }catch(e){
13549             this.fireEvent("loadexception", this, o, response, e);
13550             o.request.callback.call(o.request.scope, null, o.request.arg, false);
13551             return;
13552         }
13553         
13554         this.fireEvent("load", this, o, o.request.arg);
13555         o.request.callback.call(o.request.scope, result, o.request.arg, true);
13556     },
13557
13558     // private
13559     update : function(dataSet){
13560
13561     },
13562
13563     // private
13564     updateResponse : function(dataSet){
13565
13566     }
13567 });/*
13568  * Based on:
13569  * Ext JS Library 1.1.1
13570  * Copyright(c) 2006-2007, Ext JS, LLC.
13571  *
13572  * Originally Released Under LGPL - original licence link has changed is not relivant.
13573  *
13574  * Fork - LGPL
13575  * <script type="text/javascript">
13576  */
13577
13578 /**
13579  * @class Roo.data.ScriptTagProxy
13580  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
13581  * other than the originating domain of the running page.<br><br>
13582  * <p>
13583  * <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
13584  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
13585  * <p>
13586  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
13587  * source code that is used as the source inside a &lt;script> tag.<br><br>
13588  * <p>
13589  * In order for the browser to process the returned data, the server must wrap the data object
13590  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
13591  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
13592  * depending on whether the callback name was passed:
13593  * <p>
13594  * <pre><code>
13595 boolean scriptTag = false;
13596 String cb = request.getParameter("callback");
13597 if (cb != null) {
13598     scriptTag = true;
13599     response.setContentType("text/javascript");
13600 } else {
13601     response.setContentType("application/x-json");
13602 }
13603 Writer out = response.getWriter();
13604 if (scriptTag) {
13605     out.write(cb + "(");
13606 }
13607 out.print(dataBlock.toJsonString());
13608 if (scriptTag) {
13609     out.write(");");
13610 }
13611 </pre></code>
13612  *
13613  * @constructor
13614  * @param {Object} config A configuration object.
13615  */
13616 Roo.data.ScriptTagProxy = function(config){
13617     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
13618     Roo.apply(this, config);
13619     this.head = document.getElementsByTagName("head")[0];
13620 };
13621
13622 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
13623
13624 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
13625     /**
13626      * @cfg {String} url The URL from which to request the data object.
13627      */
13628     /**
13629      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
13630      */
13631     timeout : 30000,
13632     /**
13633      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
13634      * the server the name of the callback function set up by the load call to process the returned data object.
13635      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
13636      * javascript output which calls this named function passing the data object as its only parameter.
13637      */
13638     callbackParam : "callback",
13639     /**
13640      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
13641      * name to the request.
13642      */
13643     nocache : true,
13644
13645     /**
13646      * Load data from the configured URL, read the data object into
13647      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
13648      * process that block using the passed callback.
13649      * @param {Object} params An object containing properties which are to be used as HTTP parameters
13650      * for the request to the remote server.
13651      * @param {Roo.data.DataReader} reader The Reader object which converts the data
13652      * object into a block of Roo.data.Records.
13653      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
13654      * The function must be passed <ul>
13655      * <li>The Record block object</li>
13656      * <li>The "arg" argument from the load function</li>
13657      * <li>A boolean success indicator</li>
13658      * </ul>
13659      * @param {Object} scope The scope in which to call the callback
13660      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
13661      */
13662     load : function(params, reader, callback, scope, arg){
13663         if(this.fireEvent("beforeload", this, params) !== false){
13664
13665             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
13666
13667             var url = this.url;
13668             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
13669             if(this.nocache){
13670                 url += "&_dc=" + (new Date().getTime());
13671             }
13672             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
13673             var trans = {
13674                 id : transId,
13675                 cb : "stcCallback"+transId,
13676                 scriptId : "stcScript"+transId,
13677                 params : params,
13678                 arg : arg,
13679                 url : url,
13680                 callback : callback,
13681                 scope : scope,
13682                 reader : reader
13683             };
13684             var conn = this;
13685
13686             window[trans.cb] = function(o){
13687                 conn.handleResponse(o, trans);
13688             };
13689
13690             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
13691
13692             if(this.autoAbort !== false){
13693                 this.abort();
13694             }
13695
13696             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
13697
13698             var script = document.createElement("script");
13699             script.setAttribute("src", url);
13700             script.setAttribute("type", "text/javascript");
13701             script.setAttribute("id", trans.scriptId);
13702             this.head.appendChild(script);
13703
13704             this.trans = trans;
13705         }else{
13706             callback.call(scope||this, null, arg, false);
13707         }
13708     },
13709
13710     // private
13711     isLoading : function(){
13712         return this.trans ? true : false;
13713     },
13714
13715     /**
13716      * Abort the current server request.
13717      */
13718     abort : function(){
13719         if(this.isLoading()){
13720             this.destroyTrans(this.trans);
13721         }
13722     },
13723
13724     // private
13725     destroyTrans : function(trans, isLoaded){
13726         this.head.removeChild(document.getElementById(trans.scriptId));
13727         clearTimeout(trans.timeoutId);
13728         if(isLoaded){
13729             window[trans.cb] = undefined;
13730             try{
13731                 delete window[trans.cb];
13732             }catch(e){}
13733         }else{
13734             // if hasn't been loaded, wait for load to remove it to prevent script error
13735             window[trans.cb] = function(){
13736                 window[trans.cb] = undefined;
13737                 try{
13738                     delete window[trans.cb];
13739                 }catch(e){}
13740             };
13741         }
13742     },
13743
13744     // private
13745     handleResponse : function(o, trans){
13746         this.trans = false;
13747         this.destroyTrans(trans, true);
13748         var result;
13749         try {
13750             result = trans.reader.readRecords(o);
13751         }catch(e){
13752             this.fireEvent("loadexception", this, o, trans.arg, e);
13753             trans.callback.call(trans.scope||window, null, trans.arg, false);
13754             return;
13755         }
13756         this.fireEvent("load", this, o, trans.arg);
13757         trans.callback.call(trans.scope||window, result, trans.arg, true);
13758     },
13759
13760     // private
13761     handleFailure : function(trans){
13762         this.trans = false;
13763         this.destroyTrans(trans, false);
13764         this.fireEvent("loadexception", this, null, trans.arg);
13765         trans.callback.call(trans.scope||window, null, trans.arg, false);
13766     }
13767 });/*
13768  * Based on:
13769  * Ext JS Library 1.1.1
13770  * Copyright(c) 2006-2007, Ext JS, LLC.
13771  *
13772  * Originally Released Under LGPL - original licence link has changed is not relivant.
13773  *
13774  * Fork - LGPL
13775  * <script type="text/javascript">
13776  */
13777
13778 /**
13779  * @class Roo.data.JsonReader
13780  * @extends Roo.data.DataReader
13781  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
13782  * based on mappings in a provided Roo.data.Record constructor.
13783  * 
13784  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
13785  * in the reply previously. 
13786  * 
13787  * <p>
13788  * Example code:
13789  * <pre><code>
13790 var RecordDef = Roo.data.Record.create([
13791     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
13792     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
13793 ]);
13794 var myReader = new Roo.data.JsonReader({
13795     totalProperty: "results",    // The property which contains the total dataset size (optional)
13796     root: "rows",                // The property which contains an Array of row objects
13797     id: "id"                     // The property within each row object that provides an ID for the record (optional)
13798 }, RecordDef);
13799 </code></pre>
13800  * <p>
13801  * This would consume a JSON file like this:
13802  * <pre><code>
13803 { 'results': 2, 'rows': [
13804     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
13805     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
13806 }
13807 </code></pre>
13808  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
13809  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
13810  * paged from the remote server.
13811  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
13812  * @cfg {String} root name of the property which contains the Array of row objects.
13813  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
13814  * @cfg {Array} fields Array of field definition objects
13815  * @constructor
13816  * Create a new JsonReader
13817  * @param {Object} meta Metadata configuration options
13818  * @param {Object} recordType Either an Array of field definition objects,
13819  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
13820  */
13821 Roo.data.JsonReader = function(meta, recordType){
13822     
13823     meta = meta || {};
13824     // set some defaults:
13825     Roo.applyIf(meta, {
13826         totalProperty: 'total',
13827         successProperty : 'success',
13828         root : 'data',
13829         id : 'id'
13830     });
13831     
13832     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
13833 };
13834 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
13835     
13836     readerType : 'Json',
13837     
13838     /**
13839      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
13840      * Used by Store query builder to append _requestMeta to params.
13841      * 
13842      */
13843     metaFromRemote : false,
13844     /**
13845      * This method is only used by a DataProxy which has retrieved data from a remote server.
13846      * @param {Object} response The XHR object which contains the JSON data in its responseText.
13847      * @return {Object} data A data block which is used by an Roo.data.Store object as
13848      * a cache of Roo.data.Records.
13849      */
13850     read : function(response){
13851         var json = response.responseText;
13852        
13853         var o = /* eval:var:o */ eval("("+json+")");
13854         if(!o) {
13855             throw {message: "JsonReader.read: Json object not found"};
13856         }
13857         
13858         if(o.metaData){
13859             
13860             delete this.ef;
13861             this.metaFromRemote = true;
13862             this.meta = o.metaData;
13863             this.recordType = Roo.data.Record.create(o.metaData.fields);
13864             this.onMetaChange(this.meta, this.recordType, o);
13865         }
13866         return this.readRecords(o);
13867     },
13868
13869     // private function a store will implement
13870     onMetaChange : function(meta, recordType, o){
13871
13872     },
13873
13874     /**
13875          * @ignore
13876          */
13877     simpleAccess: function(obj, subsc) {
13878         return obj[subsc];
13879     },
13880
13881         /**
13882          * @ignore
13883          */
13884     getJsonAccessor: function(){
13885         var re = /[\[\.]/;
13886         return function(expr) {
13887             try {
13888                 return(re.test(expr))
13889                     ? new Function("obj", "return obj." + expr)
13890                     : function(obj){
13891                         return obj[expr];
13892                     };
13893             } catch(e){}
13894             return Roo.emptyFn;
13895         };
13896     }(),
13897
13898     /**
13899      * Create a data block containing Roo.data.Records from an XML document.
13900      * @param {Object} o An object which contains an Array of row objects in the property specified
13901      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
13902      * which contains the total size of the dataset.
13903      * @return {Object} data A data block which is used by an Roo.data.Store object as
13904      * a cache of Roo.data.Records.
13905      */
13906     readRecords : function(o){
13907         /**
13908          * After any data loads, the raw JSON data is available for further custom processing.
13909          * @type Object
13910          */
13911         this.o = o;
13912         var s = this.meta, Record = this.recordType,
13913             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
13914
13915 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
13916         if (!this.ef) {
13917             if(s.totalProperty) {
13918                     this.getTotal = this.getJsonAccessor(s.totalProperty);
13919                 }
13920                 if(s.successProperty) {
13921                     this.getSuccess = this.getJsonAccessor(s.successProperty);
13922                 }
13923                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
13924                 if (s.id) {
13925                         var g = this.getJsonAccessor(s.id);
13926                         this.getId = function(rec) {
13927                                 var r = g(rec);  
13928                                 return (r === undefined || r === "") ? null : r;
13929                         };
13930                 } else {
13931                         this.getId = function(){return null;};
13932                 }
13933             this.ef = [];
13934             for(var jj = 0; jj < fl; jj++){
13935                 f = fi[jj];
13936                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
13937                 this.ef[jj] = this.getJsonAccessor(map);
13938             }
13939         }
13940
13941         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
13942         if(s.totalProperty){
13943             var vt = parseInt(this.getTotal(o), 10);
13944             if(!isNaN(vt)){
13945                 totalRecords = vt;
13946             }
13947         }
13948         if(s.successProperty){
13949             var vs = this.getSuccess(o);
13950             if(vs === false || vs === 'false'){
13951                 success = false;
13952             }
13953         }
13954         var records = [];
13955         for(var i = 0; i < c; i++){
13956                 var n = root[i];
13957             var values = {};
13958             var id = this.getId(n);
13959             for(var j = 0; j < fl; j++){
13960                 f = fi[j];
13961             var v = this.ef[j](n);
13962             if (!f.convert) {
13963                 Roo.log('missing convert for ' + f.name);
13964                 Roo.log(f);
13965                 continue;
13966             }
13967             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
13968             }
13969             var record = new Record(values, id);
13970             record.json = n;
13971             records[i] = record;
13972         }
13973         return {
13974             raw : o,
13975             success : success,
13976             records : records,
13977             totalRecords : totalRecords
13978         };
13979     },
13980     // used when loading children.. @see loadDataFromChildren
13981     toLoadData: function(rec)
13982     {
13983         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
13984         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
13985         return { data : data, total : data.length };
13986         
13987     }
13988 });/*
13989  * Based on:
13990  * Ext JS Library 1.1.1
13991  * Copyright(c) 2006-2007, Ext JS, LLC.
13992  *
13993  * Originally Released Under LGPL - original licence link has changed is not relivant.
13994  *
13995  * Fork - LGPL
13996  * <script type="text/javascript">
13997  */
13998
13999 /**
14000  * @class Roo.data.ArrayReader
14001  * @extends Roo.data.DataReader
14002  * Data reader class to create an Array of Roo.data.Record objects from an Array.
14003  * Each element of that Array represents a row of data fields. The
14004  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14005  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14006  * <p>
14007  * Example code:.
14008  * <pre><code>
14009 var RecordDef = Roo.data.Record.create([
14010     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
14011     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
14012 ]);
14013 var myReader = new Roo.data.ArrayReader({
14014     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
14015 }, RecordDef);
14016 </code></pre>
14017  * <p>
14018  * This would consume an Array like this:
14019  * <pre><code>
14020 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14021   </code></pre>
14022  
14023  * @constructor
14024  * Create a new JsonReader
14025  * @param {Object} meta Metadata configuration options.
14026  * @param {Object|Array} recordType Either an Array of field definition objects
14027  * 
14028  * @cfg {Array} fields Array of field definition objects
14029  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14030  * as specified to {@link Roo.data.Record#create},
14031  * or an {@link Roo.data.Record} object
14032  *
14033  * 
14034  * created using {@link Roo.data.Record#create}.
14035  */
14036 Roo.data.ArrayReader = function(meta, recordType)
14037 {    
14038     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14039 };
14040
14041 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14042     
14043       /**
14044      * Create a data block containing Roo.data.Records from an XML document.
14045      * @param {Object} o An Array of row objects which represents the dataset.
14046      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14047      * a cache of Roo.data.Records.
14048      */
14049     readRecords : function(o)
14050     {
14051         var sid = this.meta ? this.meta.id : null;
14052         var recordType = this.recordType, fields = recordType.prototype.fields;
14053         var records = [];
14054         var root = o;
14055         for(var i = 0; i < root.length; i++){
14056                 var n = root[i];
14057             var values = {};
14058             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14059             for(var j = 0, jlen = fields.length; j < jlen; j++){
14060                 var f = fields.items[j];
14061                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14062                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14063                 v = f.convert(v);
14064                 values[f.name] = v;
14065             }
14066             var record = new recordType(values, id);
14067             record.json = n;
14068             records[records.length] = record;
14069         }
14070         return {
14071             records : records,
14072             totalRecords : records.length
14073         };
14074     },
14075     // used when loading children.. @see loadDataFromChildren
14076     toLoadData: function(rec)
14077     {
14078         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14079         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14080         
14081     }
14082     
14083     
14084 });/*
14085  * - LGPL
14086  * * 
14087  */
14088
14089 /**
14090  * @class Roo.bootstrap.ComboBox
14091  * @extends Roo.bootstrap.TriggerField
14092  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14093  * @cfg {Boolean} append (true|false) default false
14094  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14095  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14096  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14097  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14098  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14099  * @cfg {Boolean} animate default true
14100  * @cfg {Boolean} emptyResultText only for touch device
14101  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14102  * @cfg {String} emptyTitle default ''
14103  * @constructor
14104  * Create a new ComboBox.
14105  * @param {Object} config Configuration options
14106  */
14107 Roo.bootstrap.ComboBox = function(config){
14108     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14109     this.addEvents({
14110         /**
14111          * @event expand
14112          * Fires when the dropdown list is expanded
14113         * @param {Roo.bootstrap.ComboBox} combo This combo box
14114         */
14115         'expand' : true,
14116         /**
14117          * @event collapse
14118          * Fires when the dropdown list is collapsed
14119         * @param {Roo.bootstrap.ComboBox} combo This combo box
14120         */
14121         'collapse' : true,
14122         /**
14123          * @event beforeselect
14124          * Fires before a list item is selected. Return false to cancel the selection.
14125         * @param {Roo.bootstrap.ComboBox} combo This combo box
14126         * @param {Roo.data.Record} record The data record returned from the underlying store
14127         * @param {Number} index The index of the selected item in the dropdown list
14128         */
14129         'beforeselect' : true,
14130         /**
14131          * @event select
14132          * Fires when a list item is selected
14133         * @param {Roo.bootstrap.ComboBox} combo This combo box
14134         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14135         * @param {Number} index The index of the selected item in the dropdown list
14136         */
14137         'select' : true,
14138         /**
14139          * @event beforequery
14140          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14141          * The event object passed has these properties:
14142         * @param {Roo.bootstrap.ComboBox} combo This combo box
14143         * @param {String} query The query
14144         * @param {Boolean} forceAll true to force "all" query
14145         * @param {Boolean} cancel true to cancel the query
14146         * @param {Object} e The query event object
14147         */
14148         'beforequery': true,
14149          /**
14150          * @event add
14151          * Fires when the 'add' icon is pressed (add a listener to enable add button)
14152         * @param {Roo.bootstrap.ComboBox} combo This combo box
14153         */
14154         'add' : true,
14155         /**
14156          * @event edit
14157          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14158         * @param {Roo.bootstrap.ComboBox} combo This combo box
14159         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14160         */
14161         'edit' : true,
14162         /**
14163          * @event remove
14164          * Fires when the remove value from the combobox array
14165         * @param {Roo.bootstrap.ComboBox} combo This combo box
14166         */
14167         'remove' : true,
14168         /**
14169          * @event afterremove
14170          * Fires when the remove value from the combobox array
14171         * @param {Roo.bootstrap.ComboBox} combo This combo box
14172         */
14173         'afterremove' : true,
14174         /**
14175          * @event specialfilter
14176          * Fires when specialfilter
14177             * @param {Roo.bootstrap.ComboBox} combo This combo box
14178             */
14179         'specialfilter' : true,
14180         /**
14181          * @event tick
14182          * Fires when tick the element
14183             * @param {Roo.bootstrap.ComboBox} combo This combo box
14184             */
14185         'tick' : true,
14186         /**
14187          * @event touchviewdisplay
14188          * Fires when touch view require special display (default is using displayField)
14189             * @param {Roo.bootstrap.ComboBox} combo This combo box
14190             * @param {Object} cfg set html .
14191             */
14192         'touchviewdisplay' : true
14193         
14194     });
14195     
14196     this.item = [];
14197     this.tickItems = [];
14198     
14199     this.selectedIndex = -1;
14200     if(this.mode == 'local'){
14201         if(config.queryDelay === undefined){
14202             this.queryDelay = 10;
14203         }
14204         if(config.minChars === undefined){
14205             this.minChars = 0;
14206         }
14207     }
14208 };
14209
14210 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
14211      
14212     /**
14213      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
14214      * rendering into an Roo.Editor, defaults to false)
14215      */
14216     /**
14217      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
14218      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
14219      */
14220     /**
14221      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
14222      */
14223     /**
14224      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
14225      * the dropdown list (defaults to undefined, with no header element)
14226      */
14227
14228      /**
14229      * @cfg {String/Roo.Template} tpl The template to use to render the output
14230      */
14231      
14232      /**
14233      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
14234      */
14235     listWidth: undefined,
14236     /**
14237      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
14238      * mode = 'remote' or 'text' if mode = 'local')
14239      */
14240     displayField: undefined,
14241     
14242     /**
14243      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
14244      * mode = 'remote' or 'value' if mode = 'local'). 
14245      * Note: use of a valueField requires the user make a selection
14246      * in order for a value to be mapped.
14247      */
14248     valueField: undefined,
14249     /**
14250      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
14251      */
14252     modalTitle : '',
14253     
14254     /**
14255      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
14256      * field's data value (defaults to the underlying DOM element's name)
14257      */
14258     hiddenName: undefined,
14259     /**
14260      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
14261      */
14262     listClass: '',
14263     /**
14264      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
14265      */
14266     selectedClass: 'active',
14267     
14268     /**
14269      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14270      */
14271     shadow:'sides',
14272     /**
14273      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
14274      * anchor positions (defaults to 'tl-bl')
14275      */
14276     listAlign: 'tl-bl?',
14277     /**
14278      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
14279      */
14280     maxHeight: 300,
14281     /**
14282      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
14283      * query specified by the allQuery config option (defaults to 'query')
14284      */
14285     triggerAction: 'query',
14286     /**
14287      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
14288      * (defaults to 4, does not apply if editable = false)
14289      */
14290     minChars : 4,
14291     /**
14292      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
14293      * delay (typeAheadDelay) if it matches a known value (defaults to false)
14294      */
14295     typeAhead: false,
14296     /**
14297      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
14298      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
14299      */
14300     queryDelay: 500,
14301     /**
14302      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
14303      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
14304      */
14305     pageSize: 0,
14306     /**
14307      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
14308      * when editable = true (defaults to false)
14309      */
14310     selectOnFocus:false,
14311     /**
14312      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
14313      */
14314     queryParam: 'query',
14315     /**
14316      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
14317      * when mode = 'remote' (defaults to 'Loading...')
14318      */
14319     loadingText: 'Loading...',
14320     /**
14321      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
14322      */
14323     resizable: false,
14324     /**
14325      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
14326      */
14327     handleHeight : 8,
14328     /**
14329      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
14330      * traditional select (defaults to true)
14331      */
14332     editable: true,
14333     /**
14334      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
14335      */
14336     allQuery: '',
14337     /**
14338      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
14339      */
14340     mode: 'remote',
14341     /**
14342      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
14343      * listWidth has a higher value)
14344      */
14345     minListWidth : 70,
14346     /**
14347      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
14348      * allow the user to set arbitrary text into the field (defaults to false)
14349      */
14350     forceSelection:false,
14351     /**
14352      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
14353      * if typeAhead = true (defaults to 250)
14354      */
14355     typeAheadDelay : 250,
14356     /**
14357      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
14358      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
14359      */
14360     valueNotFoundText : undefined,
14361     /**
14362      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
14363      */
14364     blockFocus : false,
14365     
14366     /**
14367      * @cfg {Boolean} disableClear Disable showing of clear button.
14368      */
14369     disableClear : false,
14370     /**
14371      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
14372      */
14373     alwaysQuery : false,
14374     
14375     /**
14376      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
14377      */
14378     multiple : false,
14379     
14380     /**
14381      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
14382      */
14383     invalidClass : "has-warning",
14384     
14385     /**
14386      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
14387      */
14388     validClass : "has-success",
14389     
14390     /**
14391      * @cfg {Boolean} specialFilter (true|false) special filter default false
14392      */
14393     specialFilter : false,
14394     
14395     /**
14396      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
14397      */
14398     mobileTouchView : true,
14399     
14400     /**
14401      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
14402      */
14403     useNativeIOS : false,
14404     
14405     /**
14406      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
14407      */
14408     mobile_restrict_height : false,
14409     
14410     ios_options : false,
14411     
14412     //private
14413     addicon : false,
14414     editicon: false,
14415     
14416     page: 0,
14417     hasQuery: false,
14418     append: false,
14419     loadNext: false,
14420     autoFocus : true,
14421     tickable : false,
14422     btnPosition : 'right',
14423     triggerList : true,
14424     showToggleBtn : true,
14425     animate : true,
14426     emptyResultText: 'Empty',
14427     triggerText : 'Select',
14428     emptyTitle : '',
14429     
14430     // element that contains real text value.. (when hidden is used..)
14431     
14432     getAutoCreate : function()
14433     {   
14434         var cfg = false;
14435         //render
14436         /*
14437          * Render classic select for iso
14438          */
14439         
14440         if(Roo.isIOS && this.useNativeIOS){
14441             cfg = this.getAutoCreateNativeIOS();
14442             return cfg;
14443         }
14444         
14445         /*
14446          * Touch Devices
14447          */
14448         
14449         if(Roo.isTouch && this.mobileTouchView){
14450             cfg = this.getAutoCreateTouchView();
14451             return cfg;;
14452         }
14453         
14454         /*
14455          *  Normal ComboBox
14456          */
14457         if(!this.tickable){
14458             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
14459             return cfg;
14460         }
14461         
14462         /*
14463          *  ComboBox with tickable selections
14464          */
14465              
14466         var align = this.labelAlign || this.parentLabelAlign();
14467         
14468         cfg = {
14469             cls : 'form-group roo-combobox-tickable' //input-group
14470         };
14471         
14472         var btn_text_select = '';
14473         var btn_text_done = '';
14474         var btn_text_cancel = '';
14475         
14476         if (this.btn_text_show) {
14477             btn_text_select = 'Select';
14478             btn_text_done = 'Done';
14479             btn_text_cancel = 'Cancel'; 
14480         }
14481         
14482         var buttons = {
14483             tag : 'div',
14484             cls : 'tickable-buttons',
14485             cn : [
14486                 {
14487                     tag : 'button',
14488                     type : 'button',
14489                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
14490                     //html : this.triggerText
14491                     html: btn_text_select
14492                 },
14493                 {
14494                     tag : 'button',
14495                     type : 'button',
14496                     name : 'ok',
14497                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
14498                     //html : 'Done'
14499                     html: btn_text_done
14500                 },
14501                 {
14502                     tag : 'button',
14503                     type : 'button',
14504                     name : 'cancel',
14505                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
14506                     //html : 'Cancel'
14507                     html: btn_text_cancel
14508                 }
14509             ]
14510         };
14511         
14512         if(this.editable){
14513             buttons.cn.unshift({
14514                 tag: 'input',
14515                 cls: 'roo-select2-search-field-input'
14516             });
14517         }
14518         
14519         var _this = this;
14520         
14521         Roo.each(buttons.cn, function(c){
14522             if (_this.size) {
14523                 c.cls += ' btn-' + _this.size;
14524             }
14525
14526             if (_this.disabled) {
14527                 c.disabled = true;
14528             }
14529         });
14530         
14531         var box = {
14532             tag: 'div',
14533             style : 'display: contents',
14534             cn: [
14535                 {
14536                     tag: 'input',
14537                     type : 'hidden',
14538                     cls: 'form-hidden-field'
14539                 },
14540                 {
14541                     tag: 'ul',
14542                     cls: 'roo-select2-choices',
14543                     cn:[
14544                         {
14545                             tag: 'li',
14546                             cls: 'roo-select2-search-field',
14547                             cn: [
14548                                 buttons
14549                             ]
14550                         }
14551                     ]
14552                 }
14553             ]
14554         };
14555         
14556         var combobox = {
14557             cls: 'roo-select2-container input-group roo-select2-container-multi',
14558             cn: [
14559                 
14560                 box
14561 //                {
14562 //                    tag: 'ul',
14563 //                    cls: 'typeahead typeahead-long dropdown-menu',
14564 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
14565 //                }
14566             ]
14567         };
14568         
14569         if(this.hasFeedback && !this.allowBlank){
14570             
14571             var feedback = {
14572                 tag: 'span',
14573                 cls: 'glyphicon form-control-feedback'
14574             };
14575
14576             combobox.cn.push(feedback);
14577         }
14578         
14579         var indicator = {
14580             tag : 'i',
14581             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
14582             tooltip : 'This field is required'
14583         };
14584         if (Roo.bootstrap.version == 4) {
14585             indicator = {
14586                 tag : 'i',
14587                 style : 'display:none'
14588             };
14589         }
14590         if (align ==='left' && this.fieldLabel.length) {
14591             
14592             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
14593             
14594             cfg.cn = [
14595                 indicator,
14596                 {
14597                     tag: 'label',
14598                     'for' :  id,
14599                     cls : 'control-label col-form-label',
14600                     html : this.fieldLabel
14601
14602                 },
14603                 {
14604                     cls : "", 
14605                     cn: [
14606                         combobox
14607                     ]
14608                 }
14609
14610             ];
14611             
14612             var labelCfg = cfg.cn[1];
14613             var contentCfg = cfg.cn[2];
14614             
14615
14616             if(this.indicatorpos == 'right'){
14617                 
14618                 cfg.cn = [
14619                     {
14620                         tag: 'label',
14621                         'for' :  id,
14622                         cls : 'control-label col-form-label',
14623                         cn : [
14624                             {
14625                                 tag : 'span',
14626                                 html : this.fieldLabel
14627                             },
14628                             indicator
14629                         ]
14630                     },
14631                     {
14632                         cls : "",
14633                         cn: [
14634                             combobox
14635                         ]
14636                     }
14637
14638                 ];
14639                 
14640                 
14641                 
14642                 labelCfg = cfg.cn[0];
14643                 contentCfg = cfg.cn[1];
14644             
14645             }
14646             
14647             if(this.labelWidth > 12){
14648                 labelCfg.style = "width: " + this.labelWidth + 'px';
14649             }
14650             
14651             if(this.labelWidth < 13 && this.labelmd == 0){
14652                 this.labelmd = this.labelWidth;
14653             }
14654             
14655             if(this.labellg > 0){
14656                 labelCfg.cls += ' col-lg-' + this.labellg;
14657                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14658             }
14659             
14660             if(this.labelmd > 0){
14661                 labelCfg.cls += ' col-md-' + this.labelmd;
14662                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14663             }
14664             
14665             if(this.labelsm > 0){
14666                 labelCfg.cls += ' col-sm-' + this.labelsm;
14667                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14668             }
14669             
14670             if(this.labelxs > 0){
14671                 labelCfg.cls += ' col-xs-' + this.labelxs;
14672                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14673             }
14674                 
14675                 
14676         } else if ( this.fieldLabel.length) {
14677 //                Roo.log(" label");
14678                  cfg.cn = [
14679                    indicator,
14680                     {
14681                         tag: 'label',
14682                         //cls : 'input-group-addon',
14683                         html : this.fieldLabel
14684                     },
14685                     combobox
14686                 ];
14687                 
14688                 if(this.indicatorpos == 'right'){
14689                     cfg.cn = [
14690                         {
14691                             tag: 'label',
14692                             //cls : 'input-group-addon',
14693                             html : this.fieldLabel
14694                         },
14695                         indicator,
14696                         combobox
14697                     ];
14698                     
14699                 }
14700
14701         } else {
14702             
14703 //                Roo.log(" no label && no align");
14704                 cfg = combobox
14705                      
14706                 
14707         }
14708          
14709         var settings=this;
14710         ['xs','sm','md','lg'].map(function(size){
14711             if (settings[size]) {
14712                 cfg.cls += ' col-' + size + '-' + settings[size];
14713             }
14714         });
14715         
14716         return cfg;
14717         
14718     },
14719     
14720     _initEventsCalled : false,
14721     
14722     // private
14723     initEvents: function()
14724     {   
14725         if (this._initEventsCalled) { // as we call render... prevent looping...
14726             return;
14727         }
14728         this._initEventsCalled = true;
14729         
14730         if (!this.store) {
14731             throw "can not find store for combo";
14732         }
14733         
14734         this.indicator = this.indicatorEl();
14735         
14736         this.store = Roo.factory(this.store, Roo.data);
14737         this.store.parent = this;
14738         
14739         // if we are building from html. then this element is so complex, that we can not really
14740         // use the rendered HTML.
14741         // so we have to trash and replace the previous code.
14742         if (Roo.XComponent.build_from_html) {
14743             // remove this element....
14744             var e = this.el.dom, k=0;
14745             while (e ) { e = e.previousSibling;  ++k;}
14746
14747             this.el.remove();
14748             
14749             this.el=false;
14750             this.rendered = false;
14751             
14752             this.render(this.parent().getChildContainer(true), k);
14753         }
14754         
14755         if(Roo.isIOS && this.useNativeIOS){
14756             this.initIOSView();
14757             return;
14758         }
14759         
14760         /*
14761          * Touch Devices
14762          */
14763         
14764         if(Roo.isTouch && this.mobileTouchView){
14765             this.initTouchView();
14766             return;
14767         }
14768         
14769         if(this.tickable){
14770             this.initTickableEvents();
14771             return;
14772         }
14773         
14774         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
14775         
14776         if(this.hiddenName){
14777             
14778             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14779             
14780             this.hiddenField.dom.value =
14781                 this.hiddenValue !== undefined ? this.hiddenValue :
14782                 this.value !== undefined ? this.value : '';
14783
14784             // prevent input submission
14785             this.el.dom.removeAttribute('name');
14786             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14787              
14788              
14789         }
14790         //if(Roo.isGecko){
14791         //    this.el.dom.setAttribute('autocomplete', 'off');
14792         //}
14793         
14794         var cls = 'x-combo-list';
14795         
14796         //this.list = new Roo.Layer({
14797         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
14798         //});
14799         
14800         var _this = this;
14801         
14802         (function(){
14803             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
14804             _this.list.setWidth(lw);
14805         }).defer(100);
14806         
14807         this.list.on('mouseover', this.onViewOver, this);
14808         this.list.on('mousemove', this.onViewMove, this);
14809         this.list.on('scroll', this.onViewScroll, this);
14810         
14811         /*
14812         this.list.swallowEvent('mousewheel');
14813         this.assetHeight = 0;
14814
14815         if(this.title){
14816             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
14817             this.assetHeight += this.header.getHeight();
14818         }
14819
14820         this.innerList = this.list.createChild({cls:cls+'-inner'});
14821         this.innerList.on('mouseover', this.onViewOver, this);
14822         this.innerList.on('mousemove', this.onViewMove, this);
14823         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14824         
14825         if(this.allowBlank && !this.pageSize && !this.disableClear){
14826             this.footer = this.list.createChild({cls:cls+'-ft'});
14827             this.pageTb = new Roo.Toolbar(this.footer);
14828            
14829         }
14830         if(this.pageSize){
14831             this.footer = this.list.createChild({cls:cls+'-ft'});
14832             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
14833                     {pageSize: this.pageSize});
14834             
14835         }
14836         
14837         if (this.pageTb && this.allowBlank && !this.disableClear) {
14838             var _this = this;
14839             this.pageTb.add(new Roo.Toolbar.Fill(), {
14840                 cls: 'x-btn-icon x-btn-clear',
14841                 text: '&#160;',
14842                 handler: function()
14843                 {
14844                     _this.collapse();
14845                     _this.clearValue();
14846                     _this.onSelect(false, -1);
14847                 }
14848             });
14849         }
14850         if (this.footer) {
14851             this.assetHeight += this.footer.getHeight();
14852         }
14853         */
14854             
14855         if(!this.tpl){
14856             this.tpl = Roo.bootstrap.version == 4 ?
14857                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
14858                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
14859         }
14860
14861         this.view = new Roo.View(this.list, this.tpl, {
14862             singleSelect:true, store: this.store, selectedClass: this.selectedClass
14863         });
14864         //this.view.wrapEl.setDisplayed(false);
14865         this.view.on('click', this.onViewClick, this);
14866         
14867         
14868         this.store.on('beforeload', this.onBeforeLoad, this);
14869         this.store.on('load', this.onLoad, this);
14870         this.store.on('loadexception', this.onLoadException, this);
14871         /*
14872         if(this.resizable){
14873             this.resizer = new Roo.Resizable(this.list,  {
14874                pinned:true, handles:'se'
14875             });
14876             this.resizer.on('resize', function(r, w, h){
14877                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
14878                 this.listWidth = w;
14879                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
14880                 this.restrictHeight();
14881             }, this);
14882             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
14883         }
14884         */
14885         if(!this.editable){
14886             this.editable = true;
14887             this.setEditable(false);
14888         }
14889         
14890         /*
14891         
14892         if (typeof(this.events.add.listeners) != 'undefined') {
14893             
14894             this.addicon = this.wrap.createChild(
14895                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
14896        
14897             this.addicon.on('click', function(e) {
14898                 this.fireEvent('add', this);
14899             }, this);
14900         }
14901         if (typeof(this.events.edit.listeners) != 'undefined') {
14902             
14903             this.editicon = this.wrap.createChild(
14904                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
14905             if (this.addicon) {
14906                 this.editicon.setStyle('margin-left', '40px');
14907             }
14908             this.editicon.on('click', function(e) {
14909                 
14910                 // we fire even  if inothing is selected..
14911                 this.fireEvent('edit', this, this.lastData );
14912                 
14913             }, this);
14914         }
14915         */
14916         
14917         this.keyNav = new Roo.KeyNav(this.inputEl(), {
14918             "up" : function(e){
14919                 this.inKeyMode = true;
14920                 this.selectPrev();
14921             },
14922
14923             "down" : function(e){
14924                 if(!this.isExpanded()){
14925                     this.onTriggerClick();
14926                 }else{
14927                     this.inKeyMode = true;
14928                     this.selectNext();
14929                 }
14930             },
14931
14932             "enter" : function(e){
14933 //                this.onViewClick();
14934                 //return true;
14935                 this.collapse();
14936                 
14937                 if(this.fireEvent("specialkey", this, e)){
14938                     this.onViewClick(false);
14939                 }
14940                 
14941                 return true;
14942             },
14943
14944             "esc" : function(e){
14945                 this.collapse();
14946             },
14947
14948             "tab" : function(e){
14949                 this.collapse();
14950                 
14951                 if(this.fireEvent("specialkey", this, e)){
14952                     this.onViewClick(false);
14953                 }
14954                 
14955                 return true;
14956             },
14957
14958             scope : this,
14959
14960             doRelay : function(foo, bar, hname){
14961                 if(hname == 'down' || this.scope.isExpanded()){
14962                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14963                 }
14964                 return true;
14965             },
14966
14967             forceKeyDown: true
14968         });
14969         
14970         
14971         this.queryDelay = Math.max(this.queryDelay || 10,
14972                 this.mode == 'local' ? 10 : 250);
14973         
14974         
14975         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14976         
14977         if(this.typeAhead){
14978             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14979         }
14980         if(this.editable !== false){
14981             this.inputEl().on("keyup", this.onKeyUp, this);
14982         }
14983         if(this.forceSelection){
14984             this.inputEl().on('blur', this.doForce, this);
14985         }
14986         
14987         if(this.multiple){
14988             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14989             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14990         }
14991     },
14992     
14993     initTickableEvents: function()
14994     {   
14995         this.createList();
14996         
14997         if(this.hiddenName){
14998             
14999             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15000             
15001             this.hiddenField.dom.value =
15002                 this.hiddenValue !== undefined ? this.hiddenValue :
15003                 this.value !== undefined ? this.value : '';
15004
15005             // prevent input submission
15006             this.el.dom.removeAttribute('name');
15007             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15008              
15009              
15010         }
15011         
15012 //        this.list = this.el.select('ul.dropdown-menu',true).first();
15013         
15014         this.choices = this.el.select('ul.roo-select2-choices', true).first();
15015         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15016         if(this.triggerList){
15017             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15018         }
15019          
15020         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15021         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15022         
15023         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15024         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15025         
15026         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15027         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15028         
15029         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15030         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15031         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15032         
15033         this.okBtn.hide();
15034         this.cancelBtn.hide();
15035         
15036         var _this = this;
15037         
15038         (function(){
15039             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15040             _this.list.setWidth(lw);
15041         }).defer(100);
15042         
15043         this.list.on('mouseover', this.onViewOver, this);
15044         this.list.on('mousemove', this.onViewMove, this);
15045         
15046         this.list.on('scroll', this.onViewScroll, this);
15047         
15048         if(!this.tpl){
15049             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
15050                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15051         }
15052
15053         this.view = new Roo.View(this.list, this.tpl, {
15054             singleSelect:true,
15055             tickable:true,
15056             parent:this,
15057             store: this.store,
15058             selectedClass: this.selectedClass
15059         });
15060         
15061         //this.view.wrapEl.setDisplayed(false);
15062         this.view.on('click', this.onViewClick, this);
15063         
15064         
15065         
15066         this.store.on('beforeload', this.onBeforeLoad, this);
15067         this.store.on('load', this.onLoad, this);
15068         this.store.on('loadexception', this.onLoadException, this);
15069         
15070         if(this.editable){
15071             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15072                 "up" : function(e){
15073                     this.inKeyMode = true;
15074                     this.selectPrev();
15075                 },
15076
15077                 "down" : function(e){
15078                     this.inKeyMode = true;
15079                     this.selectNext();
15080                 },
15081
15082                 "enter" : function(e){
15083                     if(this.fireEvent("specialkey", this, e)){
15084                         this.onViewClick(false);
15085                     }
15086                     
15087                     return true;
15088                 },
15089
15090                 "esc" : function(e){
15091                     this.onTickableFooterButtonClick(e, false, false);
15092                 },
15093
15094                 "tab" : function(e){
15095                     this.fireEvent("specialkey", this, e);
15096                     
15097                     this.onTickableFooterButtonClick(e, false, false);
15098                     
15099                     return true;
15100                 },
15101
15102                 scope : this,
15103
15104                 doRelay : function(e, fn, key){
15105                     if(this.scope.isExpanded()){
15106                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15107                     }
15108                     return true;
15109                 },
15110
15111                 forceKeyDown: true
15112             });
15113         }
15114         
15115         this.queryDelay = Math.max(this.queryDelay || 10,
15116                 this.mode == 'local' ? 10 : 250);
15117         
15118         
15119         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15120         
15121         if(this.typeAhead){
15122             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15123         }
15124         
15125         if(this.editable !== false){
15126             this.tickableInputEl().on("keyup", this.onKeyUp, this);
15127         }
15128         
15129         this.indicator = this.indicatorEl();
15130         
15131         if(this.indicator){
15132             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15133             this.indicator.hide();
15134         }
15135         
15136     },
15137
15138     onDestroy : function(){
15139         if(this.view){
15140             this.view.setStore(null);
15141             this.view.el.removeAllListeners();
15142             this.view.el.remove();
15143             this.view.purgeListeners();
15144         }
15145         if(this.list){
15146             this.list.dom.innerHTML  = '';
15147         }
15148         
15149         if(this.store){
15150             this.store.un('beforeload', this.onBeforeLoad, this);
15151             this.store.un('load', this.onLoad, this);
15152             this.store.un('loadexception', this.onLoadException, this);
15153         }
15154         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15155     },
15156
15157     // private
15158     fireKey : function(e){
15159         if(e.isNavKeyPress() && !this.list.isVisible()){
15160             this.fireEvent("specialkey", this, e);
15161         }
15162     },
15163
15164     // private
15165     onResize: function(w, h){
15166 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
15167 //        
15168 //        if(typeof w != 'number'){
15169 //            // we do not handle it!?!?
15170 //            return;
15171 //        }
15172 //        var tw = this.trigger.getWidth();
15173 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
15174 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
15175 //        var x = w - tw;
15176 //        this.inputEl().setWidth( this.adjustWidth('input', x));
15177 //            
15178 //        //this.trigger.setStyle('left', x+'px');
15179 //        
15180 //        if(this.list && this.listWidth === undefined){
15181 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
15182 //            this.list.setWidth(lw);
15183 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15184 //        }
15185         
15186     
15187         
15188     },
15189
15190     /**
15191      * Allow or prevent the user from directly editing the field text.  If false is passed,
15192      * the user will only be able to select from the items defined in the dropdown list.  This method
15193      * is the runtime equivalent of setting the 'editable' config option at config time.
15194      * @param {Boolean} value True to allow the user to directly edit the field text
15195      */
15196     setEditable : function(value){
15197         if(value == this.editable){
15198             return;
15199         }
15200         this.editable = value;
15201         if(!value){
15202             this.inputEl().dom.setAttribute('readOnly', true);
15203             this.inputEl().on('mousedown', this.onTriggerClick,  this);
15204             this.inputEl().addClass('x-combo-noedit');
15205         }else{
15206             this.inputEl().dom.setAttribute('readOnly', false);
15207             this.inputEl().un('mousedown', this.onTriggerClick,  this);
15208             this.inputEl().removeClass('x-combo-noedit');
15209         }
15210     },
15211
15212     // private
15213     
15214     onBeforeLoad : function(combo,opts){
15215         if(!this.hasFocus){
15216             return;
15217         }
15218          if (!opts.add) {
15219             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
15220          }
15221         this.restrictHeight();
15222         this.selectedIndex = -1;
15223     },
15224
15225     // private
15226     onLoad : function(){
15227         
15228         this.hasQuery = false;
15229         
15230         if(!this.hasFocus){
15231             return;
15232         }
15233         
15234         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
15235             this.loading.hide();
15236         }
15237         
15238         if(this.store.getCount() > 0){
15239             
15240             this.expand();
15241             this.restrictHeight();
15242             if(this.lastQuery == this.allQuery){
15243                 if(this.editable && !this.tickable){
15244                     this.inputEl().dom.select();
15245                 }
15246                 
15247                 if(
15248                     !this.selectByValue(this.value, true) &&
15249                     this.autoFocus && 
15250                     (
15251                         !this.store.lastOptions ||
15252                         typeof(this.store.lastOptions.add) == 'undefined' || 
15253                         this.store.lastOptions.add != true
15254                     )
15255                 ){
15256                     this.select(0, true);
15257                 }
15258             }else{
15259                 if(this.autoFocus){
15260                     this.selectNext();
15261                 }
15262                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
15263                     this.taTask.delay(this.typeAheadDelay);
15264                 }
15265             }
15266         }else{
15267             this.onEmptyResults();
15268         }
15269         
15270         //this.el.focus();
15271     },
15272     // private
15273     onLoadException : function()
15274     {
15275         this.hasQuery = false;
15276         
15277         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
15278             this.loading.hide();
15279         }
15280         
15281         if(this.tickable && this.editable){
15282             return;
15283         }
15284         
15285         this.collapse();
15286         // only causes errors at present
15287         //Roo.log(this.store.reader.jsonData);
15288         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
15289             // fixme
15290             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
15291         //}
15292         
15293         
15294     },
15295     // private
15296     onTypeAhead : function(){
15297         if(this.store.getCount() > 0){
15298             var r = this.store.getAt(0);
15299             var newValue = r.data[this.displayField];
15300             var len = newValue.length;
15301             var selStart = this.getRawValue().length;
15302             
15303             if(selStart != len){
15304                 this.setRawValue(newValue);
15305                 this.selectText(selStart, newValue.length);
15306             }
15307         }
15308     },
15309
15310     // private
15311     onSelect : function(record, index){
15312         
15313         if(this.fireEvent('beforeselect', this, record, index) !== false){
15314         
15315             this.setFromData(index > -1 ? record.data : false);
15316             
15317             this.collapse();
15318             this.fireEvent('select', this, record, index);
15319         }
15320     },
15321
15322     /**
15323      * Returns the currently selected field value or empty string if no value is set.
15324      * @return {String} value The selected value
15325      */
15326     getValue : function()
15327     {
15328         if(Roo.isIOS && this.useNativeIOS){
15329             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
15330         }
15331         
15332         if(this.multiple){
15333             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
15334         }
15335         
15336         if(this.valueField){
15337             return typeof this.value != 'undefined' ? this.value : '';
15338         }else{
15339             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
15340         }
15341     },
15342     
15343     getRawValue : function()
15344     {
15345         if(Roo.isIOS && this.useNativeIOS){
15346             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
15347         }
15348         
15349         var v = this.inputEl().getValue();
15350         
15351         return v;
15352     },
15353
15354     /**
15355      * Clears any text/value currently set in the field
15356      */
15357     clearValue : function(){
15358         
15359         if(this.hiddenField){
15360             this.hiddenField.dom.value = '';
15361         }
15362         this.value = '';
15363         this.setRawValue('');
15364         this.lastSelectionText = '';
15365         this.lastData = false;
15366         
15367         var close = this.closeTriggerEl();
15368         
15369         if(close){
15370             close.hide();
15371         }
15372         
15373         this.validate();
15374         
15375     },
15376
15377     /**
15378      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
15379      * will be displayed in the field.  If the value does not match the data value of an existing item,
15380      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
15381      * Otherwise the field will be blank (although the value will still be set).
15382      * @param {String} value The value to match
15383      */
15384     setValue : function(v)
15385     {
15386         if(Roo.isIOS && this.useNativeIOS){
15387             this.setIOSValue(v);
15388             return;
15389         }
15390         
15391         if(this.multiple){
15392             this.syncValue();
15393             return;
15394         }
15395         
15396         var text = v;
15397         if(this.valueField){
15398             var r = this.findRecord(this.valueField, v);
15399             if(r){
15400                 text = r.data[this.displayField];
15401             }else if(this.valueNotFoundText !== undefined){
15402                 text = this.valueNotFoundText;
15403             }
15404         }
15405         this.lastSelectionText = text;
15406         if(this.hiddenField){
15407             this.hiddenField.dom.value = v;
15408         }
15409         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
15410         this.value = v;
15411         
15412         var close = this.closeTriggerEl();
15413         
15414         if(close){
15415             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
15416         }
15417         
15418         this.validate();
15419     },
15420     /**
15421      * @property {Object} the last set data for the element
15422      */
15423     
15424     lastData : false,
15425     /**
15426      * Sets the value of the field based on a object which is related to the record format for the store.
15427      * @param {Object} value the value to set as. or false on reset?
15428      */
15429     setFromData : function(o){
15430         
15431         if(this.multiple){
15432             this.addItem(o);
15433             return;
15434         }
15435             
15436         var dv = ''; // display value
15437         var vv = ''; // value value..
15438         this.lastData = o;
15439         if (this.displayField) {
15440             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
15441         } else {
15442             // this is an error condition!!!
15443             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
15444         }
15445         
15446         if(this.valueField){
15447             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
15448         }
15449         
15450         var close = this.closeTriggerEl();
15451         
15452         if(close){
15453             if(dv.length || vv * 1 > 0){
15454                 close.show() ;
15455                 this.blockFocus=true;
15456             } else {
15457                 close.hide();
15458             }             
15459         }
15460         
15461         if(this.hiddenField){
15462             this.hiddenField.dom.value = vv;
15463             
15464             this.lastSelectionText = dv;
15465             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
15466             this.value = vv;
15467             return;
15468         }
15469         // no hidden field.. - we store the value in 'value', but still display
15470         // display field!!!!
15471         this.lastSelectionText = dv;
15472         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
15473         this.value = vv;
15474         
15475         
15476         
15477     },
15478     // private
15479     reset : function(){
15480         // overridden so that last data is reset..
15481         
15482         if(this.multiple){
15483             this.clearItem();
15484             return;
15485         }
15486         
15487         this.setValue(this.originalValue);
15488         //this.clearInvalid();
15489         this.lastData = false;
15490         if (this.view) {
15491             this.view.clearSelections();
15492         }
15493         
15494         this.validate();
15495     },
15496     // private
15497     findRecord : function(prop, value){
15498         var record;
15499         if(this.store.getCount() > 0){
15500             this.store.each(function(r){
15501                 if(r.data[prop] == value){
15502                     record = r;
15503                     return false;
15504                 }
15505                 return true;
15506             });
15507         }
15508         return record;
15509     },
15510     
15511     getName: function()
15512     {
15513         // returns hidden if it's set..
15514         if (!this.rendered) {return ''};
15515         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
15516         
15517     },
15518     // private
15519     onViewMove : function(e, t){
15520         this.inKeyMode = false;
15521     },
15522
15523     // private
15524     onViewOver : function(e, t){
15525         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
15526             return;
15527         }
15528         var item = this.view.findItemFromChild(t);
15529         
15530         if(item){
15531             var index = this.view.indexOf(item);
15532             this.select(index, false);
15533         }
15534     },
15535
15536     // private
15537     onViewClick : function(view, doFocus, el, e)
15538     {
15539         var index = this.view.getSelectedIndexes()[0];
15540         
15541         var r = this.store.getAt(index);
15542         
15543         if(this.tickable){
15544             
15545             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
15546                 return;
15547             }
15548             
15549             var rm = false;
15550             var _this = this;
15551             
15552             Roo.each(this.tickItems, function(v,k){
15553                 
15554                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
15555                     Roo.log(v);
15556                     _this.tickItems.splice(k, 1);
15557                     
15558                     if(typeof(e) == 'undefined' && view == false){
15559                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
15560                     }
15561                     
15562                     rm = true;
15563                     return;
15564                 }
15565             });
15566             
15567             if(rm){
15568                 return;
15569             }
15570             
15571             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
15572                 this.tickItems.push(r.data);
15573             }
15574             
15575             if(typeof(e) == 'undefined' && view == false){
15576                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
15577             }
15578                     
15579             return;
15580         }
15581         
15582         if(r){
15583             this.onSelect(r, index);
15584         }
15585         if(doFocus !== false && !this.blockFocus){
15586             this.inputEl().focus();
15587         }
15588     },
15589
15590     // private
15591     restrictHeight : function(){
15592         //this.innerList.dom.style.height = '';
15593         //var inner = this.innerList.dom;
15594         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
15595         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
15596         //this.list.beginUpdate();
15597         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
15598         this.list.alignTo(this.inputEl(), this.listAlign);
15599         this.list.alignTo(this.inputEl(), this.listAlign);
15600         //this.list.endUpdate();
15601     },
15602
15603     // private
15604     onEmptyResults : function(){
15605         
15606         if(this.tickable && this.editable){
15607             this.hasFocus = false;
15608             this.restrictHeight();
15609             return;
15610         }
15611         
15612         this.collapse();
15613     },
15614
15615     /**
15616      * Returns true if the dropdown list is expanded, else false.
15617      */
15618     isExpanded : function(){
15619         return this.list.isVisible();
15620     },
15621
15622     /**
15623      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
15624      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
15625      * @param {String} value The data value of the item to select
15626      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
15627      * selected item if it is not currently in view (defaults to true)
15628      * @return {Boolean} True if the value matched an item in the list, else false
15629      */
15630     selectByValue : function(v, scrollIntoView){
15631         if(v !== undefined && v !== null){
15632             var r = this.findRecord(this.valueField || this.displayField, v);
15633             if(r){
15634                 this.select(this.store.indexOf(r), scrollIntoView);
15635                 return true;
15636             }
15637         }
15638         return false;
15639     },
15640
15641     /**
15642      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
15643      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
15644      * @param {Number} index The zero-based index of the list item to select
15645      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
15646      * selected item if it is not currently in view (defaults to true)
15647      */
15648     select : function(index, scrollIntoView){
15649         this.selectedIndex = index;
15650         this.view.select(index);
15651         if(scrollIntoView !== false){
15652             var el = this.view.getNode(index);
15653             /*
15654              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
15655              */
15656             if(el){
15657                 this.list.scrollChildIntoView(el, false);
15658             }
15659         }
15660     },
15661
15662     // private
15663     selectNext : function(){
15664         var ct = this.store.getCount();
15665         if(ct > 0){
15666             if(this.selectedIndex == -1){
15667                 this.select(0);
15668             }else if(this.selectedIndex < ct-1){
15669                 this.select(this.selectedIndex+1);
15670             }
15671         }
15672     },
15673
15674     // private
15675     selectPrev : function(){
15676         var ct = this.store.getCount();
15677         if(ct > 0){
15678             if(this.selectedIndex == -1){
15679                 this.select(0);
15680             }else if(this.selectedIndex != 0){
15681                 this.select(this.selectedIndex-1);
15682             }
15683         }
15684     },
15685
15686     // private
15687     onKeyUp : function(e){
15688         if(this.editable !== false && !e.isSpecialKey()){
15689             this.lastKey = e.getKey();
15690             this.dqTask.delay(this.queryDelay);
15691         }
15692     },
15693
15694     // private
15695     validateBlur : function(){
15696         return !this.list || !this.list.isVisible();   
15697     },
15698
15699     // private
15700     initQuery : function(){
15701         
15702         var v = this.getRawValue();
15703         
15704         if(this.tickable && this.editable){
15705             v = this.tickableInputEl().getValue();
15706         }
15707         
15708         this.doQuery(v);
15709     },
15710
15711     // private
15712     doForce : function(){
15713         if(this.inputEl().dom.value.length > 0){
15714             this.inputEl().dom.value =
15715                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
15716              
15717         }
15718     },
15719
15720     /**
15721      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
15722      * query allowing the query action to be canceled if needed.
15723      * @param {String} query The SQL query to execute
15724      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
15725      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
15726      * saved in the current store (defaults to false)
15727      */
15728     doQuery : function(q, forceAll){
15729         
15730         if(q === undefined || q === null){
15731             q = '';
15732         }
15733         var qe = {
15734             query: q,
15735             forceAll: forceAll,
15736             combo: this,
15737             cancel:false
15738         };
15739         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
15740             return false;
15741         }
15742         q = qe.query;
15743         
15744         forceAll = qe.forceAll;
15745         if(forceAll === true || (q.length >= this.minChars)){
15746             
15747             this.hasQuery = true;
15748             
15749             if(this.lastQuery != q || this.alwaysQuery){
15750                 this.lastQuery = q;
15751                 if(this.mode == 'local'){
15752                     this.selectedIndex = -1;
15753                     if(forceAll){
15754                         this.store.clearFilter();
15755                     }else{
15756                         
15757                         if(this.specialFilter){
15758                             this.fireEvent('specialfilter', this);
15759                             this.onLoad();
15760                             return;
15761                         }
15762                         
15763                         this.store.filter(this.displayField, q);
15764                     }
15765                     
15766                     this.store.fireEvent("datachanged", this.store);
15767                     
15768                     this.onLoad();
15769                     
15770                     
15771                 }else{
15772                     
15773                     this.store.baseParams[this.queryParam] = q;
15774                     
15775                     var options = {params : this.getParams(q)};
15776                     
15777                     if(this.loadNext){
15778                         options.add = true;
15779                         options.params.start = this.page * this.pageSize;
15780                     }
15781                     
15782                     this.store.load(options);
15783                     
15784                     /*
15785                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
15786                      *  we should expand the list on onLoad
15787                      *  so command out it
15788                      */
15789 //                    this.expand();
15790                 }
15791             }else{
15792                 this.selectedIndex = -1;
15793                 this.onLoad();   
15794             }
15795         }
15796         
15797         this.loadNext = false;
15798     },
15799     
15800     // private
15801     getParams : function(q){
15802         var p = {};
15803         //p[this.queryParam] = q;
15804         
15805         if(this.pageSize){
15806             p.start = 0;
15807             p.limit = this.pageSize;
15808         }
15809         return p;
15810     },
15811
15812     /**
15813      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
15814      */
15815     collapse : function(){
15816         if(!this.isExpanded()){
15817             return;
15818         }
15819         
15820         this.list.hide();
15821         
15822         this.hasFocus = false;
15823         
15824         if(this.tickable){
15825             this.okBtn.hide();
15826             this.cancelBtn.hide();
15827             this.trigger.show();
15828             
15829             if(this.editable){
15830                 this.tickableInputEl().dom.value = '';
15831                 this.tickableInputEl().blur();
15832             }
15833             
15834         }
15835         
15836         Roo.get(document).un('mousedown', this.collapseIf, this);
15837         Roo.get(document).un('mousewheel', this.collapseIf, this);
15838         if (!this.editable) {
15839             Roo.get(document).un('keydown', this.listKeyPress, this);
15840         }
15841         this.fireEvent('collapse', this);
15842         
15843         this.validate();
15844     },
15845
15846     // private
15847     collapseIf : function(e){
15848         var in_combo  = e.within(this.el);
15849         var in_list =  e.within(this.list);
15850         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
15851         
15852         if (in_combo || in_list || is_list) {
15853             //e.stopPropagation();
15854             return;
15855         }
15856         
15857         if(this.tickable){
15858             this.onTickableFooterButtonClick(e, false, false);
15859         }
15860
15861         this.collapse();
15862         
15863     },
15864
15865     /**
15866      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
15867      */
15868     expand : function(){
15869        
15870         if(this.isExpanded() || !this.hasFocus){
15871             return;
15872         }
15873         
15874         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
15875         this.list.setWidth(lw);
15876         
15877         Roo.log('expand');
15878         
15879         this.list.show();
15880         
15881         this.restrictHeight();
15882         
15883         if(this.tickable){
15884             
15885             this.tickItems = Roo.apply([], this.item);
15886             
15887             this.okBtn.show();
15888             this.cancelBtn.show();
15889             this.trigger.hide();
15890             
15891             if(this.editable){
15892                 this.tickableInputEl().focus();
15893             }
15894             
15895         }
15896         
15897         Roo.get(document).on('mousedown', this.collapseIf, this);
15898         Roo.get(document).on('mousewheel', this.collapseIf, this);
15899         if (!this.editable) {
15900             Roo.get(document).on('keydown', this.listKeyPress, this);
15901         }
15902         
15903         this.fireEvent('expand', this);
15904     },
15905
15906     // private
15907     // Implements the default empty TriggerField.onTriggerClick function
15908     onTriggerClick : function(e)
15909     {
15910         Roo.log('trigger click');
15911         
15912         if(this.disabled || !this.triggerList){
15913             return;
15914         }
15915         
15916         this.page = 0;
15917         this.loadNext = false;
15918         
15919         if(this.isExpanded()){
15920             this.collapse();
15921             if (!this.blockFocus) {
15922                 this.inputEl().focus();
15923             }
15924             
15925         }else {
15926             this.hasFocus = true;
15927             if(this.triggerAction == 'all') {
15928                 this.doQuery(this.allQuery, true);
15929             } else {
15930                 this.doQuery(this.getRawValue());
15931             }
15932             if (!this.blockFocus) {
15933                 this.inputEl().focus();
15934             }
15935         }
15936     },
15937     
15938     onTickableTriggerClick : function(e)
15939     {
15940         if(this.disabled){
15941             return;
15942         }
15943         
15944         this.page = 0;
15945         this.loadNext = false;
15946         this.hasFocus = true;
15947         
15948         if(this.triggerAction == 'all') {
15949             this.doQuery(this.allQuery, true);
15950         } else {
15951             this.doQuery(this.getRawValue());
15952         }
15953     },
15954     
15955     onSearchFieldClick : function(e)
15956     {
15957         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
15958             this.onTickableFooterButtonClick(e, false, false);
15959             return;
15960         }
15961         
15962         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
15963             return;
15964         }
15965         
15966         this.page = 0;
15967         this.loadNext = false;
15968         this.hasFocus = true;
15969         
15970         if(this.triggerAction == 'all') {
15971             this.doQuery(this.allQuery, true);
15972         } else {
15973             this.doQuery(this.getRawValue());
15974         }
15975     },
15976     
15977     listKeyPress : function(e)
15978     {
15979         //Roo.log('listkeypress');
15980         // scroll to first matching element based on key pres..
15981         if (e.isSpecialKey()) {
15982             return false;
15983         }
15984         var k = String.fromCharCode(e.getKey()).toUpperCase();
15985         //Roo.log(k);
15986         var match  = false;
15987         var csel = this.view.getSelectedNodes();
15988         var cselitem = false;
15989         if (csel.length) {
15990             var ix = this.view.indexOf(csel[0]);
15991             cselitem  = this.store.getAt(ix);
15992             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
15993                 cselitem = false;
15994             }
15995             
15996         }
15997         
15998         this.store.each(function(v) { 
15999             if (cselitem) {
16000                 // start at existing selection.
16001                 if (cselitem.id == v.id) {
16002                     cselitem = false;
16003                 }
16004                 return true;
16005             }
16006                 
16007             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16008                 match = this.store.indexOf(v);
16009                 return false;
16010             }
16011             return true;
16012         }, this);
16013         
16014         if (match === false) {
16015             return true; // no more action?
16016         }
16017         // scroll to?
16018         this.view.select(match);
16019         var sn = Roo.get(this.view.getSelectedNodes()[0]);
16020         sn.scrollIntoView(sn.dom.parentNode, false);
16021     },
16022     
16023     onViewScroll : function(e, t){
16024         
16025         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){
16026             return;
16027         }
16028         
16029         this.hasQuery = true;
16030         
16031         this.loading = this.list.select('.loading', true).first();
16032         
16033         if(this.loading === null){
16034             this.list.createChild({
16035                 tag: 'div',
16036                 cls: 'loading roo-select2-more-results roo-select2-active',
16037                 html: 'Loading more results...'
16038             });
16039             
16040             this.loading = this.list.select('.loading', true).first();
16041             
16042             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16043             
16044             this.loading.hide();
16045         }
16046         
16047         this.loading.show();
16048         
16049         var _combo = this;
16050         
16051         this.page++;
16052         this.loadNext = true;
16053         
16054         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16055         
16056         return;
16057     },
16058     
16059     addItem : function(o)
16060     {   
16061         var dv = ''; // display value
16062         
16063         if (this.displayField) {
16064             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16065         } else {
16066             // this is an error condition!!!
16067             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16068         }
16069         
16070         if(!dv.length){
16071             return;
16072         }
16073         
16074         var choice = this.choices.createChild({
16075             tag: 'li',
16076             cls: 'roo-select2-search-choice',
16077             cn: [
16078                 {
16079                     tag: 'div',
16080                     html: dv
16081                 },
16082                 {
16083                     tag: 'a',
16084                     href: '#',
16085                     cls: 'roo-select2-search-choice-close fa fa-times',
16086                     tabindex: '-1'
16087                 }
16088             ]
16089             
16090         }, this.searchField);
16091         
16092         var close = choice.select('a.roo-select2-search-choice-close', true).first();
16093         
16094         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16095         
16096         this.item.push(o);
16097         
16098         this.lastData = o;
16099         
16100         this.syncValue();
16101         
16102         this.inputEl().dom.value = '';
16103         
16104         this.validate();
16105     },
16106     
16107     onRemoveItem : function(e, _self, o)
16108     {
16109         e.preventDefault();
16110         
16111         this.lastItem = Roo.apply([], this.item);
16112         
16113         var index = this.item.indexOf(o.data) * 1;
16114         
16115         if( index < 0){
16116             Roo.log('not this item?!');
16117             return;
16118         }
16119         
16120         this.item.splice(index, 1);
16121         o.item.remove();
16122         
16123         this.syncValue();
16124         
16125         this.fireEvent('remove', this, e);
16126         
16127         this.validate();
16128         
16129     },
16130     
16131     syncValue : function()
16132     {
16133         if(!this.item.length){
16134             this.clearValue();
16135             return;
16136         }
16137             
16138         var value = [];
16139         var _this = this;
16140         Roo.each(this.item, function(i){
16141             if(_this.valueField){
16142                 value.push(i[_this.valueField]);
16143                 return;
16144             }
16145
16146             value.push(i);
16147         });
16148
16149         this.value = value.join(',');
16150
16151         if(this.hiddenField){
16152             this.hiddenField.dom.value = this.value;
16153         }
16154         
16155         this.store.fireEvent("datachanged", this.store);
16156         
16157         this.validate();
16158     },
16159     
16160     clearItem : function()
16161     {
16162         if(!this.multiple){
16163             return;
16164         }
16165         
16166         this.item = [];
16167         
16168         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
16169            c.remove();
16170         });
16171         
16172         this.syncValue();
16173         
16174         this.validate();
16175         
16176         if(this.tickable && !Roo.isTouch){
16177             this.view.refresh();
16178         }
16179     },
16180     
16181     inputEl: function ()
16182     {
16183         if(Roo.isIOS && this.useNativeIOS){
16184             return this.el.select('select.roo-ios-select', true).first();
16185         }
16186         
16187         if(Roo.isTouch && this.mobileTouchView){
16188             return this.el.select('input.form-control',true).first();
16189         }
16190         
16191         if(this.tickable){
16192             return this.searchField;
16193         }
16194         
16195         return this.el.select('input.form-control',true).first();
16196     },
16197     
16198     onTickableFooterButtonClick : function(e, btn, el)
16199     {
16200         e.preventDefault();
16201         
16202         this.lastItem = Roo.apply([], this.item);
16203         
16204         if(btn && btn.name == 'cancel'){
16205             this.tickItems = Roo.apply([], this.item);
16206             this.collapse();
16207             return;
16208         }
16209         
16210         this.clearItem();
16211         
16212         var _this = this;
16213         
16214         Roo.each(this.tickItems, function(o){
16215             _this.addItem(o);
16216         });
16217         
16218         this.collapse();
16219         
16220     },
16221     
16222     validate : function()
16223     {
16224         if(this.getVisibilityEl().hasClass('hidden')){
16225             return true;
16226         }
16227         
16228         var v = this.getRawValue();
16229         
16230         if(this.multiple){
16231             v = this.getValue();
16232         }
16233         
16234         if(this.disabled || this.allowBlank || v.length){
16235             this.markValid();
16236             return true;
16237         }
16238         
16239         this.markInvalid();
16240         return false;
16241     },
16242     
16243     tickableInputEl : function()
16244     {
16245         if(!this.tickable || !this.editable){
16246             return this.inputEl();
16247         }
16248         
16249         return this.inputEl().select('.roo-select2-search-field-input', true).first();
16250     },
16251     
16252     
16253     getAutoCreateTouchView : function()
16254     {
16255         var id = Roo.id();
16256         
16257         var cfg = {
16258             cls: 'form-group' //input-group
16259         };
16260         
16261         var input =  {
16262             tag: 'input',
16263             id : id,
16264             type : this.inputType,
16265             cls : 'form-control x-combo-noedit',
16266             autocomplete: 'new-password',
16267             placeholder : this.placeholder || '',
16268             readonly : true
16269         };
16270         
16271         if (this.name) {
16272             input.name = this.name;
16273         }
16274         
16275         if (this.size) {
16276             input.cls += ' input-' + this.size;
16277         }
16278         
16279         if (this.disabled) {
16280             input.disabled = true;
16281         }
16282         
16283         var inputblock = {
16284             cls : '',
16285             cn : [
16286                 input
16287             ]
16288         };
16289         
16290         if(this.before){
16291             inputblock.cls += ' input-group';
16292             
16293             inputblock.cn.unshift({
16294                 tag :'span',
16295                 cls : 'input-group-addon input-group-prepend input-group-text',
16296                 html : this.before
16297             });
16298         }
16299         
16300         if(this.removable && !this.multiple){
16301             inputblock.cls += ' roo-removable';
16302             
16303             inputblock.cn.push({
16304                 tag: 'button',
16305                 html : 'x',
16306                 cls : 'roo-combo-removable-btn close'
16307             });
16308         }
16309
16310         if(this.hasFeedback && !this.allowBlank){
16311             
16312             inputblock.cls += ' has-feedback';
16313             
16314             inputblock.cn.push({
16315                 tag: 'span',
16316                 cls: 'glyphicon form-control-feedback'
16317             });
16318             
16319         }
16320         
16321         if (this.after) {
16322             
16323             inputblock.cls += (this.before) ? '' : ' input-group';
16324             
16325             inputblock.cn.push({
16326                 tag :'span',
16327                 cls : 'input-group-addon input-group-append input-group-text',
16328                 html : this.after
16329             });
16330         }
16331
16332         
16333         var ibwrap = inputblock;
16334         
16335         if(this.multiple){
16336             ibwrap = {
16337                 tag: 'ul',
16338                 cls: 'roo-select2-choices',
16339                 cn:[
16340                     {
16341                         tag: 'li',
16342                         cls: 'roo-select2-search-field',
16343                         cn: [
16344
16345                             inputblock
16346                         ]
16347                     }
16348                 ]
16349             };
16350         
16351             
16352         }
16353         
16354         var combobox = {
16355             cls: 'roo-select2-container input-group roo-touchview-combobox ',
16356             cn: [
16357                 {
16358                     tag: 'input',
16359                     type : 'hidden',
16360                     cls: 'form-hidden-field'
16361                 },
16362                 ibwrap
16363             ]
16364         };
16365         
16366         if(!this.multiple && this.showToggleBtn){
16367             
16368             var caret = {
16369                 cls: 'caret'
16370             };
16371             
16372             if (this.caret != false) {
16373                 caret = {
16374                      tag: 'i',
16375                      cls: 'fa fa-' + this.caret
16376                 };
16377                 
16378             }
16379             
16380             combobox.cn.push({
16381                 tag :'span',
16382                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
16383                 cn : [
16384                     Roo.bootstrap.version == 3 ? caret : '',
16385                     {
16386                         tag: 'span',
16387                         cls: 'combobox-clear',
16388                         cn  : [
16389                             {
16390                                 tag : 'i',
16391                                 cls: 'icon-remove'
16392                             }
16393                         ]
16394                     }
16395                 ]
16396
16397             })
16398         }
16399         
16400         if(this.multiple){
16401             combobox.cls += ' roo-select2-container-multi';
16402         }
16403         
16404         var align = this.labelAlign || this.parentLabelAlign();
16405         
16406         if (align ==='left' && this.fieldLabel.length) {
16407
16408             cfg.cn = [
16409                 {
16410                    tag : 'i',
16411                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
16412                    tooltip : 'This field is required'
16413                 },
16414                 {
16415                     tag: 'label',
16416                     cls : 'control-label col-form-label',
16417                     html : this.fieldLabel
16418
16419                 },
16420                 {
16421                     cls : '', 
16422                     cn: [
16423                         combobox
16424                     ]
16425                 }
16426             ];
16427             
16428             var labelCfg = cfg.cn[1];
16429             var contentCfg = cfg.cn[2];
16430             
16431
16432             if(this.indicatorpos == 'right'){
16433                 cfg.cn = [
16434                     {
16435                         tag: 'label',
16436                         'for' :  id,
16437                         cls : 'control-label col-form-label',
16438                         cn : [
16439                             {
16440                                 tag : 'span',
16441                                 html : this.fieldLabel
16442                             },
16443                             {
16444                                 tag : 'i',
16445                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
16446                                 tooltip : 'This field is required'
16447                             }
16448                         ]
16449                     },
16450                     {
16451                         cls : "",
16452                         cn: [
16453                             combobox
16454                         ]
16455                     }
16456
16457                 ];
16458                 
16459                 labelCfg = cfg.cn[0];
16460                 contentCfg = cfg.cn[1];
16461             }
16462             
16463            
16464             
16465             if(this.labelWidth > 12){
16466                 labelCfg.style = "width: " + this.labelWidth + 'px';
16467             }
16468             
16469             if(this.labelWidth < 13 && this.labelmd == 0){
16470                 this.labelmd = this.labelWidth;
16471             }
16472             
16473             if(this.labellg > 0){
16474                 labelCfg.cls += ' col-lg-' + this.labellg;
16475                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
16476             }
16477             
16478             if(this.labelmd > 0){
16479                 labelCfg.cls += ' col-md-' + this.labelmd;
16480                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
16481             }
16482             
16483             if(this.labelsm > 0){
16484                 labelCfg.cls += ' col-sm-' + this.labelsm;
16485                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
16486             }
16487             
16488             if(this.labelxs > 0){
16489                 labelCfg.cls += ' col-xs-' + this.labelxs;
16490                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
16491             }
16492                 
16493                 
16494         } else if ( this.fieldLabel.length) {
16495             cfg.cn = [
16496                 {
16497                    tag : 'i',
16498                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
16499                    tooltip : 'This field is required'
16500                 },
16501                 {
16502                     tag: 'label',
16503                     cls : 'control-label',
16504                     html : this.fieldLabel
16505
16506                 },
16507                 {
16508                     cls : '', 
16509                     cn: [
16510                         combobox
16511                     ]
16512                 }
16513             ];
16514             
16515             if(this.indicatorpos == 'right'){
16516                 cfg.cn = [
16517                     {
16518                         tag: 'label',
16519                         cls : 'control-label',
16520                         html : this.fieldLabel,
16521                         cn : [
16522                             {
16523                                tag : 'i',
16524                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
16525                                tooltip : 'This field is required'
16526                             }
16527                         ]
16528                     },
16529                     {
16530                         cls : '', 
16531                         cn: [
16532                             combobox
16533                         ]
16534                     }
16535                 ];
16536             }
16537         } else {
16538             cfg.cn = combobox;    
16539         }
16540         
16541         
16542         var settings = this;
16543         
16544         ['xs','sm','md','lg'].map(function(size){
16545             if (settings[size]) {
16546                 cfg.cls += ' col-' + size + '-' + settings[size];
16547             }
16548         });
16549         
16550         return cfg;
16551     },
16552     
16553     initTouchView : function()
16554     {
16555         this.renderTouchView();
16556         
16557         this.touchViewEl.on('scroll', function(){
16558             this.el.dom.scrollTop = 0;
16559         }, this);
16560         
16561         this.originalValue = this.getValue();
16562         
16563         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
16564         
16565         this.inputEl().on("click", this.showTouchView, this);
16566         if (this.triggerEl) {
16567             this.triggerEl.on("click", this.showTouchView, this);
16568         }
16569         
16570         
16571         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
16572         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
16573         
16574         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
16575         
16576         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
16577         this.store.on('load', this.onTouchViewLoad, this);
16578         this.store.on('loadexception', this.onTouchViewLoadException, this);
16579         
16580         if(this.hiddenName){
16581             
16582             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16583             
16584             this.hiddenField.dom.value =
16585                 this.hiddenValue !== undefined ? this.hiddenValue :
16586                 this.value !== undefined ? this.value : '';
16587         
16588             this.el.dom.removeAttribute('name');
16589             this.hiddenField.dom.setAttribute('name', this.hiddenName);
16590         }
16591         
16592         if(this.multiple){
16593             this.choices = this.el.select('ul.roo-select2-choices', true).first();
16594             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16595         }
16596         
16597         if(this.removable && !this.multiple){
16598             var close = this.closeTriggerEl();
16599             if(close){
16600                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
16601                 close.on('click', this.removeBtnClick, this, close);
16602             }
16603         }
16604         /*
16605          * fix the bug in Safari iOS8
16606          */
16607         this.inputEl().on("focus", function(e){
16608             document.activeElement.blur();
16609         }, this);
16610         
16611         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
16612         
16613         return;
16614         
16615         
16616     },
16617     
16618     renderTouchView : function()
16619     {
16620         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
16621         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16622         
16623         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
16624         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16625         
16626         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
16627         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16628         this.touchViewBodyEl.setStyle('overflow', 'auto');
16629         
16630         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
16631         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16632         
16633         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
16634         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16635         
16636     },
16637     
16638     showTouchView : function()
16639     {
16640         if(this.disabled){
16641             return;
16642         }
16643         
16644         this.touchViewHeaderEl.hide();
16645
16646         if(this.modalTitle.length){
16647             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
16648             this.touchViewHeaderEl.show();
16649         }
16650
16651         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
16652         this.touchViewEl.show();
16653
16654         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
16655         
16656         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
16657         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
16658
16659         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
16660
16661         if(this.modalTitle.length){
16662             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
16663         }
16664         
16665         this.touchViewBodyEl.setHeight(bodyHeight);
16666
16667         if(this.animate){
16668             var _this = this;
16669             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
16670         }else{
16671             this.touchViewEl.addClass('in');
16672         }
16673         
16674         if(this._touchViewMask){
16675             Roo.get(document.body).addClass("x-body-masked");
16676             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
16677             this._touchViewMask.setStyle('z-index', 10000);
16678             this._touchViewMask.addClass('show');
16679         }
16680         
16681         this.doTouchViewQuery();
16682         
16683     },
16684     
16685     hideTouchView : function()
16686     {
16687         this.touchViewEl.removeClass('in');
16688
16689         if(this.animate){
16690             var _this = this;
16691             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
16692         }else{
16693             this.touchViewEl.setStyle('display', 'none');
16694         }
16695         
16696         if(this._touchViewMask){
16697             this._touchViewMask.removeClass('show');
16698             Roo.get(document.body).removeClass("x-body-masked");
16699         }
16700     },
16701     
16702     setTouchViewValue : function()
16703     {
16704         if(this.multiple){
16705             this.clearItem();
16706         
16707             var _this = this;
16708
16709             Roo.each(this.tickItems, function(o){
16710                 this.addItem(o);
16711             }, this);
16712         }
16713         
16714         this.hideTouchView();
16715     },
16716     
16717     doTouchViewQuery : function()
16718     {
16719         var qe = {
16720             query: '',
16721             forceAll: true,
16722             combo: this,
16723             cancel:false
16724         };
16725         
16726         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
16727             return false;
16728         }
16729         
16730         if(!this.alwaysQuery || this.mode == 'local'){
16731             this.onTouchViewLoad();
16732             return;
16733         }
16734         
16735         this.store.load();
16736     },
16737     
16738     onTouchViewBeforeLoad : function(combo,opts)
16739     {
16740         return;
16741     },
16742
16743     // private
16744     onTouchViewLoad : function()
16745     {
16746         if(this.store.getCount() < 1){
16747             this.onTouchViewEmptyResults();
16748             return;
16749         }
16750         
16751         this.clearTouchView();
16752         
16753         var rawValue = this.getRawValue();
16754         
16755         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
16756         
16757         this.tickItems = [];
16758         
16759         this.store.data.each(function(d, rowIndex){
16760             var row = this.touchViewListGroup.createChild(template);
16761             
16762             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
16763                 row.addClass(d.data.cls);
16764             }
16765             
16766             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
16767                 var cfg = {
16768                     data : d.data,
16769                     html : d.data[this.displayField]
16770                 };
16771                 
16772                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
16773                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
16774                 }
16775             }
16776             row.removeClass('selected');
16777             if(!this.multiple && this.valueField &&
16778                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
16779             {
16780                 // radio buttons..
16781                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16782                 row.addClass('selected');
16783             }
16784             
16785             if(this.multiple && this.valueField &&
16786                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
16787             {
16788                 
16789                 // checkboxes...
16790                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16791                 this.tickItems.push(d.data);
16792             }
16793             
16794             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
16795             
16796         }, this);
16797         
16798         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
16799         
16800         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
16801
16802         if(this.modalTitle.length){
16803             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
16804         }
16805
16806         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
16807         
16808         if(this.mobile_restrict_height && listHeight < bodyHeight){
16809             this.touchViewBodyEl.setHeight(listHeight);
16810         }
16811         
16812         var _this = this;
16813         
16814         if(firstChecked && listHeight > bodyHeight){
16815             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
16816         }
16817         
16818     },
16819     
16820     onTouchViewLoadException : function()
16821     {
16822         this.hideTouchView();
16823     },
16824     
16825     onTouchViewEmptyResults : function()
16826     {
16827         this.clearTouchView();
16828         
16829         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
16830         
16831         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
16832         
16833     },
16834     
16835     clearTouchView : function()
16836     {
16837         this.touchViewListGroup.dom.innerHTML = '';
16838     },
16839     
16840     onTouchViewClick : function(e, el, o)
16841     {
16842         e.preventDefault();
16843         
16844         var row = o.row;
16845         var rowIndex = o.rowIndex;
16846         
16847         var r = this.store.getAt(rowIndex);
16848         
16849         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
16850             
16851             if(!this.multiple){
16852                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
16853                     c.dom.removeAttribute('checked');
16854                 }, this);
16855
16856                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16857
16858                 this.setFromData(r.data);
16859
16860                 var close = this.closeTriggerEl();
16861
16862                 if(close){
16863                     close.show();
16864                 }
16865
16866                 this.hideTouchView();
16867
16868                 this.fireEvent('select', this, r, rowIndex);
16869
16870                 return;
16871             }
16872
16873             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
16874                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
16875                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
16876                 return;
16877             }
16878
16879             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16880             this.addItem(r.data);
16881             this.tickItems.push(r.data);
16882         }
16883     },
16884     
16885     getAutoCreateNativeIOS : function()
16886     {
16887         var cfg = {
16888             cls: 'form-group' //input-group,
16889         };
16890         
16891         var combobox =  {
16892             tag: 'select',
16893             cls : 'roo-ios-select'
16894         };
16895         
16896         if (this.name) {
16897             combobox.name = this.name;
16898         }
16899         
16900         if (this.disabled) {
16901             combobox.disabled = true;
16902         }
16903         
16904         var settings = this;
16905         
16906         ['xs','sm','md','lg'].map(function(size){
16907             if (settings[size]) {
16908                 cfg.cls += ' col-' + size + '-' + settings[size];
16909             }
16910         });
16911         
16912         cfg.cn = combobox;
16913         
16914         return cfg;
16915         
16916     },
16917     
16918     initIOSView : function()
16919     {
16920         this.store.on('load', this.onIOSViewLoad, this);
16921         
16922         return;
16923     },
16924     
16925     onIOSViewLoad : function()
16926     {
16927         if(this.store.getCount() < 1){
16928             return;
16929         }
16930         
16931         this.clearIOSView();
16932         
16933         if(this.allowBlank) {
16934             
16935             var default_text = '-- SELECT --';
16936             
16937             if(this.placeholder.length){
16938                 default_text = this.placeholder;
16939             }
16940             
16941             if(this.emptyTitle.length){
16942                 default_text += ' - ' + this.emptyTitle + ' -';
16943             }
16944             
16945             var opt = this.inputEl().createChild({
16946                 tag: 'option',
16947                 value : 0,
16948                 html : default_text
16949             });
16950             
16951             var o = {};
16952             o[this.valueField] = 0;
16953             o[this.displayField] = default_text;
16954             
16955             this.ios_options.push({
16956                 data : o,
16957                 el : opt
16958             });
16959             
16960         }
16961         
16962         this.store.data.each(function(d, rowIndex){
16963             
16964             var html = '';
16965             
16966             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
16967                 html = d.data[this.displayField];
16968             }
16969             
16970             var value = '';
16971             
16972             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
16973                 value = d.data[this.valueField];
16974             }
16975             
16976             var option = {
16977                 tag: 'option',
16978                 value : value,
16979                 html : html
16980             };
16981             
16982             if(this.value == d.data[this.valueField]){
16983                 option['selected'] = true;
16984             }
16985             
16986             var opt = this.inputEl().createChild(option);
16987             
16988             this.ios_options.push({
16989                 data : d.data,
16990                 el : opt
16991             });
16992             
16993         }, this);
16994         
16995         this.inputEl().on('change', function(){
16996            this.fireEvent('select', this);
16997         }, this);
16998         
16999     },
17000     
17001     clearIOSView: function()
17002     {
17003         this.inputEl().dom.innerHTML = '';
17004         
17005         this.ios_options = [];
17006     },
17007     
17008     setIOSValue: function(v)
17009     {
17010         this.value = v;
17011         
17012         if(!this.ios_options){
17013             return;
17014         }
17015         
17016         Roo.each(this.ios_options, function(opts){
17017            
17018            opts.el.dom.removeAttribute('selected');
17019            
17020            if(opts.data[this.valueField] != v){
17021                return;
17022            }
17023            
17024            opts.el.dom.setAttribute('selected', true);
17025            
17026         }, this);
17027     }
17028
17029     /** 
17030     * @cfg {Boolean} grow 
17031     * @hide 
17032     */
17033     /** 
17034     * @cfg {Number} growMin 
17035     * @hide 
17036     */
17037     /** 
17038     * @cfg {Number} growMax 
17039     * @hide 
17040     */
17041     /**
17042      * @hide
17043      * @method autoSize
17044      */
17045 });
17046
17047 Roo.apply(Roo.bootstrap.ComboBox,  {
17048     
17049     header : {
17050         tag: 'div',
17051         cls: 'modal-header',
17052         cn: [
17053             {
17054                 tag: 'h4',
17055                 cls: 'modal-title'
17056             }
17057         ]
17058     },
17059     
17060     body : {
17061         tag: 'div',
17062         cls: 'modal-body',
17063         cn: [
17064             {
17065                 tag: 'ul',
17066                 cls: 'list-group'
17067             }
17068         ]
17069     },
17070     
17071     listItemRadio : {
17072         tag: 'li',
17073         cls: 'list-group-item',
17074         cn: [
17075             {
17076                 tag: 'span',
17077                 cls: 'roo-combobox-list-group-item-value'
17078             },
17079             {
17080                 tag: 'div',
17081                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17082                 cn: [
17083                     {
17084                         tag: 'input',
17085                         type: 'radio'
17086                     },
17087                     {
17088                         tag: 'label'
17089                     }
17090                 ]
17091             }
17092         ]
17093     },
17094     
17095     listItemCheckbox : {
17096         tag: 'li',
17097         cls: 'list-group-item',
17098         cn: [
17099             {
17100                 tag: 'span',
17101                 cls: 'roo-combobox-list-group-item-value'
17102             },
17103             {
17104                 tag: 'div',
17105                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17106                 cn: [
17107                     {
17108                         tag: 'input',
17109                         type: 'checkbox'
17110                     },
17111                     {
17112                         tag: 'label'
17113                     }
17114                 ]
17115             }
17116         ]
17117     },
17118     
17119     emptyResult : {
17120         tag: 'div',
17121         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17122     },
17123     
17124     footer : {
17125         tag: 'div',
17126         cls: 'modal-footer',
17127         cn: [
17128             {
17129                 tag: 'div',
17130                 cls: 'row',
17131                 cn: [
17132                     {
17133                         tag: 'div',
17134                         cls: 'col-xs-6 text-left',
17135                         cn: {
17136                             tag: 'button',
17137                             cls: 'btn btn-danger roo-touch-view-cancel',
17138                             html: 'Cancel'
17139                         }
17140                     },
17141                     {
17142                         tag: 'div',
17143                         cls: 'col-xs-6 text-right',
17144                         cn: {
17145                             tag: 'button',
17146                             cls: 'btn btn-success roo-touch-view-ok',
17147                             html: 'OK'
17148                         }
17149                     }
17150                 ]
17151             }
17152         ]
17153         
17154     }
17155 });
17156
17157 Roo.apply(Roo.bootstrap.ComboBox,  {
17158     
17159     touchViewTemplate : {
17160         tag: 'div',
17161         cls: 'modal fade roo-combobox-touch-view',
17162         cn: [
17163             {
17164                 tag: 'div',
17165                 cls: 'modal-dialog',
17166                 style : 'position:fixed', // we have to fix position....
17167                 cn: [
17168                     {
17169                         tag: 'div',
17170                         cls: 'modal-content',
17171                         cn: [
17172                             Roo.bootstrap.ComboBox.header,
17173                             Roo.bootstrap.ComboBox.body,
17174                             Roo.bootstrap.ComboBox.footer
17175                         ]
17176                     }
17177                 ]
17178             }
17179         ]
17180     }
17181 });/*
17182  * Based on:
17183  * Ext JS Library 1.1.1
17184  * Copyright(c) 2006-2007, Ext JS, LLC.
17185  *
17186  * Originally Released Under LGPL - original licence link has changed is not relivant.
17187  *
17188  * Fork - LGPL
17189  * <script type="text/javascript">
17190  */
17191
17192 /**
17193  * @class Roo.View
17194  * @extends Roo.util.Observable
17195  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
17196  * This class also supports single and multi selection modes. <br>
17197  * Create a data model bound view:
17198  <pre><code>
17199  var store = new Roo.data.Store(...);
17200
17201  var view = new Roo.View({
17202     el : "my-element",
17203     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
17204  
17205     singleSelect: true,
17206     selectedClass: "ydataview-selected",
17207     store: store
17208  });
17209
17210  // listen for node click?
17211  view.on("click", function(vw, index, node, e){
17212  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
17213  });
17214
17215  // load XML data
17216  dataModel.load("foobar.xml");
17217  </code></pre>
17218  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
17219  * <br><br>
17220  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
17221  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
17222  * 
17223  * Note: old style constructor is still suported (container, template, config)
17224  * 
17225  * @constructor
17226  * Create a new View
17227  * @param {Object} config The config object
17228  * 
17229  */
17230 Roo.View = function(config, depreciated_tpl, depreciated_config){
17231     
17232     this.parent = false;
17233     
17234     if (typeof(depreciated_tpl) == 'undefined') {
17235         // new way.. - universal constructor.
17236         Roo.apply(this, config);
17237         this.el  = Roo.get(this.el);
17238     } else {
17239         // old format..
17240         this.el  = Roo.get(config);
17241         this.tpl = depreciated_tpl;
17242         Roo.apply(this, depreciated_config);
17243     }
17244     this.wrapEl  = this.el.wrap().wrap();
17245     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
17246     
17247     
17248     if(typeof(this.tpl) == "string"){
17249         this.tpl = new Roo.Template(this.tpl);
17250     } else {
17251         // support xtype ctors..
17252         this.tpl = new Roo.factory(this.tpl, Roo);
17253     }
17254     
17255     
17256     this.tpl.compile();
17257     
17258     /** @private */
17259     this.addEvents({
17260         /**
17261          * @event beforeclick
17262          * Fires before a click is processed. Returns false to cancel the default action.
17263          * @param {Roo.View} this
17264          * @param {Number} index The index of the target node
17265          * @param {HTMLElement} node The target node
17266          * @param {Roo.EventObject} e The raw event object
17267          */
17268             "beforeclick" : true,
17269         /**
17270          * @event click
17271          * Fires when a template node is clicked.
17272          * @param {Roo.View} this
17273          * @param {Number} index The index of the target node
17274          * @param {HTMLElement} node The target node
17275          * @param {Roo.EventObject} e The raw event object
17276          */
17277             "click" : true,
17278         /**
17279          * @event dblclick
17280          * Fires when a template node is double clicked.
17281          * @param {Roo.View} this
17282          * @param {Number} index The index of the target node
17283          * @param {HTMLElement} node The target node
17284          * @param {Roo.EventObject} e The raw event object
17285          */
17286             "dblclick" : true,
17287         /**
17288          * @event contextmenu
17289          * Fires when a template node is right clicked.
17290          * @param {Roo.View} this
17291          * @param {Number} index The index of the target node
17292          * @param {HTMLElement} node The target node
17293          * @param {Roo.EventObject} e The raw event object
17294          */
17295             "contextmenu" : true,
17296         /**
17297          * @event selectionchange
17298          * Fires when the selected nodes change.
17299          * @param {Roo.View} this
17300          * @param {Array} selections Array of the selected nodes
17301          */
17302             "selectionchange" : true,
17303     
17304         /**
17305          * @event beforeselect
17306          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
17307          * @param {Roo.View} this
17308          * @param {HTMLElement} node The node to be selected
17309          * @param {Array} selections Array of currently selected nodes
17310          */
17311             "beforeselect" : true,
17312         /**
17313          * @event preparedata
17314          * Fires on every row to render, to allow you to change the data.
17315          * @param {Roo.View} this
17316          * @param {Object} data to be rendered (change this)
17317          */
17318           "preparedata" : true
17319           
17320           
17321         });
17322
17323
17324
17325     this.el.on({
17326         "click": this.onClick,
17327         "dblclick": this.onDblClick,
17328         "contextmenu": this.onContextMenu,
17329         scope:this
17330     });
17331
17332     this.selections = [];
17333     this.nodes = [];
17334     this.cmp = new Roo.CompositeElementLite([]);
17335     if(this.store){
17336         this.store = Roo.factory(this.store, Roo.data);
17337         this.setStore(this.store, true);
17338     }
17339     
17340     if ( this.footer && this.footer.xtype) {
17341            
17342          var fctr = this.wrapEl.appendChild(document.createElement("div"));
17343         
17344         this.footer.dataSource = this.store;
17345         this.footer.container = fctr;
17346         this.footer = Roo.factory(this.footer, Roo);
17347         fctr.insertFirst(this.el);
17348         
17349         // this is a bit insane - as the paging toolbar seems to detach the el..
17350 //        dom.parentNode.parentNode.parentNode
17351          // they get detached?
17352     }
17353     
17354     
17355     Roo.View.superclass.constructor.call(this);
17356     
17357     
17358 };
17359
17360 Roo.extend(Roo.View, Roo.util.Observable, {
17361     
17362      /**
17363      * @cfg {Roo.data.Store} store Data store to load data from.
17364      */
17365     store : false,
17366     
17367     /**
17368      * @cfg {String|Roo.Element} el The container element.
17369      */
17370     el : '',
17371     
17372     /**
17373      * @cfg {String|Roo.Template} tpl The template used by this View 
17374      */
17375     tpl : false,
17376     /**
17377      * @cfg {String} dataName the named area of the template to use as the data area
17378      *                          Works with domtemplates roo-name="name"
17379      */
17380     dataName: false,
17381     /**
17382      * @cfg {String} selectedClass The css class to add to selected nodes
17383      */
17384     selectedClass : "x-view-selected",
17385      /**
17386      * @cfg {String} emptyText The empty text to show when nothing is loaded.
17387      */
17388     emptyText : "",
17389     
17390     /**
17391      * @cfg {String} text to display on mask (default Loading)
17392      */
17393     mask : false,
17394     /**
17395      * @cfg {Boolean} multiSelect Allow multiple selection
17396      */
17397     multiSelect : false,
17398     /**
17399      * @cfg {Boolean} singleSelect Allow single selection
17400      */
17401     singleSelect:  false,
17402     
17403     /**
17404      * @cfg {Boolean} toggleSelect - selecting 
17405      */
17406     toggleSelect : false,
17407     
17408     /**
17409      * @cfg {Boolean} tickable - selecting 
17410      */
17411     tickable : false,
17412     
17413     /**
17414      * Returns the element this view is bound to.
17415      * @return {Roo.Element}
17416      */
17417     getEl : function(){
17418         return this.wrapEl;
17419     },
17420     
17421     
17422
17423     /**
17424      * Refreshes the view. - called by datachanged on the store. - do not call directly.
17425      */
17426     refresh : function(){
17427         //Roo.log('refresh');
17428         var t = this.tpl;
17429         
17430         // if we are using something like 'domtemplate', then
17431         // the what gets used is:
17432         // t.applySubtemplate(NAME, data, wrapping data..)
17433         // the outer template then get' applied with
17434         //     the store 'extra data'
17435         // and the body get's added to the
17436         //      roo-name="data" node?
17437         //      <span class='roo-tpl-{name}'></span> ?????
17438         
17439         
17440         
17441         this.clearSelections();
17442         this.el.update("");
17443         var html = [];
17444         var records = this.store.getRange();
17445         if(records.length < 1) {
17446             
17447             // is this valid??  = should it render a template??
17448             
17449             this.el.update(this.emptyText);
17450             return;
17451         }
17452         var el = this.el;
17453         if (this.dataName) {
17454             this.el.update(t.apply(this.store.meta)); //????
17455             el = this.el.child('.roo-tpl-' + this.dataName);
17456         }
17457         
17458         for(var i = 0, len = records.length; i < len; i++){
17459             var data = this.prepareData(records[i].data, i, records[i]);
17460             this.fireEvent("preparedata", this, data, i, records[i]);
17461             
17462             var d = Roo.apply({}, data);
17463             
17464             if(this.tickable){
17465                 Roo.apply(d, {'roo-id' : Roo.id()});
17466                 
17467                 var _this = this;
17468             
17469                 Roo.each(this.parent.item, function(item){
17470                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
17471                         return;
17472                     }
17473                     Roo.apply(d, {'roo-data-checked' : 'checked'});
17474                 });
17475             }
17476             
17477             html[html.length] = Roo.util.Format.trim(
17478                 this.dataName ?
17479                     t.applySubtemplate(this.dataName, d, this.store.meta) :
17480                     t.apply(d)
17481             );
17482         }
17483         
17484         
17485         
17486         el.update(html.join(""));
17487         this.nodes = el.dom.childNodes;
17488         this.updateIndexes(0);
17489     },
17490     
17491
17492     /**
17493      * Function to override to reformat the data that is sent to
17494      * the template for each node.
17495      * DEPRICATED - use the preparedata event handler.
17496      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
17497      * a JSON object for an UpdateManager bound view).
17498      */
17499     prepareData : function(data, index, record)
17500     {
17501         this.fireEvent("preparedata", this, data, index, record);
17502         return data;
17503     },
17504
17505     onUpdate : function(ds, record){
17506         // Roo.log('on update');   
17507         this.clearSelections();
17508         var index = this.store.indexOf(record);
17509         var n = this.nodes[index];
17510         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
17511         n.parentNode.removeChild(n);
17512         this.updateIndexes(index, index);
17513     },
17514
17515     
17516     
17517 // --------- FIXME     
17518     onAdd : function(ds, records, index)
17519     {
17520         //Roo.log(['on Add', ds, records, index] );        
17521         this.clearSelections();
17522         if(this.nodes.length == 0){
17523             this.refresh();
17524             return;
17525         }
17526         var n = this.nodes[index];
17527         for(var i = 0, len = records.length; i < len; i++){
17528             var d = this.prepareData(records[i].data, i, records[i]);
17529             if(n){
17530                 this.tpl.insertBefore(n, d);
17531             }else{
17532                 
17533                 this.tpl.append(this.el, d);
17534             }
17535         }
17536         this.updateIndexes(index);
17537     },
17538
17539     onRemove : function(ds, record, index){
17540        // Roo.log('onRemove');
17541         this.clearSelections();
17542         var el = this.dataName  ?
17543             this.el.child('.roo-tpl-' + this.dataName) :
17544             this.el; 
17545         
17546         el.dom.removeChild(this.nodes[index]);
17547         this.updateIndexes(index);
17548     },
17549
17550     /**
17551      * Refresh an individual node.
17552      * @param {Number} index
17553      */
17554     refreshNode : function(index){
17555         this.onUpdate(this.store, this.store.getAt(index));
17556     },
17557
17558     updateIndexes : function(startIndex, endIndex){
17559         var ns = this.nodes;
17560         startIndex = startIndex || 0;
17561         endIndex = endIndex || ns.length - 1;
17562         for(var i = startIndex; i <= endIndex; i++){
17563             ns[i].nodeIndex = i;
17564         }
17565     },
17566
17567     /**
17568      * Changes the data store this view uses and refresh the view.
17569      * @param {Store} store
17570      */
17571     setStore : function(store, initial){
17572         if(!initial && this.store){
17573             this.store.un("datachanged", this.refresh);
17574             this.store.un("add", this.onAdd);
17575             this.store.un("remove", this.onRemove);
17576             this.store.un("update", this.onUpdate);
17577             this.store.un("clear", this.refresh);
17578             this.store.un("beforeload", this.onBeforeLoad);
17579             this.store.un("load", this.onLoad);
17580             this.store.un("loadexception", this.onLoad);
17581         }
17582         if(store){
17583           
17584             store.on("datachanged", this.refresh, this);
17585             store.on("add", this.onAdd, this);
17586             store.on("remove", this.onRemove, this);
17587             store.on("update", this.onUpdate, this);
17588             store.on("clear", this.refresh, this);
17589             store.on("beforeload", this.onBeforeLoad, this);
17590             store.on("load", this.onLoad, this);
17591             store.on("loadexception", this.onLoad, this);
17592         }
17593         
17594         if(store){
17595             this.refresh();
17596         }
17597     },
17598     /**
17599      * onbeforeLoad - masks the loading area.
17600      *
17601      */
17602     onBeforeLoad : function(store,opts)
17603     {
17604          //Roo.log('onBeforeLoad');   
17605         if (!opts.add) {
17606             this.el.update("");
17607         }
17608         this.el.mask(this.mask ? this.mask : "Loading" ); 
17609     },
17610     onLoad : function ()
17611     {
17612         this.el.unmask();
17613     },
17614     
17615
17616     /**
17617      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
17618      * @param {HTMLElement} node
17619      * @return {HTMLElement} The template node
17620      */
17621     findItemFromChild : function(node){
17622         var el = this.dataName  ?
17623             this.el.child('.roo-tpl-' + this.dataName,true) :
17624             this.el.dom; 
17625         
17626         if(!node || node.parentNode == el){
17627                     return node;
17628             }
17629             var p = node.parentNode;
17630             while(p && p != el){
17631             if(p.parentNode == el){
17632                 return p;
17633             }
17634             p = p.parentNode;
17635         }
17636             return null;
17637     },
17638
17639     /** @ignore */
17640     onClick : function(e){
17641         var item = this.findItemFromChild(e.getTarget());
17642         if(item){
17643             var index = this.indexOf(item);
17644             if(this.onItemClick(item, index, e) !== false){
17645                 this.fireEvent("click", this, index, item, e);
17646             }
17647         }else{
17648             this.clearSelections();
17649         }
17650     },
17651
17652     /** @ignore */
17653     onContextMenu : function(e){
17654         var item = this.findItemFromChild(e.getTarget());
17655         if(item){
17656             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
17657         }
17658     },
17659
17660     /** @ignore */
17661     onDblClick : function(e){
17662         var item = this.findItemFromChild(e.getTarget());
17663         if(item){
17664             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
17665         }
17666     },
17667
17668     onItemClick : function(item, index, e)
17669     {
17670         if(this.fireEvent("beforeclick", this, index, item, e) === false){
17671             return false;
17672         }
17673         if (this.toggleSelect) {
17674             var m = this.isSelected(item) ? 'unselect' : 'select';
17675             //Roo.log(m);
17676             var _t = this;
17677             _t[m](item, true, false);
17678             return true;
17679         }
17680         if(this.multiSelect || this.singleSelect){
17681             if(this.multiSelect && e.shiftKey && this.lastSelection){
17682                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
17683             }else{
17684                 this.select(item, this.multiSelect && e.ctrlKey);
17685                 this.lastSelection = item;
17686             }
17687             
17688             if(!this.tickable){
17689                 e.preventDefault();
17690             }
17691             
17692         }
17693         return true;
17694     },
17695
17696     /**
17697      * Get the number of selected nodes.
17698      * @return {Number}
17699      */
17700     getSelectionCount : function(){
17701         return this.selections.length;
17702     },
17703
17704     /**
17705      * Get the currently selected nodes.
17706      * @return {Array} An array of HTMLElements
17707      */
17708     getSelectedNodes : function(){
17709         return this.selections;
17710     },
17711
17712     /**
17713      * Get the indexes of the selected nodes.
17714      * @return {Array}
17715      */
17716     getSelectedIndexes : function(){
17717         var indexes = [], s = this.selections;
17718         for(var i = 0, len = s.length; i < len; i++){
17719             indexes.push(s[i].nodeIndex);
17720         }
17721         return indexes;
17722     },
17723
17724     /**
17725      * Clear all selections
17726      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
17727      */
17728     clearSelections : function(suppressEvent){
17729         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
17730             this.cmp.elements = this.selections;
17731             this.cmp.removeClass(this.selectedClass);
17732             this.selections = [];
17733             if(!suppressEvent){
17734                 this.fireEvent("selectionchange", this, this.selections);
17735             }
17736         }
17737     },
17738
17739     /**
17740      * Returns true if the passed node is selected
17741      * @param {HTMLElement/Number} node The node or node index
17742      * @return {Boolean}
17743      */
17744     isSelected : function(node){
17745         var s = this.selections;
17746         if(s.length < 1){
17747             return false;
17748         }
17749         node = this.getNode(node);
17750         return s.indexOf(node) !== -1;
17751     },
17752
17753     /**
17754      * Selects nodes.
17755      * @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
17756      * @param {Boolean} keepExisting (optional) true to keep existing selections
17757      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
17758      */
17759     select : function(nodeInfo, keepExisting, suppressEvent){
17760         if(nodeInfo instanceof Array){
17761             if(!keepExisting){
17762                 this.clearSelections(true);
17763             }
17764             for(var i = 0, len = nodeInfo.length; i < len; i++){
17765                 this.select(nodeInfo[i], true, true);
17766             }
17767             return;
17768         } 
17769         var node = this.getNode(nodeInfo);
17770         if(!node || this.isSelected(node)){
17771             return; // already selected.
17772         }
17773         if(!keepExisting){
17774             this.clearSelections(true);
17775         }
17776         
17777         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
17778             Roo.fly(node).addClass(this.selectedClass);
17779             this.selections.push(node);
17780             if(!suppressEvent){
17781                 this.fireEvent("selectionchange", this, this.selections);
17782             }
17783         }
17784         
17785         
17786     },
17787       /**
17788      * Unselects nodes.
17789      * @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
17790      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
17791      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
17792      */
17793     unselect : function(nodeInfo, keepExisting, suppressEvent)
17794     {
17795         if(nodeInfo instanceof Array){
17796             Roo.each(this.selections, function(s) {
17797                 this.unselect(s, nodeInfo);
17798             }, this);
17799             return;
17800         }
17801         var node = this.getNode(nodeInfo);
17802         if(!node || !this.isSelected(node)){
17803             //Roo.log("not selected");
17804             return; // not selected.
17805         }
17806         // fireevent???
17807         var ns = [];
17808         Roo.each(this.selections, function(s) {
17809             if (s == node ) {
17810                 Roo.fly(node).removeClass(this.selectedClass);
17811
17812                 return;
17813             }
17814             ns.push(s);
17815         },this);
17816         
17817         this.selections= ns;
17818         this.fireEvent("selectionchange", this, this.selections);
17819     },
17820
17821     /**
17822      * Gets a template node.
17823      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
17824      * @return {HTMLElement} The node or null if it wasn't found
17825      */
17826     getNode : function(nodeInfo){
17827         if(typeof nodeInfo == "string"){
17828             return document.getElementById(nodeInfo);
17829         }else if(typeof nodeInfo == "number"){
17830             return this.nodes[nodeInfo];
17831         }
17832         return nodeInfo;
17833     },
17834
17835     /**
17836      * Gets a range template nodes.
17837      * @param {Number} startIndex
17838      * @param {Number} endIndex
17839      * @return {Array} An array of nodes
17840      */
17841     getNodes : function(start, end){
17842         var ns = this.nodes;
17843         start = start || 0;
17844         end = typeof end == "undefined" ? ns.length - 1 : end;
17845         var nodes = [];
17846         if(start <= end){
17847             for(var i = start; i <= end; i++){
17848                 nodes.push(ns[i]);
17849             }
17850         } else{
17851             for(var i = start; i >= end; i--){
17852                 nodes.push(ns[i]);
17853             }
17854         }
17855         return nodes;
17856     },
17857
17858     /**
17859      * Finds the index of the passed node
17860      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
17861      * @return {Number} The index of the node or -1
17862      */
17863     indexOf : function(node){
17864         node = this.getNode(node);
17865         if(typeof node.nodeIndex == "number"){
17866             return node.nodeIndex;
17867         }
17868         var ns = this.nodes;
17869         for(var i = 0, len = ns.length; i < len; i++){
17870             if(ns[i] == node){
17871                 return i;
17872             }
17873         }
17874         return -1;
17875     }
17876 });
17877 /*
17878  * - LGPL
17879  *
17880  * based on jquery fullcalendar
17881  * 
17882  */
17883
17884 Roo.bootstrap = Roo.bootstrap || {};
17885 /**
17886  * @class Roo.bootstrap.Calendar
17887  * @extends Roo.bootstrap.Component
17888  * Bootstrap Calendar class
17889  * @cfg {Boolean} loadMask (true|false) default false
17890  * @cfg {Object} header generate the user specific header of the calendar, default false
17891
17892  * @constructor
17893  * Create a new Container
17894  * @param {Object} config The config object
17895  */
17896
17897
17898
17899 Roo.bootstrap.Calendar = function(config){
17900     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
17901      this.addEvents({
17902         /**
17903              * @event select
17904              * Fires when a date is selected
17905              * @param {DatePicker} this
17906              * @param {Date} date The selected date
17907              */
17908         'select': true,
17909         /**
17910              * @event monthchange
17911              * Fires when the displayed month changes 
17912              * @param {DatePicker} this
17913              * @param {Date} date The selected month
17914              */
17915         'monthchange': true,
17916         /**
17917              * @event evententer
17918              * Fires when mouse over an event
17919              * @param {Calendar} this
17920              * @param {event} Event
17921              */
17922         'evententer': true,
17923         /**
17924              * @event eventleave
17925              * Fires when the mouse leaves an
17926              * @param {Calendar} this
17927              * @param {event}
17928              */
17929         'eventleave': true,
17930         /**
17931              * @event eventclick
17932              * Fires when the mouse click an
17933              * @param {Calendar} this
17934              * @param {event}
17935              */
17936         'eventclick': true
17937         
17938     });
17939
17940 };
17941
17942 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
17943     
17944      /**
17945      * @cfg {Number} startDay
17946      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
17947      */
17948     startDay : 0,
17949     
17950     loadMask : false,
17951     
17952     header : false,
17953       
17954     getAutoCreate : function(){
17955         
17956         
17957         var fc_button = function(name, corner, style, content ) {
17958             return Roo.apply({},{
17959                 tag : 'span',
17960                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
17961                          (corner.length ?
17962                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
17963                             ''
17964                         ),
17965                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
17966                 unselectable: 'on'
17967             });
17968         };
17969         
17970         var header = {};
17971         
17972         if(!this.header){
17973             header = {
17974                 tag : 'table',
17975                 cls : 'fc-header',
17976                 style : 'width:100%',
17977                 cn : [
17978                     {
17979                         tag: 'tr',
17980                         cn : [
17981                             {
17982                                 tag : 'td',
17983                                 cls : 'fc-header-left',
17984                                 cn : [
17985                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
17986                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
17987                                     { tag: 'span', cls: 'fc-header-space' },
17988                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
17989
17990
17991                                 ]
17992                             },
17993
17994                             {
17995                                 tag : 'td',
17996                                 cls : 'fc-header-center',
17997                                 cn : [
17998                                     {
17999                                         tag: 'span',
18000                                         cls: 'fc-header-title',
18001                                         cn : {
18002                                             tag: 'H2',
18003                                             html : 'month / year'
18004                                         }
18005                                     }
18006
18007                                 ]
18008                             },
18009                             {
18010                                 tag : 'td',
18011                                 cls : 'fc-header-right',
18012                                 cn : [
18013                               /*      fc_button('month', 'left', '', 'month' ),
18014                                     fc_button('week', '', '', 'week' ),
18015                                     fc_button('day', 'right', '', 'day' )
18016                                 */    
18017
18018                                 ]
18019                             }
18020
18021                         ]
18022                     }
18023                 ]
18024             };
18025         }
18026         
18027         header = this.header;
18028         
18029        
18030         var cal_heads = function() {
18031             var ret = [];
18032             // fixme - handle this.
18033             
18034             for (var i =0; i < Date.dayNames.length; i++) {
18035                 var d = Date.dayNames[i];
18036                 ret.push({
18037                     tag: 'th',
18038                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18039                     html : d.substring(0,3)
18040                 });
18041                 
18042             }
18043             ret[0].cls += ' fc-first';
18044             ret[6].cls += ' fc-last';
18045             return ret;
18046         };
18047         var cal_cell = function(n) {
18048             return  {
18049                 tag: 'td',
18050                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18051                 cn : [
18052                     {
18053                         cn : [
18054                             {
18055                                 cls: 'fc-day-number',
18056                                 html: 'D'
18057                             },
18058                             {
18059                                 cls: 'fc-day-content',
18060                              
18061                                 cn : [
18062                                      {
18063                                         style: 'position: relative;' // height: 17px;
18064                                     }
18065                                 ]
18066                             }
18067                             
18068                             
18069                         ]
18070                     }
18071                 ]
18072                 
18073             }
18074         };
18075         var cal_rows = function() {
18076             
18077             var ret = [];
18078             for (var r = 0; r < 6; r++) {
18079                 var row= {
18080                     tag : 'tr',
18081                     cls : 'fc-week',
18082                     cn : []
18083                 };
18084                 
18085                 for (var i =0; i < Date.dayNames.length; i++) {
18086                     var d = Date.dayNames[i];
18087                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18088
18089                 }
18090                 row.cn[0].cls+=' fc-first';
18091                 row.cn[0].cn[0].style = 'min-height:90px';
18092                 row.cn[6].cls+=' fc-last';
18093                 ret.push(row);
18094                 
18095             }
18096             ret[0].cls += ' fc-first';
18097             ret[4].cls += ' fc-prev-last';
18098             ret[5].cls += ' fc-last';
18099             return ret;
18100             
18101         };
18102         
18103         var cal_table = {
18104             tag: 'table',
18105             cls: 'fc-border-separate',
18106             style : 'width:100%',
18107             cellspacing  : 0,
18108             cn : [
18109                 { 
18110                     tag: 'thead',
18111                     cn : [
18112                         { 
18113                             tag: 'tr',
18114                             cls : 'fc-first fc-last',
18115                             cn : cal_heads()
18116                         }
18117                     ]
18118                 },
18119                 { 
18120                     tag: 'tbody',
18121                     cn : cal_rows()
18122                 }
18123                   
18124             ]
18125         };
18126          
18127          var cfg = {
18128             cls : 'fc fc-ltr',
18129             cn : [
18130                 header,
18131                 {
18132                     cls : 'fc-content',
18133                     style : "position: relative;",
18134                     cn : [
18135                         {
18136                             cls : 'fc-view fc-view-month fc-grid',
18137                             style : 'position: relative',
18138                             unselectable : 'on',
18139                             cn : [
18140                                 {
18141                                     cls : 'fc-event-container',
18142                                     style : 'position:absolute;z-index:8;top:0;left:0;'
18143                                 },
18144                                 cal_table
18145                             ]
18146                         }
18147                     ]
18148     
18149                 }
18150            ] 
18151             
18152         };
18153         
18154          
18155         
18156         return cfg;
18157     },
18158     
18159     
18160     initEvents : function()
18161     {
18162         if(!this.store){
18163             throw "can not find store for calendar";
18164         }
18165         
18166         var mark = {
18167             tag: "div",
18168             cls:"x-dlg-mask",
18169             style: "text-align:center",
18170             cn: [
18171                 {
18172                     tag: "div",
18173                     style: "background-color:white;width:50%;margin:250 auto",
18174                     cn: [
18175                         {
18176                             tag: "img",
18177                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
18178                         },
18179                         {
18180                             tag: "span",
18181                             html: "Loading"
18182                         }
18183                         
18184                     ]
18185                 }
18186             ]
18187         };
18188         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
18189         
18190         var size = this.el.select('.fc-content', true).first().getSize();
18191         this.maskEl.setSize(size.width, size.height);
18192         this.maskEl.enableDisplayMode("block");
18193         if(!this.loadMask){
18194             this.maskEl.hide();
18195         }
18196         
18197         this.store = Roo.factory(this.store, Roo.data);
18198         this.store.on('load', this.onLoad, this);
18199         this.store.on('beforeload', this.onBeforeLoad, this);
18200         
18201         this.resize();
18202         
18203         this.cells = this.el.select('.fc-day',true);
18204         //Roo.log(this.cells);
18205         this.textNodes = this.el.query('.fc-day-number');
18206         this.cells.addClassOnOver('fc-state-hover');
18207         
18208         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
18209         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
18210         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
18211         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
18212         
18213         this.on('monthchange', this.onMonthChange, this);
18214         
18215         this.update(new Date().clearTime());
18216     },
18217     
18218     resize : function() {
18219         var sz  = this.el.getSize();
18220         
18221         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
18222         this.el.select('.fc-day-content div',true).setHeight(34);
18223     },
18224     
18225     
18226     // private
18227     showPrevMonth : function(e){
18228         this.update(this.activeDate.add("mo", -1));
18229     },
18230     showToday : function(e){
18231         this.update(new Date().clearTime());
18232     },
18233     // private
18234     showNextMonth : function(e){
18235         this.update(this.activeDate.add("mo", 1));
18236     },
18237
18238     // private
18239     showPrevYear : function(){
18240         this.update(this.activeDate.add("y", -1));
18241     },
18242
18243     // private
18244     showNextYear : function(){
18245         this.update(this.activeDate.add("y", 1));
18246     },
18247
18248     
18249    // private
18250     update : function(date)
18251     {
18252         var vd = this.activeDate;
18253         this.activeDate = date;
18254 //        if(vd && this.el){
18255 //            var t = date.getTime();
18256 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
18257 //                Roo.log('using add remove');
18258 //                
18259 //                this.fireEvent('monthchange', this, date);
18260 //                
18261 //                this.cells.removeClass("fc-state-highlight");
18262 //                this.cells.each(function(c){
18263 //                   if(c.dateValue == t){
18264 //                       c.addClass("fc-state-highlight");
18265 //                       setTimeout(function(){
18266 //                            try{c.dom.firstChild.focus();}catch(e){}
18267 //                       }, 50);
18268 //                       return false;
18269 //                   }
18270 //                   return true;
18271 //                });
18272 //                return;
18273 //            }
18274 //        }
18275         
18276         var days = date.getDaysInMonth();
18277         
18278         var firstOfMonth = date.getFirstDateOfMonth();
18279         var startingPos = firstOfMonth.getDay()-this.startDay;
18280         
18281         if(startingPos < this.startDay){
18282             startingPos += 7;
18283         }
18284         
18285         var pm = date.add(Date.MONTH, -1);
18286         var prevStart = pm.getDaysInMonth()-startingPos;
18287 //        
18288         this.cells = this.el.select('.fc-day',true);
18289         this.textNodes = this.el.query('.fc-day-number');
18290         this.cells.addClassOnOver('fc-state-hover');
18291         
18292         var cells = this.cells.elements;
18293         var textEls = this.textNodes;
18294         
18295         Roo.each(cells, function(cell){
18296             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
18297         });
18298         
18299         days += startingPos;
18300
18301         // convert everything to numbers so it's fast
18302         var day = 86400000;
18303         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
18304         //Roo.log(d);
18305         //Roo.log(pm);
18306         //Roo.log(prevStart);
18307         
18308         var today = new Date().clearTime().getTime();
18309         var sel = date.clearTime().getTime();
18310         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
18311         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
18312         var ddMatch = this.disabledDatesRE;
18313         var ddText = this.disabledDatesText;
18314         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
18315         var ddaysText = this.disabledDaysText;
18316         var format = this.format;
18317         
18318         var setCellClass = function(cal, cell){
18319             cell.row = 0;
18320             cell.events = [];
18321             cell.more = [];
18322             //Roo.log('set Cell Class');
18323             cell.title = "";
18324             var t = d.getTime();
18325             
18326             //Roo.log(d);
18327             
18328             cell.dateValue = t;
18329             if(t == today){
18330                 cell.className += " fc-today";
18331                 cell.className += " fc-state-highlight";
18332                 cell.title = cal.todayText;
18333             }
18334             if(t == sel){
18335                 // disable highlight in other month..
18336                 //cell.className += " fc-state-highlight";
18337                 
18338             }
18339             // disabling
18340             if(t < min) {
18341                 cell.className = " fc-state-disabled";
18342                 cell.title = cal.minText;
18343                 return;
18344             }
18345             if(t > max) {
18346                 cell.className = " fc-state-disabled";
18347                 cell.title = cal.maxText;
18348                 return;
18349             }
18350             if(ddays){
18351                 if(ddays.indexOf(d.getDay()) != -1){
18352                     cell.title = ddaysText;
18353                     cell.className = " fc-state-disabled";
18354                 }
18355             }
18356             if(ddMatch && format){
18357                 var fvalue = d.dateFormat(format);
18358                 if(ddMatch.test(fvalue)){
18359                     cell.title = ddText.replace("%0", fvalue);
18360                     cell.className = " fc-state-disabled";
18361                 }
18362             }
18363             
18364             if (!cell.initialClassName) {
18365                 cell.initialClassName = cell.dom.className;
18366             }
18367             
18368             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
18369         };
18370
18371         var i = 0;
18372         
18373         for(; i < startingPos; i++) {
18374             textEls[i].innerHTML = (++prevStart);
18375             d.setDate(d.getDate()+1);
18376             
18377             cells[i].className = "fc-past fc-other-month";
18378             setCellClass(this, cells[i]);
18379         }
18380         
18381         var intDay = 0;
18382         
18383         for(; i < days; i++){
18384             intDay = i - startingPos + 1;
18385             textEls[i].innerHTML = (intDay);
18386             d.setDate(d.getDate()+1);
18387             
18388             cells[i].className = ''; // "x-date-active";
18389             setCellClass(this, cells[i]);
18390         }
18391         var extraDays = 0;
18392         
18393         for(; i < 42; i++) {
18394             textEls[i].innerHTML = (++extraDays);
18395             d.setDate(d.getDate()+1);
18396             
18397             cells[i].className = "fc-future fc-other-month";
18398             setCellClass(this, cells[i]);
18399         }
18400         
18401         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
18402         
18403         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
18404         
18405         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
18406         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
18407         
18408         if(totalRows != 6){
18409             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
18410             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
18411         }
18412         
18413         this.fireEvent('monthchange', this, date);
18414         
18415         
18416         /*
18417         if(!this.internalRender){
18418             var main = this.el.dom.firstChild;
18419             var w = main.offsetWidth;
18420             this.el.setWidth(w + this.el.getBorderWidth("lr"));
18421             Roo.fly(main).setWidth(w);
18422             this.internalRender = true;
18423             // opera does not respect the auto grow header center column
18424             // then, after it gets a width opera refuses to recalculate
18425             // without a second pass
18426             if(Roo.isOpera && !this.secondPass){
18427                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
18428                 this.secondPass = true;
18429                 this.update.defer(10, this, [date]);
18430             }
18431         }
18432         */
18433         
18434     },
18435     
18436     findCell : function(dt) {
18437         dt = dt.clearTime().getTime();
18438         var ret = false;
18439         this.cells.each(function(c){
18440             //Roo.log("check " +c.dateValue + '?=' + dt);
18441             if(c.dateValue == dt){
18442                 ret = c;
18443                 return false;
18444             }
18445             return true;
18446         });
18447         
18448         return ret;
18449     },
18450     
18451     findCells : function(ev) {
18452         var s = ev.start.clone().clearTime().getTime();
18453        // Roo.log(s);
18454         var e= ev.end.clone().clearTime().getTime();
18455        // Roo.log(e);
18456         var ret = [];
18457         this.cells.each(function(c){
18458              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
18459             
18460             if(c.dateValue > e){
18461                 return ;
18462             }
18463             if(c.dateValue < s){
18464                 return ;
18465             }
18466             ret.push(c);
18467         });
18468         
18469         return ret;    
18470     },
18471     
18472 //    findBestRow: function(cells)
18473 //    {
18474 //        var ret = 0;
18475 //        
18476 //        for (var i =0 ; i < cells.length;i++) {
18477 //            ret  = Math.max(cells[i].rows || 0,ret);
18478 //        }
18479 //        return ret;
18480 //        
18481 //    },
18482     
18483     
18484     addItem : function(ev)
18485     {
18486         // look for vertical location slot in
18487         var cells = this.findCells(ev);
18488         
18489 //        ev.row = this.findBestRow(cells);
18490         
18491         // work out the location.
18492         
18493         var crow = false;
18494         var rows = [];
18495         for(var i =0; i < cells.length; i++) {
18496             
18497             cells[i].row = cells[0].row;
18498             
18499             if(i == 0){
18500                 cells[i].row = cells[i].row + 1;
18501             }
18502             
18503             if (!crow) {
18504                 crow = {
18505                     start : cells[i],
18506                     end :  cells[i]
18507                 };
18508                 continue;
18509             }
18510             if (crow.start.getY() == cells[i].getY()) {
18511                 // on same row.
18512                 crow.end = cells[i];
18513                 continue;
18514             }
18515             // different row.
18516             rows.push(crow);
18517             crow = {
18518                 start: cells[i],
18519                 end : cells[i]
18520             };
18521             
18522         }
18523         
18524         rows.push(crow);
18525         ev.els = [];
18526         ev.rows = rows;
18527         ev.cells = cells;
18528         
18529         cells[0].events.push(ev);
18530         
18531         this.calevents.push(ev);
18532     },
18533     
18534     clearEvents: function() {
18535         
18536         if(!this.calevents){
18537             return;
18538         }
18539         
18540         Roo.each(this.cells.elements, function(c){
18541             c.row = 0;
18542             c.events = [];
18543             c.more = [];
18544         });
18545         
18546         Roo.each(this.calevents, function(e) {
18547             Roo.each(e.els, function(el) {
18548                 el.un('mouseenter' ,this.onEventEnter, this);
18549                 el.un('mouseleave' ,this.onEventLeave, this);
18550                 el.remove();
18551             },this);
18552         },this);
18553         
18554         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
18555             e.remove();
18556         });
18557         
18558     },
18559     
18560     renderEvents: function()
18561     {   
18562         var _this = this;
18563         
18564         this.cells.each(function(c) {
18565             
18566             if(c.row < 5){
18567                 return;
18568             }
18569             
18570             var ev = c.events;
18571             
18572             var r = 4;
18573             if(c.row != c.events.length){
18574                 r = 4 - (4 - (c.row - c.events.length));
18575             }
18576             
18577             c.events = ev.slice(0, r);
18578             c.more = ev.slice(r);
18579             
18580             if(c.more.length && c.more.length == 1){
18581                 c.events.push(c.more.pop());
18582             }
18583             
18584             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
18585             
18586         });
18587             
18588         this.cells.each(function(c) {
18589             
18590             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
18591             
18592             
18593             for (var e = 0; e < c.events.length; e++){
18594                 var ev = c.events[e];
18595                 var rows = ev.rows;
18596                 
18597                 for(var i = 0; i < rows.length; i++) {
18598                 
18599                     // how many rows should it span..
18600
18601                     var  cfg = {
18602                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
18603                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
18604
18605                         unselectable : "on",
18606                         cn : [
18607                             {
18608                                 cls: 'fc-event-inner',
18609                                 cn : [
18610     //                                {
18611     //                                  tag:'span',
18612     //                                  cls: 'fc-event-time',
18613     //                                  html : cells.length > 1 ? '' : ev.time
18614     //                                },
18615                                     {
18616                                       tag:'span',
18617                                       cls: 'fc-event-title',
18618                                       html : String.format('{0}', ev.title)
18619                                     }
18620
18621
18622                                 ]
18623                             },
18624                             {
18625                                 cls: 'ui-resizable-handle ui-resizable-e',
18626                                 html : '&nbsp;&nbsp;&nbsp'
18627                             }
18628
18629                         ]
18630                     };
18631
18632                     if (i == 0) {
18633                         cfg.cls += ' fc-event-start';
18634                     }
18635                     if ((i+1) == rows.length) {
18636                         cfg.cls += ' fc-event-end';
18637                     }
18638
18639                     var ctr = _this.el.select('.fc-event-container',true).first();
18640                     var cg = ctr.createChild(cfg);
18641
18642                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
18643                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
18644
18645                     var r = (c.more.length) ? 1 : 0;
18646                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
18647                     cg.setWidth(ebox.right - sbox.x -2);
18648
18649                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
18650                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
18651                     cg.on('click', _this.onEventClick, _this, ev);
18652
18653                     ev.els.push(cg);
18654                     
18655                 }
18656                 
18657             }
18658             
18659             
18660             if(c.more.length){
18661                 var  cfg = {
18662                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
18663                     style : 'position: absolute',
18664                     unselectable : "on",
18665                     cn : [
18666                         {
18667                             cls: 'fc-event-inner',
18668                             cn : [
18669                                 {
18670                                   tag:'span',
18671                                   cls: 'fc-event-title',
18672                                   html : 'More'
18673                                 }
18674
18675
18676                             ]
18677                         },
18678                         {
18679                             cls: 'ui-resizable-handle ui-resizable-e',
18680                             html : '&nbsp;&nbsp;&nbsp'
18681                         }
18682
18683                     ]
18684                 };
18685
18686                 var ctr = _this.el.select('.fc-event-container',true).first();
18687                 var cg = ctr.createChild(cfg);
18688
18689                 var sbox = c.select('.fc-day-content',true).first().getBox();
18690                 var ebox = c.select('.fc-day-content',true).first().getBox();
18691                 //Roo.log(cg);
18692                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
18693                 cg.setWidth(ebox.right - sbox.x -2);
18694
18695                 cg.on('click', _this.onMoreEventClick, _this, c.more);
18696                 
18697             }
18698             
18699         });
18700         
18701         
18702         
18703     },
18704     
18705     onEventEnter: function (e, el,event,d) {
18706         this.fireEvent('evententer', this, el, event);
18707     },
18708     
18709     onEventLeave: function (e, el,event,d) {
18710         this.fireEvent('eventleave', this, el, event);
18711     },
18712     
18713     onEventClick: function (e, el,event,d) {
18714         this.fireEvent('eventclick', this, el, event);
18715     },
18716     
18717     onMonthChange: function () {
18718         this.store.load();
18719     },
18720     
18721     onMoreEventClick: function(e, el, more)
18722     {
18723         var _this = this;
18724         
18725         this.calpopover.placement = 'right';
18726         this.calpopover.setTitle('More');
18727         
18728         this.calpopover.setContent('');
18729         
18730         var ctr = this.calpopover.el.select('.popover-content', true).first();
18731         
18732         Roo.each(more, function(m){
18733             var cfg = {
18734                 cls : 'fc-event-hori fc-event-draggable',
18735                 html : m.title
18736             };
18737             var cg = ctr.createChild(cfg);
18738             
18739             cg.on('click', _this.onEventClick, _this, m);
18740         });
18741         
18742         this.calpopover.show(el);
18743         
18744         
18745     },
18746     
18747     onLoad: function () 
18748     {   
18749         this.calevents = [];
18750         var cal = this;
18751         
18752         if(this.store.getCount() > 0){
18753             this.store.data.each(function(d){
18754                cal.addItem({
18755                     id : d.data.id,
18756                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
18757                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
18758                     time : d.data.start_time,
18759                     title : d.data.title,
18760                     description : d.data.description,
18761                     venue : d.data.venue
18762                 });
18763             });
18764         }
18765         
18766         this.renderEvents();
18767         
18768         if(this.calevents.length && this.loadMask){
18769             this.maskEl.hide();
18770         }
18771     },
18772     
18773     onBeforeLoad: function()
18774     {
18775         this.clearEvents();
18776         if(this.loadMask){
18777             this.maskEl.show();
18778         }
18779     }
18780 });
18781
18782  
18783  /*
18784  * - LGPL
18785  *
18786  * element
18787  * 
18788  */
18789
18790 /**
18791  * @class Roo.bootstrap.Popover
18792  * @extends Roo.bootstrap.Component
18793  * Bootstrap Popover class
18794  * @cfg {String} html contents of the popover   (or false to use children..)
18795  * @cfg {String} title of popover (or false to hide)
18796  * @cfg {String} placement how it is placed
18797  * @cfg {String} trigger click || hover (or false to trigger manually)
18798  * @cfg {String} over what (parent or false to trigger manually.)
18799  * @cfg {Number} delay - delay before showing
18800  
18801  * @constructor
18802  * Create a new Popover
18803  * @param {Object} config The config object
18804  */
18805
18806 Roo.bootstrap.Popover = function(config){
18807     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
18808     
18809     this.addEvents({
18810         // raw events
18811          /**
18812          * @event show
18813          * After the popover show
18814          * 
18815          * @param {Roo.bootstrap.Popover} this
18816          */
18817         "show" : true,
18818         /**
18819          * @event hide
18820          * After the popover hide
18821          * 
18822          * @param {Roo.bootstrap.Popover} this
18823          */
18824         "hide" : true
18825     });
18826 };
18827
18828 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
18829     
18830     title: 'Fill in a title',
18831     html: false,
18832     
18833     placement : 'right',
18834     trigger : 'hover', // hover
18835     
18836     delay : 0,
18837     
18838     over: 'parent',
18839     
18840     can_build_overlaid : false,
18841     
18842     getChildContainer : function()
18843     {
18844         return this.el.select('.popover-content',true).first();
18845     },
18846     
18847     getAutoCreate : function(){
18848          
18849         var cfg = {
18850            cls : 'popover roo-dynamic',
18851            style: 'display:block',
18852            cn : [
18853                 {
18854                     cls : 'arrow'
18855                 },
18856                 {
18857                     cls : 'popover-inner',
18858                     cn : [
18859                         {
18860                             tag: 'h3',
18861                             cls: 'popover-title popover-header',
18862                             html : this.title
18863                         },
18864                         {
18865                             cls : 'popover-content popover-body',
18866                             html : this.html
18867                         }
18868                     ]
18869                     
18870                 }
18871            ]
18872         };
18873         
18874         return cfg;
18875     },
18876     setTitle: function(str)
18877     {
18878         this.title = str;
18879         this.el.select('.popover-title',true).first().dom.innerHTML = str;
18880     },
18881     setContent: function(str)
18882     {
18883         this.html = str;
18884         this.el.select('.popover-content',true).first().dom.innerHTML = str;
18885     },
18886     // as it get's added to the bottom of the page.
18887     onRender : function(ct, position)
18888     {
18889         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
18890         if(!this.el){
18891             var cfg = Roo.apply({},  this.getAutoCreate());
18892             cfg.id = Roo.id();
18893             
18894             if (this.cls) {
18895                 cfg.cls += ' ' + this.cls;
18896             }
18897             if (this.style) {
18898                 cfg.style = this.style;
18899             }
18900             //Roo.log("adding to ");
18901             this.el = Roo.get(document.body).createChild(cfg, position);
18902 //            Roo.log(this.el);
18903         }
18904         this.initEvents();
18905     },
18906     
18907     initEvents : function()
18908     {
18909         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
18910         this.el.enableDisplayMode('block');
18911         this.el.hide();
18912         if (this.over === false) {
18913             return; 
18914         }
18915         if (this.triggers === false) {
18916             return;
18917         }
18918         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
18919         var triggers = this.trigger ? this.trigger.split(' ') : [];
18920         Roo.each(triggers, function(trigger) {
18921         
18922             if (trigger == 'click') {
18923                 on_el.on('click', this.toggle, this);
18924             } else if (trigger != 'manual') {
18925                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
18926                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
18927       
18928                 on_el.on(eventIn  ,this.enter, this);
18929                 on_el.on(eventOut, this.leave, this);
18930             }
18931         }, this);
18932         
18933     },
18934     
18935     
18936     // private
18937     timeout : null,
18938     hoverState : null,
18939     
18940     toggle : function () {
18941         this.hoverState == 'in' ? this.leave() : this.enter();
18942     },
18943     
18944     enter : function () {
18945         
18946         clearTimeout(this.timeout);
18947     
18948         this.hoverState = 'in';
18949     
18950         if (!this.delay || !this.delay.show) {
18951             this.show();
18952             return;
18953         }
18954         var _t = this;
18955         this.timeout = setTimeout(function () {
18956             if (_t.hoverState == 'in') {
18957                 _t.show();
18958             }
18959         }, this.delay.show)
18960     },
18961     
18962     leave : function() {
18963         clearTimeout(this.timeout);
18964     
18965         this.hoverState = 'out';
18966     
18967         if (!this.delay || !this.delay.hide) {
18968             this.hide();
18969             return;
18970         }
18971         var _t = this;
18972         this.timeout = setTimeout(function () {
18973             if (_t.hoverState == 'out') {
18974                 _t.hide();
18975             }
18976         }, this.delay.hide)
18977     },
18978     
18979     show : function (on_el)
18980     {
18981         if (!on_el) {
18982             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
18983         }
18984         
18985         // set content.
18986         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
18987         if (this.html !== false) {
18988             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
18989         }
18990         this.el.removeClass([
18991             'fade','top','bottom', 'left', 'right','in',
18992             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
18993         ]);
18994         if (!this.title.length) {
18995             this.el.select('.popover-title',true).hide();
18996         }
18997         
18998         var placement = typeof this.placement == 'function' ?
18999             this.placement.call(this, this.el, on_el) :
19000             this.placement;
19001             
19002         var autoToken = /\s?auto?\s?/i;
19003         var autoPlace = autoToken.test(placement);
19004         if (autoPlace) {
19005             placement = placement.replace(autoToken, '') || 'top';
19006         }
19007         
19008         //this.el.detach()
19009         //this.el.setXY([0,0]);
19010         this.el.show();
19011         this.el.dom.style.display='block';
19012         this.el.addClass(placement);
19013         
19014         //this.el.appendTo(on_el);
19015         
19016         var p = this.getPosition();
19017         var box = this.el.getBox();
19018         
19019         if (autoPlace) {
19020             // fixme..
19021         }
19022         var align = Roo.bootstrap.Popover.alignment[placement];
19023         
19024 //        Roo.log(align);
19025         this.el.alignTo(on_el, align[0],align[1]);
19026         //var arrow = this.el.select('.arrow',true).first();
19027         //arrow.set(align[2], 
19028         
19029         this.el.addClass('in');
19030         
19031         
19032         if (this.el.hasClass('fade')) {
19033             // fade it?
19034         }
19035         
19036         this.hoverState = 'in';
19037         
19038         this.fireEvent('show', this);
19039         
19040     },
19041     hide : function()
19042     {
19043         this.el.setXY([0,0]);
19044         this.el.removeClass('in');
19045         this.el.hide();
19046         this.hoverState = null;
19047         
19048         this.fireEvent('hide', this);
19049     }
19050     
19051 });
19052
19053 Roo.bootstrap.Popover.alignment = {
19054     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
19055     'right' : ['l-r', [10,0], 'left bs-popover-left'],
19056     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
19057     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
19058 };
19059
19060  /*
19061  * - LGPL
19062  *
19063  * Progress
19064  * 
19065  */
19066
19067 /**
19068  * @class Roo.bootstrap.Progress
19069  * @extends Roo.bootstrap.Component
19070  * Bootstrap Progress class
19071  * @cfg {Boolean} striped striped of the progress bar
19072  * @cfg {Boolean} active animated of the progress bar
19073  * 
19074  * 
19075  * @constructor
19076  * Create a new Progress
19077  * @param {Object} config The config object
19078  */
19079
19080 Roo.bootstrap.Progress = function(config){
19081     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
19082 };
19083
19084 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
19085     
19086     striped : false,
19087     active: false,
19088     
19089     getAutoCreate : function(){
19090         var cfg = {
19091             tag: 'div',
19092             cls: 'progress'
19093         };
19094         
19095         
19096         if(this.striped){
19097             cfg.cls += ' progress-striped';
19098         }
19099       
19100         if(this.active){
19101             cfg.cls += ' active';
19102         }
19103         
19104         
19105         return cfg;
19106     }
19107    
19108 });
19109
19110  
19111
19112  /*
19113  * - LGPL
19114  *
19115  * ProgressBar
19116  * 
19117  */
19118
19119 /**
19120  * @class Roo.bootstrap.ProgressBar
19121  * @extends Roo.bootstrap.Component
19122  * Bootstrap ProgressBar class
19123  * @cfg {Number} aria_valuenow aria-value now
19124  * @cfg {Number} aria_valuemin aria-value min
19125  * @cfg {Number} aria_valuemax aria-value max
19126  * @cfg {String} label label for the progress bar
19127  * @cfg {String} panel (success | info | warning | danger )
19128  * @cfg {String} role role of the progress bar
19129  * @cfg {String} sr_only text
19130  * 
19131  * 
19132  * @constructor
19133  * Create a new ProgressBar
19134  * @param {Object} config The config object
19135  */
19136
19137 Roo.bootstrap.ProgressBar = function(config){
19138     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
19139 };
19140
19141 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
19142     
19143     aria_valuenow : 0,
19144     aria_valuemin : 0,
19145     aria_valuemax : 100,
19146     label : false,
19147     panel : false,
19148     role : false,
19149     sr_only: false,
19150     
19151     getAutoCreate : function()
19152     {
19153         
19154         var cfg = {
19155             tag: 'div',
19156             cls: 'progress-bar',
19157             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
19158         };
19159         
19160         if(this.sr_only){
19161             cfg.cn = {
19162                 tag: 'span',
19163                 cls: 'sr-only',
19164                 html: this.sr_only
19165             }
19166         }
19167         
19168         if(this.role){
19169             cfg.role = this.role;
19170         }
19171         
19172         if(this.aria_valuenow){
19173             cfg['aria-valuenow'] = this.aria_valuenow;
19174         }
19175         
19176         if(this.aria_valuemin){
19177             cfg['aria-valuemin'] = this.aria_valuemin;
19178         }
19179         
19180         if(this.aria_valuemax){
19181             cfg['aria-valuemax'] = this.aria_valuemax;
19182         }
19183         
19184         if(this.label && !this.sr_only){
19185             cfg.html = this.label;
19186         }
19187         
19188         if(this.panel){
19189             cfg.cls += ' progress-bar-' + this.panel;
19190         }
19191         
19192         return cfg;
19193     },
19194     
19195     update : function(aria_valuenow)
19196     {
19197         this.aria_valuenow = aria_valuenow;
19198         
19199         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
19200     }
19201    
19202 });
19203
19204  
19205
19206  /*
19207  * - LGPL
19208  *
19209  * column
19210  * 
19211  */
19212
19213 /**
19214  * @class Roo.bootstrap.TabGroup
19215  * @extends Roo.bootstrap.Column
19216  * Bootstrap Column class
19217  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
19218  * @cfg {Boolean} carousel true to make the group behave like a carousel
19219  * @cfg {Boolean} bullets show bullets for the panels
19220  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
19221  * @cfg {Number} timer auto slide timer .. default 0 millisecond
19222  * @cfg {Boolean} showarrow (true|false) show arrow default true
19223  * 
19224  * @constructor
19225  * Create a new TabGroup
19226  * @param {Object} config The config object
19227  */
19228
19229 Roo.bootstrap.TabGroup = function(config){
19230     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
19231     if (!this.navId) {
19232         this.navId = Roo.id();
19233     }
19234     this.tabs = [];
19235     Roo.bootstrap.TabGroup.register(this);
19236     
19237 };
19238
19239 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
19240     
19241     carousel : false,
19242     transition : false,
19243     bullets : 0,
19244     timer : 0,
19245     autoslide : false,
19246     slideFn : false,
19247     slideOnTouch : false,
19248     showarrow : true,
19249     
19250     getAutoCreate : function()
19251     {
19252         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
19253         
19254         cfg.cls += ' tab-content';
19255         
19256         if (this.carousel) {
19257             cfg.cls += ' carousel slide';
19258             
19259             cfg.cn = [{
19260                cls : 'carousel-inner',
19261                cn : []
19262             }];
19263         
19264             if(this.bullets  && !Roo.isTouch){
19265                 
19266                 var bullets = {
19267                     cls : 'carousel-bullets',
19268                     cn : []
19269                 };
19270                
19271                 if(this.bullets_cls){
19272                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
19273                 }
19274                 
19275                 bullets.cn.push({
19276                     cls : 'clear'
19277                 });
19278                 
19279                 cfg.cn[0].cn.push(bullets);
19280             }
19281             
19282             if(this.showarrow){
19283                 cfg.cn[0].cn.push({
19284                     tag : 'div',
19285                     class : 'carousel-arrow',
19286                     cn : [
19287                         {
19288                             tag : 'div',
19289                             class : 'carousel-prev',
19290                             cn : [
19291                                 {
19292                                     tag : 'i',
19293                                     class : 'fa fa-chevron-left'
19294                                 }
19295                             ]
19296                         },
19297                         {
19298                             tag : 'div',
19299                             class : 'carousel-next',
19300                             cn : [
19301                                 {
19302                                     tag : 'i',
19303                                     class : 'fa fa-chevron-right'
19304                                 }
19305                             ]
19306                         }
19307                     ]
19308                 });
19309             }
19310             
19311         }
19312         
19313         return cfg;
19314     },
19315     
19316     initEvents:  function()
19317     {
19318 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
19319 //            this.el.on("touchstart", this.onTouchStart, this);
19320 //        }
19321         
19322         if(this.autoslide){
19323             var _this = this;
19324             
19325             this.slideFn = window.setInterval(function() {
19326                 _this.showPanelNext();
19327             }, this.timer);
19328         }
19329         
19330         if(this.showarrow){
19331             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
19332             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
19333         }
19334         
19335         
19336     },
19337     
19338 //    onTouchStart : function(e, el, o)
19339 //    {
19340 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
19341 //            return;
19342 //        }
19343 //        
19344 //        this.showPanelNext();
19345 //    },
19346     
19347     
19348     getChildContainer : function()
19349     {
19350         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
19351     },
19352     
19353     /**
19354     * register a Navigation item
19355     * @param {Roo.bootstrap.NavItem} the navitem to add
19356     */
19357     register : function(item)
19358     {
19359         this.tabs.push( item);
19360         item.navId = this.navId; // not really needed..
19361         this.addBullet();
19362     
19363     },
19364     
19365     getActivePanel : function()
19366     {
19367         var r = false;
19368         Roo.each(this.tabs, function(t) {
19369             if (t.active) {
19370                 r = t;
19371                 return false;
19372             }
19373             return null;
19374         });
19375         return r;
19376         
19377     },
19378     getPanelByName : function(n)
19379     {
19380         var r = false;
19381         Roo.each(this.tabs, function(t) {
19382             if (t.tabId == n) {
19383                 r = t;
19384                 return false;
19385             }
19386             return null;
19387         });
19388         return r;
19389     },
19390     indexOfPanel : function(p)
19391     {
19392         var r = false;
19393         Roo.each(this.tabs, function(t,i) {
19394             if (t.tabId == p.tabId) {
19395                 r = i;
19396                 return false;
19397             }
19398             return null;
19399         });
19400         return r;
19401     },
19402     /**
19403      * show a specific panel
19404      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
19405      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
19406      */
19407     showPanel : function (pan)
19408     {
19409         if(this.transition || typeof(pan) == 'undefined'){
19410             Roo.log("waiting for the transitionend");
19411             return false;
19412         }
19413         
19414         if (typeof(pan) == 'number') {
19415             pan = this.tabs[pan];
19416         }
19417         
19418         if (typeof(pan) == 'string') {
19419             pan = this.getPanelByName(pan);
19420         }
19421         
19422         var cur = this.getActivePanel();
19423         
19424         if(!pan || !cur){
19425             Roo.log('pan or acitve pan is undefined');
19426             return false;
19427         }
19428         
19429         if (pan.tabId == this.getActivePanel().tabId) {
19430             return true;
19431         }
19432         
19433         if (false === cur.fireEvent('beforedeactivate')) {
19434             return false;
19435         }
19436         
19437         if(this.bullets > 0 && !Roo.isTouch){
19438             this.setActiveBullet(this.indexOfPanel(pan));
19439         }
19440         
19441         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
19442             
19443             //class="carousel-item carousel-item-next carousel-item-left"
19444             
19445             this.transition = true;
19446             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
19447             var lr = dir == 'next' ? 'left' : 'right';
19448             pan.el.addClass(dir); // or prev
19449             pan.el.addClass('carousel-item-' + dir); // or prev
19450             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
19451             cur.el.addClass(lr); // or right
19452             pan.el.addClass(lr);
19453             cur.el.addClass('carousel-item-' +lr); // or right
19454             pan.el.addClass('carousel-item-' +lr);
19455             
19456             
19457             var _this = this;
19458             cur.el.on('transitionend', function() {
19459                 Roo.log("trans end?");
19460                 
19461                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
19462                 pan.setActive(true);
19463                 
19464                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
19465                 cur.setActive(false);
19466                 
19467                 _this.transition = false;
19468                 
19469             }, this, { single:  true } );
19470             
19471             return true;
19472         }
19473         
19474         cur.setActive(false);
19475         pan.setActive(true);
19476         
19477         return true;
19478         
19479     },
19480     showPanelNext : function()
19481     {
19482         var i = this.indexOfPanel(this.getActivePanel());
19483         
19484         if (i >= this.tabs.length - 1 && !this.autoslide) {
19485             return;
19486         }
19487         
19488         if (i >= this.tabs.length - 1 && this.autoslide) {
19489             i = -1;
19490         }
19491         
19492         this.showPanel(this.tabs[i+1]);
19493     },
19494     
19495     showPanelPrev : function()
19496     {
19497         var i = this.indexOfPanel(this.getActivePanel());
19498         
19499         if (i  < 1 && !this.autoslide) {
19500             return;
19501         }
19502         
19503         if (i < 1 && this.autoslide) {
19504             i = this.tabs.length;
19505         }
19506         
19507         this.showPanel(this.tabs[i-1]);
19508     },
19509     
19510     
19511     addBullet: function()
19512     {
19513         if(!this.bullets || Roo.isTouch){
19514             return;
19515         }
19516         var ctr = this.el.select('.carousel-bullets',true).first();
19517         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
19518         var bullet = ctr.createChild({
19519             cls : 'bullet bullet-' + i
19520         },ctr.dom.lastChild);
19521         
19522         
19523         var _this = this;
19524         
19525         bullet.on('click', (function(e, el, o, ii, t){
19526
19527             e.preventDefault();
19528
19529             this.showPanel(ii);
19530
19531             if(this.autoslide && this.slideFn){
19532                 clearInterval(this.slideFn);
19533                 this.slideFn = window.setInterval(function() {
19534                     _this.showPanelNext();
19535                 }, this.timer);
19536             }
19537
19538         }).createDelegate(this, [i, bullet], true));
19539                 
19540         
19541     },
19542      
19543     setActiveBullet : function(i)
19544     {
19545         if(Roo.isTouch){
19546             return;
19547         }
19548         
19549         Roo.each(this.el.select('.bullet', true).elements, function(el){
19550             el.removeClass('selected');
19551         });
19552
19553         var bullet = this.el.select('.bullet-' + i, true).first();
19554         
19555         if(!bullet){
19556             return;
19557         }
19558         
19559         bullet.addClass('selected');
19560     }
19561     
19562     
19563   
19564 });
19565
19566  
19567
19568  
19569  
19570 Roo.apply(Roo.bootstrap.TabGroup, {
19571     
19572     groups: {},
19573      /**
19574     * register a Navigation Group
19575     * @param {Roo.bootstrap.NavGroup} the navgroup to add
19576     */
19577     register : function(navgrp)
19578     {
19579         this.groups[navgrp.navId] = navgrp;
19580         
19581     },
19582     /**
19583     * fetch a Navigation Group based on the navigation ID
19584     * if one does not exist , it will get created.
19585     * @param {string} the navgroup to add
19586     * @returns {Roo.bootstrap.NavGroup} the navgroup 
19587     */
19588     get: function(navId) {
19589         if (typeof(this.groups[navId]) == 'undefined') {
19590             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
19591         }
19592         return this.groups[navId] ;
19593     }
19594     
19595     
19596     
19597 });
19598
19599  /*
19600  * - LGPL
19601  *
19602  * TabPanel
19603  * 
19604  */
19605
19606 /**
19607  * @class Roo.bootstrap.TabPanel
19608  * @extends Roo.bootstrap.Component
19609  * Bootstrap TabPanel class
19610  * @cfg {Boolean} active panel active
19611  * @cfg {String} html panel content
19612  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
19613  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
19614  * @cfg {String} href click to link..
19615  * 
19616  * 
19617  * @constructor
19618  * Create a new TabPanel
19619  * @param {Object} config The config object
19620  */
19621
19622 Roo.bootstrap.TabPanel = function(config){
19623     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
19624     this.addEvents({
19625         /**
19626              * @event changed
19627              * Fires when the active status changes
19628              * @param {Roo.bootstrap.TabPanel} this
19629              * @param {Boolean} state the new state
19630             
19631          */
19632         'changed': true,
19633         /**
19634              * @event beforedeactivate
19635              * Fires before a tab is de-activated - can be used to do validation on a form.
19636              * @param {Roo.bootstrap.TabPanel} this
19637              * @return {Boolean} false if there is an error
19638             
19639          */
19640         'beforedeactivate': true
19641      });
19642     
19643     this.tabId = this.tabId || Roo.id();
19644   
19645 };
19646
19647 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
19648     
19649     active: false,
19650     html: false,
19651     tabId: false,
19652     navId : false,
19653     href : '',
19654     
19655     getAutoCreate : function(){
19656         
19657         
19658         var cfg = {
19659             tag: 'div',
19660             // item is needed for carousel - not sure if it has any effect otherwise
19661             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
19662             html: this.html || ''
19663         };
19664         
19665         if(this.active){
19666             cfg.cls += ' active';
19667         }
19668         
19669         if(this.tabId){
19670             cfg.tabId = this.tabId;
19671         }
19672         
19673         
19674         
19675         return cfg;
19676     },
19677     
19678     initEvents:  function()
19679     {
19680         var p = this.parent();
19681         
19682         this.navId = this.navId || p.navId;
19683         
19684         if (typeof(this.navId) != 'undefined') {
19685             // not really needed.. but just in case.. parent should be a NavGroup.
19686             var tg = Roo.bootstrap.TabGroup.get(this.navId);
19687             
19688             tg.register(this);
19689             
19690             var i = tg.tabs.length - 1;
19691             
19692             if(this.active && tg.bullets > 0 && i < tg.bullets){
19693                 tg.setActiveBullet(i);
19694             }
19695         }
19696         
19697         this.el.on('click', this.onClick, this);
19698         
19699         if(Roo.isTouch){
19700             this.el.on("touchstart", this.onTouchStart, this);
19701             this.el.on("touchmove", this.onTouchMove, this);
19702             this.el.on("touchend", this.onTouchEnd, this);
19703         }
19704         
19705     },
19706     
19707     onRender : function(ct, position)
19708     {
19709         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
19710     },
19711     
19712     setActive : function(state)
19713     {
19714         Roo.log("panel - set active " + this.tabId + "=" + state);
19715         
19716         this.active = state;
19717         if (!state) {
19718             this.el.removeClass('active');
19719             
19720         } else  if (!this.el.hasClass('active')) {
19721             this.el.addClass('active');
19722         }
19723         
19724         this.fireEvent('changed', this, state);
19725     },
19726     
19727     onClick : function(e)
19728     {
19729         e.preventDefault();
19730         
19731         if(!this.href.length){
19732             return;
19733         }
19734         
19735         window.location.href = this.href;
19736     },
19737     
19738     startX : 0,
19739     startY : 0,
19740     endX : 0,
19741     endY : 0,
19742     swiping : false,
19743     
19744     onTouchStart : function(e)
19745     {
19746         this.swiping = false;
19747         
19748         this.startX = e.browserEvent.touches[0].clientX;
19749         this.startY = e.browserEvent.touches[0].clientY;
19750     },
19751     
19752     onTouchMove : function(e)
19753     {
19754         this.swiping = true;
19755         
19756         this.endX = e.browserEvent.touches[0].clientX;
19757         this.endY = e.browserEvent.touches[0].clientY;
19758     },
19759     
19760     onTouchEnd : function(e)
19761     {
19762         if(!this.swiping){
19763             this.onClick(e);
19764             return;
19765         }
19766         
19767         var tabGroup = this.parent();
19768         
19769         if(this.endX > this.startX){ // swiping right
19770             tabGroup.showPanelPrev();
19771             return;
19772         }
19773         
19774         if(this.startX > this.endX){ // swiping left
19775             tabGroup.showPanelNext();
19776             return;
19777         }
19778     }
19779     
19780     
19781 });
19782  
19783
19784  
19785
19786  /*
19787  * - LGPL
19788  *
19789  * DateField
19790  * 
19791  */
19792
19793 /**
19794  * @class Roo.bootstrap.DateField
19795  * @extends Roo.bootstrap.Input
19796  * Bootstrap DateField class
19797  * @cfg {Number} weekStart default 0
19798  * @cfg {String} viewMode default empty, (months|years)
19799  * @cfg {String} minViewMode default empty, (months|years)
19800  * @cfg {Number} startDate default -Infinity
19801  * @cfg {Number} endDate default Infinity
19802  * @cfg {Boolean} todayHighlight default false
19803  * @cfg {Boolean} todayBtn default false
19804  * @cfg {Boolean} calendarWeeks default false
19805  * @cfg {Object} daysOfWeekDisabled default empty
19806  * @cfg {Boolean} singleMode default false (true | false)
19807  * 
19808  * @cfg {Boolean} keyboardNavigation default true
19809  * @cfg {String} language default en
19810  * 
19811  * @constructor
19812  * Create a new DateField
19813  * @param {Object} config The config object
19814  */
19815
19816 Roo.bootstrap.DateField = function(config){
19817     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
19818      this.addEvents({
19819             /**
19820              * @event show
19821              * Fires when this field show.
19822              * @param {Roo.bootstrap.DateField} this
19823              * @param {Mixed} date The date value
19824              */
19825             show : true,
19826             /**
19827              * @event show
19828              * Fires when this field hide.
19829              * @param {Roo.bootstrap.DateField} this
19830              * @param {Mixed} date The date value
19831              */
19832             hide : true,
19833             /**
19834              * @event select
19835              * Fires when select a date.
19836              * @param {Roo.bootstrap.DateField} this
19837              * @param {Mixed} date The date value
19838              */
19839             select : true,
19840             /**
19841              * @event beforeselect
19842              * Fires when before select a date.
19843              * @param {Roo.bootstrap.DateField} this
19844              * @param {Mixed} date The date value
19845              */
19846             beforeselect : true
19847         });
19848 };
19849
19850 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
19851     
19852     /**
19853      * @cfg {String} format
19854      * The default date format string which can be overriden for localization support.  The format must be
19855      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
19856      */
19857     format : "m/d/y",
19858     /**
19859      * @cfg {String} altFormats
19860      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
19861      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
19862      */
19863     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
19864     
19865     weekStart : 0,
19866     
19867     viewMode : '',
19868     
19869     minViewMode : '',
19870     
19871     todayHighlight : false,
19872     
19873     todayBtn: false,
19874     
19875     language: 'en',
19876     
19877     keyboardNavigation: true,
19878     
19879     calendarWeeks: false,
19880     
19881     startDate: -Infinity,
19882     
19883     endDate: Infinity,
19884     
19885     daysOfWeekDisabled: [],
19886     
19887     _events: [],
19888     
19889     singleMode : false,
19890     
19891     UTCDate: function()
19892     {
19893         return new Date(Date.UTC.apply(Date, arguments));
19894     },
19895     
19896     UTCToday: function()
19897     {
19898         var today = new Date();
19899         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
19900     },
19901     
19902     getDate: function() {
19903             var d = this.getUTCDate();
19904             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
19905     },
19906     
19907     getUTCDate: function() {
19908             return this.date;
19909     },
19910     
19911     setDate: function(d) {
19912             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
19913     },
19914     
19915     setUTCDate: function(d) {
19916             this.date = d;
19917             this.setValue(this.formatDate(this.date));
19918     },
19919         
19920     onRender: function(ct, position)
19921     {
19922         
19923         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
19924         
19925         this.language = this.language || 'en';
19926         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
19927         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
19928         
19929         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
19930         this.format = this.format || 'm/d/y';
19931         this.isInline = false;
19932         this.isInput = true;
19933         this.component = this.el.select('.add-on', true).first() || false;
19934         this.component = (this.component && this.component.length === 0) ? false : this.component;
19935         this.hasInput = this.component && this.inputEl().length;
19936         
19937         if (typeof(this.minViewMode === 'string')) {
19938             switch (this.minViewMode) {
19939                 case 'months':
19940                     this.minViewMode = 1;
19941                     break;
19942                 case 'years':
19943                     this.minViewMode = 2;
19944                     break;
19945                 default:
19946                     this.minViewMode = 0;
19947                     break;
19948             }
19949         }
19950         
19951         if (typeof(this.viewMode === 'string')) {
19952             switch (this.viewMode) {
19953                 case 'months':
19954                     this.viewMode = 1;
19955                     break;
19956                 case 'years':
19957                     this.viewMode = 2;
19958                     break;
19959                 default:
19960                     this.viewMode = 0;
19961                     break;
19962             }
19963         }
19964                 
19965         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
19966         
19967 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
19968         
19969         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19970         
19971         this.picker().on('mousedown', this.onMousedown, this);
19972         this.picker().on('click', this.onClick, this);
19973         
19974         this.picker().addClass('datepicker-dropdown');
19975         
19976         this.startViewMode = this.viewMode;
19977         
19978         if(this.singleMode){
19979             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
19980                 v.setVisibilityMode(Roo.Element.DISPLAY);
19981                 v.hide();
19982             });
19983             
19984             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19985                 v.setStyle('width', '189px');
19986             });
19987         }
19988         
19989         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
19990             if(!this.calendarWeeks){
19991                 v.remove();
19992                 return;
19993             }
19994             
19995             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19996             v.attr('colspan', function(i, val){
19997                 return parseInt(val) + 1;
19998             });
19999         });
20000                         
20001         
20002         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
20003         
20004         this.setStartDate(this.startDate);
20005         this.setEndDate(this.endDate);
20006         
20007         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
20008         
20009         this.fillDow();
20010         this.fillMonths();
20011         this.update();
20012         this.showMode();
20013         
20014         if(this.isInline) {
20015             this.showPopup();
20016         }
20017     },
20018     
20019     picker : function()
20020     {
20021         return this.pickerEl;
20022 //        return this.el.select('.datepicker', true).first();
20023     },
20024     
20025     fillDow: function()
20026     {
20027         var dowCnt = this.weekStart;
20028         
20029         var dow = {
20030             tag: 'tr',
20031             cn: [
20032                 
20033             ]
20034         };
20035         
20036         if(this.calendarWeeks){
20037             dow.cn.push({
20038                 tag: 'th',
20039                 cls: 'cw',
20040                 html: '&nbsp;'
20041             })
20042         }
20043         
20044         while (dowCnt < this.weekStart + 7) {
20045             dow.cn.push({
20046                 tag: 'th',
20047                 cls: 'dow',
20048                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
20049             });
20050         }
20051         
20052         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
20053     },
20054     
20055     fillMonths: function()
20056     {    
20057         var i = 0;
20058         var months = this.picker().select('>.datepicker-months td', true).first();
20059         
20060         months.dom.innerHTML = '';
20061         
20062         while (i < 12) {
20063             var month = {
20064                 tag: 'span',
20065                 cls: 'month',
20066                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
20067             };
20068             
20069             months.createChild(month);
20070         }
20071         
20072     },
20073     
20074     update: function()
20075     {
20076         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;
20077         
20078         if (this.date < this.startDate) {
20079             this.viewDate = new Date(this.startDate);
20080         } else if (this.date > this.endDate) {
20081             this.viewDate = new Date(this.endDate);
20082         } else {
20083             this.viewDate = new Date(this.date);
20084         }
20085         
20086         this.fill();
20087     },
20088     
20089     fill: function() 
20090     {
20091         var d = new Date(this.viewDate),
20092                 year = d.getUTCFullYear(),
20093                 month = d.getUTCMonth(),
20094                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
20095                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
20096                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
20097                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
20098                 currentDate = this.date && this.date.valueOf(),
20099                 today = this.UTCToday();
20100         
20101         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
20102         
20103 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20104         
20105 //        this.picker.select('>tfoot th.today').
20106 //                                              .text(dates[this.language].today)
20107 //                                              .toggle(this.todayBtn !== false);
20108     
20109         this.updateNavArrows();
20110         this.fillMonths();
20111                                                 
20112         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
20113         
20114         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
20115          
20116         prevMonth.setUTCDate(day);
20117         
20118         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
20119         
20120         var nextMonth = new Date(prevMonth);
20121         
20122         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
20123         
20124         nextMonth = nextMonth.valueOf();
20125         
20126         var fillMonths = false;
20127         
20128         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
20129         
20130         while(prevMonth.valueOf() <= nextMonth) {
20131             var clsName = '';
20132             
20133             if (prevMonth.getUTCDay() === this.weekStart) {
20134                 if(fillMonths){
20135                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
20136                 }
20137                     
20138                 fillMonths = {
20139                     tag: 'tr',
20140                     cn: []
20141                 };
20142                 
20143                 if(this.calendarWeeks){
20144                     // ISO 8601: First week contains first thursday.
20145                     // ISO also states week starts on Monday, but we can be more abstract here.
20146                     var
20147                     // Start of current week: based on weekstart/current date
20148                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
20149                     // Thursday of this week
20150                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
20151                     // First Thursday of year, year from thursday
20152                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
20153                     // Calendar week: ms between thursdays, div ms per day, div 7 days
20154                     calWeek =  (th - yth) / 864e5 / 7 + 1;
20155                     
20156                     fillMonths.cn.push({
20157                         tag: 'td',
20158                         cls: 'cw',
20159                         html: calWeek
20160                     });
20161                 }
20162             }
20163             
20164             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
20165                 clsName += ' old';
20166             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
20167                 clsName += ' new';
20168             }
20169             if (this.todayHighlight &&
20170                 prevMonth.getUTCFullYear() == today.getFullYear() &&
20171                 prevMonth.getUTCMonth() == today.getMonth() &&
20172                 prevMonth.getUTCDate() == today.getDate()) {
20173                 clsName += ' today';
20174             }
20175             
20176             if (currentDate && prevMonth.valueOf() === currentDate) {
20177                 clsName += ' active';
20178             }
20179             
20180             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
20181                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
20182                     clsName += ' disabled';
20183             }
20184             
20185             fillMonths.cn.push({
20186                 tag: 'td',
20187                 cls: 'day ' + clsName,
20188                 html: prevMonth.getDate()
20189             });
20190             
20191             prevMonth.setDate(prevMonth.getDate()+1);
20192         }
20193           
20194         var currentYear = this.date && this.date.getUTCFullYear();
20195         var currentMonth = this.date && this.date.getUTCMonth();
20196         
20197         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
20198         
20199         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
20200             v.removeClass('active');
20201             
20202             if(currentYear === year && k === currentMonth){
20203                 v.addClass('active');
20204             }
20205             
20206             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
20207                 v.addClass('disabled');
20208             }
20209             
20210         });
20211         
20212         
20213         year = parseInt(year/10, 10) * 10;
20214         
20215         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
20216         
20217         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
20218         
20219         year -= 1;
20220         for (var i = -1; i < 11; i++) {
20221             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
20222                 tag: 'span',
20223                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
20224                 html: year
20225             });
20226             
20227             year += 1;
20228         }
20229     },
20230     
20231     showMode: function(dir) 
20232     {
20233         if (dir) {
20234             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
20235         }
20236         
20237         Roo.each(this.picker().select('>div',true).elements, function(v){
20238             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20239             v.hide();
20240         });
20241         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
20242     },
20243     
20244     place: function()
20245     {
20246         if(this.isInline) {
20247             return;
20248         }
20249         
20250         this.picker().removeClass(['bottom', 'top']);
20251         
20252         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20253             /*
20254              * place to the top of element!
20255              *
20256              */
20257             
20258             this.picker().addClass('top');
20259             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20260             
20261             return;
20262         }
20263         
20264         this.picker().addClass('bottom');
20265         
20266         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20267     },
20268     
20269     parseDate : function(value)
20270     {
20271         if(!value || value instanceof Date){
20272             return value;
20273         }
20274         var v = Date.parseDate(value, this.format);
20275         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
20276             v = Date.parseDate(value, 'Y-m-d');
20277         }
20278         if(!v && this.altFormats){
20279             if(!this.altFormatsArray){
20280                 this.altFormatsArray = this.altFormats.split("|");
20281             }
20282             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
20283                 v = Date.parseDate(value, this.altFormatsArray[i]);
20284             }
20285         }
20286         return v;
20287     },
20288     
20289     formatDate : function(date, fmt)
20290     {   
20291         return (!date || !(date instanceof Date)) ?
20292         date : date.dateFormat(fmt || this.format);
20293     },
20294     
20295     onFocus : function()
20296     {
20297         Roo.bootstrap.DateField.superclass.onFocus.call(this);
20298         this.showPopup();
20299     },
20300     
20301     onBlur : function()
20302     {
20303         Roo.bootstrap.DateField.superclass.onBlur.call(this);
20304         
20305         var d = this.inputEl().getValue();
20306         
20307         this.setValue(d);
20308                 
20309         this.hidePopup();
20310     },
20311     
20312     showPopup : function()
20313     {
20314         this.picker().show();
20315         this.update();
20316         this.place();
20317         
20318         this.fireEvent('showpopup', this, this.date);
20319     },
20320     
20321     hidePopup : function()
20322     {
20323         if(this.isInline) {
20324             return;
20325         }
20326         this.picker().hide();
20327         this.viewMode = this.startViewMode;
20328         this.showMode();
20329         
20330         this.fireEvent('hidepopup', this, this.date);
20331         
20332     },
20333     
20334     onMousedown: function(e)
20335     {
20336         e.stopPropagation();
20337         e.preventDefault();
20338     },
20339     
20340     keyup: function(e)
20341     {
20342         Roo.bootstrap.DateField.superclass.keyup.call(this);
20343         this.update();
20344     },
20345
20346     setValue: function(v)
20347     {
20348         if(this.fireEvent('beforeselect', this, v) !== false){
20349             var d = new Date(this.parseDate(v) ).clearTime();
20350         
20351             if(isNaN(d.getTime())){
20352                 this.date = this.viewDate = '';
20353                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
20354                 return;
20355             }
20356
20357             v = this.formatDate(d);
20358
20359             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
20360
20361             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
20362
20363             this.update();
20364
20365             this.fireEvent('select', this, this.date);
20366         }
20367     },
20368     
20369     getValue: function()
20370     {
20371         return this.formatDate(this.date);
20372     },
20373     
20374     fireKey: function(e)
20375     {
20376         if (!this.picker().isVisible()){
20377             if (e.keyCode == 27) { // allow escape to hide and re-show picker
20378                 this.showPopup();
20379             }
20380             return;
20381         }
20382         
20383         var dateChanged = false,
20384         dir, day, month,
20385         newDate, newViewDate;
20386         
20387         switch(e.keyCode){
20388             case 27: // escape
20389                 this.hidePopup();
20390                 e.preventDefault();
20391                 break;
20392             case 37: // left
20393             case 39: // right
20394                 if (!this.keyboardNavigation) {
20395                     break;
20396                 }
20397                 dir = e.keyCode == 37 ? -1 : 1;
20398                 
20399                 if (e.ctrlKey){
20400                     newDate = this.moveYear(this.date, dir);
20401                     newViewDate = this.moveYear(this.viewDate, dir);
20402                 } else if (e.shiftKey){
20403                     newDate = this.moveMonth(this.date, dir);
20404                     newViewDate = this.moveMonth(this.viewDate, dir);
20405                 } else {
20406                     newDate = new Date(this.date);
20407                     newDate.setUTCDate(this.date.getUTCDate() + dir);
20408                     newViewDate = new Date(this.viewDate);
20409                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
20410                 }
20411                 if (this.dateWithinRange(newDate)){
20412                     this.date = newDate;
20413                     this.viewDate = newViewDate;
20414                     this.setValue(this.formatDate(this.date));
20415 //                    this.update();
20416                     e.preventDefault();
20417                     dateChanged = true;
20418                 }
20419                 break;
20420             case 38: // up
20421             case 40: // down
20422                 if (!this.keyboardNavigation) {
20423                     break;
20424                 }
20425                 dir = e.keyCode == 38 ? -1 : 1;
20426                 if (e.ctrlKey){
20427                     newDate = this.moveYear(this.date, dir);
20428                     newViewDate = this.moveYear(this.viewDate, dir);
20429                 } else if (e.shiftKey){
20430                     newDate = this.moveMonth(this.date, dir);
20431                     newViewDate = this.moveMonth(this.viewDate, dir);
20432                 } else {
20433                     newDate = new Date(this.date);
20434                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
20435                     newViewDate = new Date(this.viewDate);
20436                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
20437                 }
20438                 if (this.dateWithinRange(newDate)){
20439                     this.date = newDate;
20440                     this.viewDate = newViewDate;
20441                     this.setValue(this.formatDate(this.date));
20442 //                    this.update();
20443                     e.preventDefault();
20444                     dateChanged = true;
20445                 }
20446                 break;
20447             case 13: // enter
20448                 this.setValue(this.formatDate(this.date));
20449                 this.hidePopup();
20450                 e.preventDefault();
20451                 break;
20452             case 9: // tab
20453                 this.setValue(this.formatDate(this.date));
20454                 this.hidePopup();
20455                 break;
20456             case 16: // shift
20457             case 17: // ctrl
20458             case 18: // alt
20459                 break;
20460             default :
20461                 this.hidePopup();
20462                 
20463         }
20464     },
20465     
20466     
20467     onClick: function(e) 
20468     {
20469         e.stopPropagation();
20470         e.preventDefault();
20471         
20472         var target = e.getTarget();
20473         
20474         if(target.nodeName.toLowerCase() === 'i'){
20475             target = Roo.get(target).dom.parentNode;
20476         }
20477         
20478         var nodeName = target.nodeName;
20479         var className = target.className;
20480         var html = target.innerHTML;
20481         //Roo.log(nodeName);
20482         
20483         switch(nodeName.toLowerCase()) {
20484             case 'th':
20485                 switch(className) {
20486                     case 'switch':
20487                         this.showMode(1);
20488                         break;
20489                     case 'prev':
20490                     case 'next':
20491                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
20492                         switch(this.viewMode){
20493                                 case 0:
20494                                         this.viewDate = this.moveMonth(this.viewDate, dir);
20495                                         break;
20496                                 case 1:
20497                                 case 2:
20498                                         this.viewDate = this.moveYear(this.viewDate, dir);
20499                                         break;
20500                         }
20501                         this.fill();
20502                         break;
20503                     case 'today':
20504                         var date = new Date();
20505                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
20506 //                        this.fill()
20507                         this.setValue(this.formatDate(this.date));
20508                         
20509                         this.hidePopup();
20510                         break;
20511                 }
20512                 break;
20513             case 'span':
20514                 if (className.indexOf('disabled') < 0) {
20515                     this.viewDate.setUTCDate(1);
20516                     if (className.indexOf('month') > -1) {
20517                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
20518                     } else {
20519                         var year = parseInt(html, 10) || 0;
20520                         this.viewDate.setUTCFullYear(year);
20521                         
20522                     }
20523                     
20524                     if(this.singleMode){
20525                         this.setValue(this.formatDate(this.viewDate));
20526                         this.hidePopup();
20527                         return;
20528                     }
20529                     
20530                     this.showMode(-1);
20531                     this.fill();
20532                 }
20533                 break;
20534                 
20535             case 'td':
20536                 //Roo.log(className);
20537                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
20538                     var day = parseInt(html, 10) || 1;
20539                     var year = this.viewDate.getUTCFullYear(),
20540                         month = this.viewDate.getUTCMonth();
20541
20542                     if (className.indexOf('old') > -1) {
20543                         if(month === 0 ){
20544                             month = 11;
20545                             year -= 1;
20546                         }else{
20547                             month -= 1;
20548                         }
20549                     } else if (className.indexOf('new') > -1) {
20550                         if (month == 11) {
20551                             month = 0;
20552                             year += 1;
20553                         } else {
20554                             month += 1;
20555                         }
20556                     }
20557                     //Roo.log([year,month,day]);
20558                     this.date = this.UTCDate(year, month, day,0,0,0,0);
20559                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
20560 //                    this.fill();
20561                     //Roo.log(this.formatDate(this.date));
20562                     this.setValue(this.formatDate(this.date));
20563                     this.hidePopup();
20564                 }
20565                 break;
20566         }
20567     },
20568     
20569     setStartDate: function(startDate)
20570     {
20571         this.startDate = startDate || -Infinity;
20572         if (this.startDate !== -Infinity) {
20573             this.startDate = this.parseDate(this.startDate);
20574         }
20575         this.update();
20576         this.updateNavArrows();
20577     },
20578
20579     setEndDate: function(endDate)
20580     {
20581         this.endDate = endDate || Infinity;
20582         if (this.endDate !== Infinity) {
20583             this.endDate = this.parseDate(this.endDate);
20584         }
20585         this.update();
20586         this.updateNavArrows();
20587     },
20588     
20589     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
20590     {
20591         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
20592         if (typeof(this.daysOfWeekDisabled) !== 'object') {
20593             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
20594         }
20595         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
20596             return parseInt(d, 10);
20597         });
20598         this.update();
20599         this.updateNavArrows();
20600     },
20601     
20602     updateNavArrows: function() 
20603     {
20604         if(this.singleMode){
20605             return;
20606         }
20607         
20608         var d = new Date(this.viewDate),
20609         year = d.getUTCFullYear(),
20610         month = d.getUTCMonth();
20611         
20612         Roo.each(this.picker().select('.prev', true).elements, function(v){
20613             v.show();
20614             switch (this.viewMode) {
20615                 case 0:
20616
20617                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
20618                         v.hide();
20619                     }
20620                     break;
20621                 case 1:
20622                 case 2:
20623                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
20624                         v.hide();
20625                     }
20626                     break;
20627             }
20628         });
20629         
20630         Roo.each(this.picker().select('.next', true).elements, function(v){
20631             v.show();
20632             switch (this.viewMode) {
20633                 case 0:
20634
20635                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
20636                         v.hide();
20637                     }
20638                     break;
20639                 case 1:
20640                 case 2:
20641                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
20642                         v.hide();
20643                     }
20644                     break;
20645             }
20646         })
20647     },
20648     
20649     moveMonth: function(date, dir)
20650     {
20651         if (!dir) {
20652             return date;
20653         }
20654         var new_date = new Date(date.valueOf()),
20655         day = new_date.getUTCDate(),
20656         month = new_date.getUTCMonth(),
20657         mag = Math.abs(dir),
20658         new_month, test;
20659         dir = dir > 0 ? 1 : -1;
20660         if (mag == 1){
20661             test = dir == -1
20662             // If going back one month, make sure month is not current month
20663             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
20664             ? function(){
20665                 return new_date.getUTCMonth() == month;
20666             }
20667             // If going forward one month, make sure month is as expected
20668             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
20669             : function(){
20670                 return new_date.getUTCMonth() != new_month;
20671             };
20672             new_month = month + dir;
20673             new_date.setUTCMonth(new_month);
20674             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
20675             if (new_month < 0 || new_month > 11) {
20676                 new_month = (new_month + 12) % 12;
20677             }
20678         } else {
20679             // For magnitudes >1, move one month at a time...
20680             for (var i=0; i<mag; i++) {
20681                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
20682                 new_date = this.moveMonth(new_date, dir);
20683             }
20684             // ...then reset the day, keeping it in the new month
20685             new_month = new_date.getUTCMonth();
20686             new_date.setUTCDate(day);
20687             test = function(){
20688                 return new_month != new_date.getUTCMonth();
20689             };
20690         }
20691         // Common date-resetting loop -- if date is beyond end of month, make it
20692         // end of month
20693         while (test()){
20694             new_date.setUTCDate(--day);
20695             new_date.setUTCMonth(new_month);
20696         }
20697         return new_date;
20698     },
20699
20700     moveYear: function(date, dir)
20701     {
20702         return this.moveMonth(date, dir*12);
20703     },
20704
20705     dateWithinRange: function(date)
20706     {
20707         return date >= this.startDate && date <= this.endDate;
20708     },
20709
20710     
20711     remove: function() 
20712     {
20713         this.picker().remove();
20714     },
20715     
20716     validateValue : function(value)
20717     {
20718         if(this.getVisibilityEl().hasClass('hidden')){
20719             return true;
20720         }
20721         
20722         if(value.length < 1)  {
20723             if(this.allowBlank){
20724                 return true;
20725             }
20726             return false;
20727         }
20728         
20729         if(value.length < this.minLength){
20730             return false;
20731         }
20732         if(value.length > this.maxLength){
20733             return false;
20734         }
20735         if(this.vtype){
20736             var vt = Roo.form.VTypes;
20737             if(!vt[this.vtype](value, this)){
20738                 return false;
20739             }
20740         }
20741         if(typeof this.validator == "function"){
20742             var msg = this.validator(value);
20743             if(msg !== true){
20744                 return false;
20745             }
20746         }
20747         
20748         if(this.regex && !this.regex.test(value)){
20749             return false;
20750         }
20751         
20752         if(typeof(this.parseDate(value)) == 'undefined'){
20753             return false;
20754         }
20755         
20756         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
20757             return false;
20758         }      
20759         
20760         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
20761             return false;
20762         } 
20763         
20764         
20765         return true;
20766     },
20767     
20768     reset : function()
20769     {
20770         this.date = this.viewDate = '';
20771         
20772         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
20773     }
20774    
20775 });
20776
20777 Roo.apply(Roo.bootstrap.DateField,  {
20778     
20779     head : {
20780         tag: 'thead',
20781         cn: [
20782         {
20783             tag: 'tr',
20784             cn: [
20785             {
20786                 tag: 'th',
20787                 cls: 'prev',
20788                 html: '<i class="fa fa-arrow-left"/>'
20789             },
20790             {
20791                 tag: 'th',
20792                 cls: 'switch',
20793                 colspan: '5'
20794             },
20795             {
20796                 tag: 'th',
20797                 cls: 'next',
20798                 html: '<i class="fa fa-arrow-right"/>'
20799             }
20800
20801             ]
20802         }
20803         ]
20804     },
20805     
20806     content : {
20807         tag: 'tbody',
20808         cn: [
20809         {
20810             tag: 'tr',
20811             cn: [
20812             {
20813                 tag: 'td',
20814                 colspan: '7'
20815             }
20816             ]
20817         }
20818         ]
20819     },
20820     
20821     footer : {
20822         tag: 'tfoot',
20823         cn: [
20824         {
20825             tag: 'tr',
20826             cn: [
20827             {
20828                 tag: 'th',
20829                 colspan: '7',
20830                 cls: 'today'
20831             }
20832                     
20833             ]
20834         }
20835         ]
20836     },
20837     
20838     dates:{
20839         en: {
20840             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
20841             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
20842             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
20843             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20844             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
20845             today: "Today"
20846         }
20847     },
20848     
20849     modes: [
20850     {
20851         clsName: 'days',
20852         navFnc: 'Month',
20853         navStep: 1
20854     },
20855     {
20856         clsName: 'months',
20857         navFnc: 'FullYear',
20858         navStep: 1
20859     },
20860     {
20861         clsName: 'years',
20862         navFnc: 'FullYear',
20863         navStep: 10
20864     }]
20865 });
20866
20867 Roo.apply(Roo.bootstrap.DateField,  {
20868   
20869     template : {
20870         tag: 'div',
20871         cls: 'datepicker dropdown-menu roo-dynamic',
20872         cn: [
20873         {
20874             tag: 'div',
20875             cls: 'datepicker-days',
20876             cn: [
20877             {
20878                 tag: 'table',
20879                 cls: 'table-condensed',
20880                 cn:[
20881                 Roo.bootstrap.DateField.head,
20882                 {
20883                     tag: 'tbody'
20884                 },
20885                 Roo.bootstrap.DateField.footer
20886                 ]
20887             }
20888             ]
20889         },
20890         {
20891             tag: 'div',
20892             cls: 'datepicker-months',
20893             cn: [
20894             {
20895                 tag: 'table',
20896                 cls: 'table-condensed',
20897                 cn:[
20898                 Roo.bootstrap.DateField.head,
20899                 Roo.bootstrap.DateField.content,
20900                 Roo.bootstrap.DateField.footer
20901                 ]
20902             }
20903             ]
20904         },
20905         {
20906             tag: 'div',
20907             cls: 'datepicker-years',
20908             cn: [
20909             {
20910                 tag: 'table',
20911                 cls: 'table-condensed',
20912                 cn:[
20913                 Roo.bootstrap.DateField.head,
20914                 Roo.bootstrap.DateField.content,
20915                 Roo.bootstrap.DateField.footer
20916                 ]
20917             }
20918             ]
20919         }
20920         ]
20921     }
20922 });
20923
20924  
20925
20926  /*
20927  * - LGPL
20928  *
20929  * TimeField
20930  * 
20931  */
20932
20933 /**
20934  * @class Roo.bootstrap.TimeField
20935  * @extends Roo.bootstrap.Input
20936  * Bootstrap DateField class
20937  * 
20938  * 
20939  * @constructor
20940  * Create a new TimeField
20941  * @param {Object} config The config object
20942  */
20943
20944 Roo.bootstrap.TimeField = function(config){
20945     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
20946     this.addEvents({
20947             /**
20948              * @event show
20949              * Fires when this field show.
20950              * @param {Roo.bootstrap.DateField} thisthis
20951              * @param {Mixed} date The date value
20952              */
20953             show : true,
20954             /**
20955              * @event show
20956              * Fires when this field hide.
20957              * @param {Roo.bootstrap.DateField} this
20958              * @param {Mixed} date The date value
20959              */
20960             hide : true,
20961             /**
20962              * @event select
20963              * Fires when select a date.
20964              * @param {Roo.bootstrap.DateField} this
20965              * @param {Mixed} date The date value
20966              */
20967             select : true
20968         });
20969 };
20970
20971 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
20972     
20973     /**
20974      * @cfg {String} format
20975      * The default time format string which can be overriden for localization support.  The format must be
20976      * valid according to {@link Date#parseDate} (defaults to 'H:i').
20977      */
20978     format : "H:i",
20979        
20980     onRender: function(ct, position)
20981     {
20982         
20983         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
20984                 
20985         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
20986         
20987         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20988         
20989         this.pop = this.picker().select('>.datepicker-time',true).first();
20990         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20991         
20992         this.picker().on('mousedown', this.onMousedown, this);
20993         this.picker().on('click', this.onClick, this);
20994         
20995         this.picker().addClass('datepicker-dropdown');
20996     
20997         this.fillTime();
20998         this.update();
20999             
21000         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
21001         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
21002         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
21003         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
21004         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
21005         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
21006
21007     },
21008     
21009     fireKey: function(e){
21010         if (!this.picker().isVisible()){
21011             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21012                 this.show();
21013             }
21014             return;
21015         }
21016
21017         e.preventDefault();
21018         
21019         switch(e.keyCode){
21020             case 27: // escape
21021                 this.hide();
21022                 break;
21023             case 37: // left
21024             case 39: // right
21025                 this.onTogglePeriod();
21026                 break;
21027             case 38: // up
21028                 this.onIncrementMinutes();
21029                 break;
21030             case 40: // down
21031                 this.onDecrementMinutes();
21032                 break;
21033             case 13: // enter
21034             case 9: // tab
21035                 this.setTime();
21036                 break;
21037         }
21038     },
21039     
21040     onClick: function(e) {
21041         e.stopPropagation();
21042         e.preventDefault();
21043     },
21044     
21045     picker : function()
21046     {
21047         return this.el.select('.datepicker', true).first();
21048     },
21049     
21050     fillTime: function()
21051     {    
21052         var time = this.pop.select('tbody', true).first();
21053         
21054         time.dom.innerHTML = '';
21055         
21056         time.createChild({
21057             tag: 'tr',
21058             cn: [
21059                 {
21060                     tag: 'td',
21061                     cn: [
21062                         {
21063                             tag: 'a',
21064                             href: '#',
21065                             cls: 'btn',
21066                             cn: [
21067                                 {
21068                                     tag: 'span',
21069                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
21070                                 }
21071                             ]
21072                         } 
21073                     ]
21074                 },
21075                 {
21076                     tag: 'td',
21077                     cls: 'separator'
21078                 },
21079                 {
21080                     tag: 'td',
21081                     cn: [
21082                         {
21083                             tag: 'a',
21084                             href: '#',
21085                             cls: 'btn',
21086                             cn: [
21087                                 {
21088                                     tag: 'span',
21089                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
21090                                 }
21091                             ]
21092                         }
21093                     ]
21094                 },
21095                 {
21096                     tag: 'td',
21097                     cls: 'separator'
21098                 }
21099             ]
21100         });
21101         
21102         time.createChild({
21103             tag: 'tr',
21104             cn: [
21105                 {
21106                     tag: 'td',
21107                     cn: [
21108                         {
21109                             tag: 'span',
21110                             cls: 'timepicker-hour',
21111                             html: '00'
21112                         }  
21113                     ]
21114                 },
21115                 {
21116                     tag: 'td',
21117                     cls: 'separator',
21118                     html: ':'
21119                 },
21120                 {
21121                     tag: 'td',
21122                     cn: [
21123                         {
21124                             tag: 'span',
21125                             cls: 'timepicker-minute',
21126                             html: '00'
21127                         }  
21128                     ]
21129                 },
21130                 {
21131                     tag: 'td',
21132                     cls: 'separator'
21133                 },
21134                 {
21135                     tag: 'td',
21136                     cn: [
21137                         {
21138                             tag: 'button',
21139                             type: 'button',
21140                             cls: 'btn btn-primary period',
21141                             html: 'AM'
21142                             
21143                         }
21144                     ]
21145                 }
21146             ]
21147         });
21148         
21149         time.createChild({
21150             tag: 'tr',
21151             cn: [
21152                 {
21153                     tag: 'td',
21154                     cn: [
21155                         {
21156                             tag: 'a',
21157                             href: '#',
21158                             cls: 'btn',
21159                             cn: [
21160                                 {
21161                                     tag: 'span',
21162                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
21163                                 }
21164                             ]
21165                         }
21166                     ]
21167                 },
21168                 {
21169                     tag: 'td',
21170                     cls: 'separator'
21171                 },
21172                 {
21173                     tag: 'td',
21174                     cn: [
21175                         {
21176                             tag: 'a',
21177                             href: '#',
21178                             cls: 'btn',
21179                             cn: [
21180                                 {
21181                                     tag: 'span',
21182                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
21183                                 }
21184                             ]
21185                         }
21186                     ]
21187                 },
21188                 {
21189                     tag: 'td',
21190                     cls: 'separator'
21191                 }
21192             ]
21193         });
21194         
21195     },
21196     
21197     update: function()
21198     {
21199         
21200         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
21201         
21202         this.fill();
21203     },
21204     
21205     fill: function() 
21206     {
21207         var hours = this.time.getHours();
21208         var minutes = this.time.getMinutes();
21209         var period = 'AM';
21210         
21211         if(hours > 11){
21212             period = 'PM';
21213         }
21214         
21215         if(hours == 0){
21216             hours = 12;
21217         }
21218         
21219         
21220         if(hours > 12){
21221             hours = hours - 12;
21222         }
21223         
21224         if(hours < 10){
21225             hours = '0' + hours;
21226         }
21227         
21228         if(minutes < 10){
21229             minutes = '0' + minutes;
21230         }
21231         
21232         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
21233         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
21234         this.pop.select('button', true).first().dom.innerHTML = period;
21235         
21236     },
21237     
21238     place: function()
21239     {   
21240         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
21241         
21242         var cls = ['bottom'];
21243         
21244         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
21245             cls.pop();
21246             cls.push('top');
21247         }
21248         
21249         cls.push('right');
21250         
21251         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
21252             cls.pop();
21253             cls.push('left');
21254         }
21255         
21256         this.picker().addClass(cls.join('-'));
21257         
21258         var _this = this;
21259         
21260         Roo.each(cls, function(c){
21261             if(c == 'bottom'){
21262                 _this.picker().setTop(_this.inputEl().getHeight());
21263                 return;
21264             }
21265             if(c == 'top'){
21266                 _this.picker().setTop(0 - _this.picker().getHeight());
21267                 return;
21268             }
21269             
21270             if(c == 'left'){
21271                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
21272                 return;
21273             }
21274             if(c == 'right'){
21275                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
21276                 return;
21277             }
21278         });
21279         
21280     },
21281   
21282     onFocus : function()
21283     {
21284         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
21285         this.show();
21286     },
21287     
21288     onBlur : function()
21289     {
21290         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
21291         this.hide();
21292     },
21293     
21294     show : function()
21295     {
21296         this.picker().show();
21297         this.pop.show();
21298         this.update();
21299         this.place();
21300         
21301         this.fireEvent('show', this, this.date);
21302     },
21303     
21304     hide : function()
21305     {
21306         this.picker().hide();
21307         this.pop.hide();
21308         
21309         this.fireEvent('hide', this, this.date);
21310     },
21311     
21312     setTime : function()
21313     {
21314         this.hide();
21315         this.setValue(this.time.format(this.format));
21316         
21317         this.fireEvent('select', this, this.date);
21318         
21319         
21320     },
21321     
21322     onMousedown: function(e){
21323         e.stopPropagation();
21324         e.preventDefault();
21325     },
21326     
21327     onIncrementHours: function()
21328     {
21329         Roo.log('onIncrementHours');
21330         this.time = this.time.add(Date.HOUR, 1);
21331         this.update();
21332         
21333     },
21334     
21335     onDecrementHours: function()
21336     {
21337         Roo.log('onDecrementHours');
21338         this.time = this.time.add(Date.HOUR, -1);
21339         this.update();
21340     },
21341     
21342     onIncrementMinutes: function()
21343     {
21344         Roo.log('onIncrementMinutes');
21345         this.time = this.time.add(Date.MINUTE, 1);
21346         this.update();
21347     },
21348     
21349     onDecrementMinutes: function()
21350     {
21351         Roo.log('onDecrementMinutes');
21352         this.time = this.time.add(Date.MINUTE, -1);
21353         this.update();
21354     },
21355     
21356     onTogglePeriod: function()
21357     {
21358         Roo.log('onTogglePeriod');
21359         this.time = this.time.add(Date.HOUR, 12);
21360         this.update();
21361     }
21362     
21363    
21364 });
21365
21366 Roo.apply(Roo.bootstrap.TimeField,  {
21367     
21368     content : {
21369         tag: 'tbody',
21370         cn: [
21371             {
21372                 tag: 'tr',
21373                 cn: [
21374                 {
21375                     tag: 'td',
21376                     colspan: '7'
21377                 }
21378                 ]
21379             }
21380         ]
21381     },
21382     
21383     footer : {
21384         tag: 'tfoot',
21385         cn: [
21386             {
21387                 tag: 'tr',
21388                 cn: [
21389                 {
21390                     tag: 'th',
21391                     colspan: '7',
21392                     cls: '',
21393                     cn: [
21394                         {
21395                             tag: 'button',
21396                             cls: 'btn btn-info ok',
21397                             html: 'OK'
21398                         }
21399                     ]
21400                 }
21401
21402                 ]
21403             }
21404         ]
21405     }
21406 });
21407
21408 Roo.apply(Roo.bootstrap.TimeField,  {
21409   
21410     template : {
21411         tag: 'div',
21412         cls: 'datepicker dropdown-menu',
21413         cn: [
21414             {
21415                 tag: 'div',
21416                 cls: 'datepicker-time',
21417                 cn: [
21418                 {
21419                     tag: 'table',
21420                     cls: 'table-condensed',
21421                     cn:[
21422                     Roo.bootstrap.TimeField.content,
21423                     Roo.bootstrap.TimeField.footer
21424                     ]
21425                 }
21426                 ]
21427             }
21428         ]
21429     }
21430 });
21431
21432  
21433
21434  /*
21435  * - LGPL
21436  *
21437  * MonthField
21438  * 
21439  */
21440
21441 /**
21442  * @class Roo.bootstrap.MonthField
21443  * @extends Roo.bootstrap.Input
21444  * Bootstrap MonthField class
21445  * 
21446  * @cfg {String} language default en
21447  * 
21448  * @constructor
21449  * Create a new MonthField
21450  * @param {Object} config The config object
21451  */
21452
21453 Roo.bootstrap.MonthField = function(config){
21454     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
21455     
21456     this.addEvents({
21457         /**
21458          * @event show
21459          * Fires when this field show.
21460          * @param {Roo.bootstrap.MonthField} this
21461          * @param {Mixed} date The date value
21462          */
21463         show : true,
21464         /**
21465          * @event show
21466          * Fires when this field hide.
21467          * @param {Roo.bootstrap.MonthField} this
21468          * @param {Mixed} date The date value
21469          */
21470         hide : true,
21471         /**
21472          * @event select
21473          * Fires when select a date.
21474          * @param {Roo.bootstrap.MonthField} this
21475          * @param {String} oldvalue The old value
21476          * @param {String} newvalue The new value
21477          */
21478         select : true
21479     });
21480 };
21481
21482 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
21483     
21484     onRender: function(ct, position)
21485     {
21486         
21487         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
21488         
21489         this.language = this.language || 'en';
21490         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
21491         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
21492         
21493         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
21494         this.isInline = false;
21495         this.isInput = true;
21496         this.component = this.el.select('.add-on', true).first() || false;
21497         this.component = (this.component && this.component.length === 0) ? false : this.component;
21498         this.hasInput = this.component && this.inputEL().length;
21499         
21500         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
21501         
21502         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21503         
21504         this.picker().on('mousedown', this.onMousedown, this);
21505         this.picker().on('click', this.onClick, this);
21506         
21507         this.picker().addClass('datepicker-dropdown');
21508         
21509         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21510             v.setStyle('width', '189px');
21511         });
21512         
21513         this.fillMonths();
21514         
21515         this.update();
21516         
21517         if(this.isInline) {
21518             this.show();
21519         }
21520         
21521     },
21522     
21523     setValue: function(v, suppressEvent)
21524     {   
21525         var o = this.getValue();
21526         
21527         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
21528         
21529         this.update();
21530
21531         if(suppressEvent !== true){
21532             this.fireEvent('select', this, o, v);
21533         }
21534         
21535     },
21536     
21537     getValue: function()
21538     {
21539         return this.value;
21540     },
21541     
21542     onClick: function(e) 
21543     {
21544         e.stopPropagation();
21545         e.preventDefault();
21546         
21547         var target = e.getTarget();
21548         
21549         if(target.nodeName.toLowerCase() === 'i'){
21550             target = Roo.get(target).dom.parentNode;
21551         }
21552         
21553         var nodeName = target.nodeName;
21554         var className = target.className;
21555         var html = target.innerHTML;
21556         
21557         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
21558             return;
21559         }
21560         
21561         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
21562         
21563         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21564         
21565         this.hide();
21566                         
21567     },
21568     
21569     picker : function()
21570     {
21571         return this.pickerEl;
21572     },
21573     
21574     fillMonths: function()
21575     {    
21576         var i = 0;
21577         var months = this.picker().select('>.datepicker-months td', true).first();
21578         
21579         months.dom.innerHTML = '';
21580         
21581         while (i < 12) {
21582             var month = {
21583                 tag: 'span',
21584                 cls: 'month',
21585                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
21586             };
21587             
21588             months.createChild(month);
21589         }
21590         
21591     },
21592     
21593     update: function()
21594     {
21595         var _this = this;
21596         
21597         if(typeof(this.vIndex) == 'undefined' && this.value.length){
21598             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
21599         }
21600         
21601         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
21602             e.removeClass('active');
21603             
21604             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
21605                 e.addClass('active');
21606             }
21607         })
21608     },
21609     
21610     place: function()
21611     {
21612         if(this.isInline) {
21613             return;
21614         }
21615         
21616         this.picker().removeClass(['bottom', 'top']);
21617         
21618         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21619             /*
21620              * place to the top of element!
21621              *
21622              */
21623             
21624             this.picker().addClass('top');
21625             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21626             
21627             return;
21628         }
21629         
21630         this.picker().addClass('bottom');
21631         
21632         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21633     },
21634     
21635     onFocus : function()
21636     {
21637         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
21638         this.show();
21639     },
21640     
21641     onBlur : function()
21642     {
21643         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
21644         
21645         var d = this.inputEl().getValue();
21646         
21647         this.setValue(d);
21648                 
21649         this.hide();
21650     },
21651     
21652     show : function()
21653     {
21654         this.picker().show();
21655         this.picker().select('>.datepicker-months', true).first().show();
21656         this.update();
21657         this.place();
21658         
21659         this.fireEvent('show', this, this.date);
21660     },
21661     
21662     hide : function()
21663     {
21664         if(this.isInline) {
21665             return;
21666         }
21667         this.picker().hide();
21668         this.fireEvent('hide', this, this.date);
21669         
21670     },
21671     
21672     onMousedown: function(e)
21673     {
21674         e.stopPropagation();
21675         e.preventDefault();
21676     },
21677     
21678     keyup: function(e)
21679     {
21680         Roo.bootstrap.MonthField.superclass.keyup.call(this);
21681         this.update();
21682     },
21683
21684     fireKey: function(e)
21685     {
21686         if (!this.picker().isVisible()){
21687             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
21688                 this.show();
21689             }
21690             return;
21691         }
21692         
21693         var dir;
21694         
21695         switch(e.keyCode){
21696             case 27: // escape
21697                 this.hide();
21698                 e.preventDefault();
21699                 break;
21700             case 37: // left
21701             case 39: // right
21702                 dir = e.keyCode == 37 ? -1 : 1;
21703                 
21704                 this.vIndex = this.vIndex + dir;
21705                 
21706                 if(this.vIndex < 0){
21707                     this.vIndex = 0;
21708                 }
21709                 
21710                 if(this.vIndex > 11){
21711                     this.vIndex = 11;
21712                 }
21713                 
21714                 if(isNaN(this.vIndex)){
21715                     this.vIndex = 0;
21716                 }
21717                 
21718                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21719                 
21720                 break;
21721             case 38: // up
21722             case 40: // down
21723                 
21724                 dir = e.keyCode == 38 ? -1 : 1;
21725                 
21726                 this.vIndex = this.vIndex + dir * 4;
21727                 
21728                 if(this.vIndex < 0){
21729                     this.vIndex = 0;
21730                 }
21731                 
21732                 if(this.vIndex > 11){
21733                     this.vIndex = 11;
21734                 }
21735                 
21736                 if(isNaN(this.vIndex)){
21737                     this.vIndex = 0;
21738                 }
21739                 
21740                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21741                 break;
21742                 
21743             case 13: // enter
21744                 
21745                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
21746                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21747                 }
21748                 
21749                 this.hide();
21750                 e.preventDefault();
21751                 break;
21752             case 9: // tab
21753                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
21754                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21755                 }
21756                 this.hide();
21757                 break;
21758             case 16: // shift
21759             case 17: // ctrl
21760             case 18: // alt
21761                 break;
21762             default :
21763                 this.hide();
21764                 
21765         }
21766     },
21767     
21768     remove: function() 
21769     {
21770         this.picker().remove();
21771     }
21772    
21773 });
21774
21775 Roo.apply(Roo.bootstrap.MonthField,  {
21776     
21777     content : {
21778         tag: 'tbody',
21779         cn: [
21780         {
21781             tag: 'tr',
21782             cn: [
21783             {
21784                 tag: 'td',
21785                 colspan: '7'
21786             }
21787             ]
21788         }
21789         ]
21790     },
21791     
21792     dates:{
21793         en: {
21794             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21795             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
21796         }
21797     }
21798 });
21799
21800 Roo.apply(Roo.bootstrap.MonthField,  {
21801   
21802     template : {
21803         tag: 'div',
21804         cls: 'datepicker dropdown-menu roo-dynamic',
21805         cn: [
21806             {
21807                 tag: 'div',
21808                 cls: 'datepicker-months',
21809                 cn: [
21810                 {
21811                     tag: 'table',
21812                     cls: 'table-condensed',
21813                     cn:[
21814                         Roo.bootstrap.DateField.content
21815                     ]
21816                 }
21817                 ]
21818             }
21819         ]
21820     }
21821 });
21822
21823  
21824
21825  
21826  /*
21827  * - LGPL
21828  *
21829  * CheckBox
21830  * 
21831  */
21832
21833 /**
21834  * @class Roo.bootstrap.CheckBox
21835  * @extends Roo.bootstrap.Input
21836  * Bootstrap CheckBox class
21837  * 
21838  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
21839  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
21840  * @cfg {String} boxLabel The text that appears beside the checkbox
21841  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
21842  * @cfg {Boolean} checked initnal the element
21843  * @cfg {Boolean} inline inline the element (default false)
21844  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
21845  * @cfg {String} tooltip label tooltip
21846  * 
21847  * @constructor
21848  * Create a new CheckBox
21849  * @param {Object} config The config object
21850  */
21851
21852 Roo.bootstrap.CheckBox = function(config){
21853     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
21854    
21855     this.addEvents({
21856         /**
21857         * @event check
21858         * Fires when the element is checked or unchecked.
21859         * @param {Roo.bootstrap.CheckBox} this This input
21860         * @param {Boolean} checked The new checked value
21861         */
21862        check : true,
21863        /**
21864         * @event click
21865         * Fires when the element is click.
21866         * @param {Roo.bootstrap.CheckBox} this This input
21867         */
21868        click : true
21869     });
21870     
21871 };
21872
21873 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
21874   
21875     inputType: 'checkbox',
21876     inputValue: 1,
21877     valueOff: 0,
21878     boxLabel: false,
21879     checked: false,
21880     weight : false,
21881     inline: false,
21882     tooltip : '',
21883     
21884     // checkbox success does not make any sense really.. 
21885     invalidClass : "",
21886     validClass : "",
21887     
21888     
21889     getAutoCreate : function()
21890     {
21891         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
21892         
21893         var id = Roo.id();
21894         
21895         var cfg = {};
21896         
21897         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
21898         
21899         if(this.inline){
21900             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
21901         }
21902         
21903         var input =  {
21904             tag: 'input',
21905             id : id,
21906             type : this.inputType,
21907             value : this.inputValue,
21908             cls : 'roo-' + this.inputType, //'form-box',
21909             placeholder : this.placeholder || ''
21910             
21911         };
21912         
21913         if(this.inputType != 'radio'){
21914             var hidden =  {
21915                 tag: 'input',
21916                 type : 'hidden',
21917                 cls : 'roo-hidden-value',
21918                 value : this.checked ? this.inputValue : this.valueOff
21919             };
21920         }
21921         
21922             
21923         if (this.weight) { // Validity check?
21924             cfg.cls += " " + this.inputType + "-" + this.weight;
21925         }
21926         
21927         if (this.disabled) {
21928             input.disabled=true;
21929         }
21930         
21931         if(this.checked){
21932             input.checked = this.checked;
21933         }
21934         
21935         if (this.name) {
21936             
21937             input.name = this.name;
21938             
21939             if(this.inputType != 'radio'){
21940                 hidden.name = this.name;
21941                 input.name = '_hidden_' + this.name;
21942             }
21943         }
21944         
21945         if (this.size) {
21946             input.cls += ' input-' + this.size;
21947         }
21948         
21949         var settings=this;
21950         
21951         ['xs','sm','md','lg'].map(function(size){
21952             if (settings[size]) {
21953                 cfg.cls += ' col-' + size + '-' + settings[size];
21954             }
21955         });
21956         
21957         var inputblock = input;
21958          
21959         if (this.before || this.after) {
21960             
21961             inputblock = {
21962                 cls : 'input-group',
21963                 cn :  [] 
21964             };
21965             
21966             if (this.before) {
21967                 inputblock.cn.push({
21968                     tag :'span',
21969                     cls : 'input-group-addon',
21970                     html : this.before
21971                 });
21972             }
21973             
21974             inputblock.cn.push(input);
21975             
21976             if(this.inputType != 'radio'){
21977                 inputblock.cn.push(hidden);
21978             }
21979             
21980             if (this.after) {
21981                 inputblock.cn.push({
21982                     tag :'span',
21983                     cls : 'input-group-addon',
21984                     html : this.after
21985                 });
21986             }
21987             
21988         }
21989         var boxLabelCfg = false;
21990         
21991         if(this.boxLabel){
21992            
21993             boxLabelCfg = {
21994                 tag: 'label',
21995                 //'for': id, // box label is handled by onclick - so no for...
21996                 cls: 'box-label',
21997                 html: this.boxLabel
21998             };
21999             if(this.tooltip){
22000                 boxLabelCfg.tooltip = this.tooltip;
22001             }
22002              
22003         }
22004         
22005         
22006         if (align ==='left' && this.fieldLabel.length) {
22007 //                Roo.log("left and has label");
22008             cfg.cn = [
22009                 {
22010                     tag: 'label',
22011                     'for' :  id,
22012                     cls : 'control-label',
22013                     html : this.fieldLabel
22014                 },
22015                 {
22016                     cls : "", 
22017                     cn: [
22018                         inputblock
22019                     ]
22020                 }
22021             ];
22022             
22023             if (boxLabelCfg) {
22024                 cfg.cn[1].cn.push(boxLabelCfg);
22025             }
22026             
22027             if(this.labelWidth > 12){
22028                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
22029             }
22030             
22031             if(this.labelWidth < 13 && this.labelmd == 0){
22032                 this.labelmd = this.labelWidth;
22033             }
22034             
22035             if(this.labellg > 0){
22036                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
22037                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
22038             }
22039             
22040             if(this.labelmd > 0){
22041                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
22042                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
22043             }
22044             
22045             if(this.labelsm > 0){
22046                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
22047                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
22048             }
22049             
22050             if(this.labelxs > 0){
22051                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
22052                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
22053             }
22054             
22055         } else if ( this.fieldLabel.length) {
22056 //                Roo.log(" label");
22057                 cfg.cn = [
22058                    
22059                     {
22060                         tag: this.boxLabel ? 'span' : 'label',
22061                         'for': id,
22062                         cls: 'control-label box-input-label',
22063                         //cls : 'input-group-addon',
22064                         html : this.fieldLabel
22065                     },
22066                     
22067                     inputblock
22068                     
22069                 ];
22070                 if (boxLabelCfg) {
22071                     cfg.cn.push(boxLabelCfg);
22072                 }
22073
22074         } else {
22075             
22076 //                Roo.log(" no label && no align");
22077                 cfg.cn = [  inputblock ] ;
22078                 if (boxLabelCfg) {
22079                     cfg.cn.push(boxLabelCfg);
22080                 }
22081
22082                 
22083         }
22084         
22085        
22086         
22087         if(this.inputType != 'radio'){
22088             cfg.cn.push(hidden);
22089         }
22090         
22091         return cfg;
22092         
22093     },
22094     
22095     /**
22096      * return the real input element.
22097      */
22098     inputEl: function ()
22099     {
22100         return this.el.select('input.roo-' + this.inputType,true).first();
22101     },
22102     hiddenEl: function ()
22103     {
22104         return this.el.select('input.roo-hidden-value',true).first();
22105     },
22106     
22107     labelEl: function()
22108     {
22109         return this.el.select('label.control-label',true).first();
22110     },
22111     /* depricated... */
22112     
22113     label: function()
22114     {
22115         return this.labelEl();
22116     },
22117     
22118     boxLabelEl: function()
22119     {
22120         return this.el.select('label.box-label',true).first();
22121     },
22122     
22123     initEvents : function()
22124     {
22125 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
22126         
22127         this.inputEl().on('click', this.onClick,  this);
22128         
22129         if (this.boxLabel) { 
22130             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
22131         }
22132         
22133         this.startValue = this.getValue();
22134         
22135         if(this.groupId){
22136             Roo.bootstrap.CheckBox.register(this);
22137         }
22138     },
22139     
22140     onClick : function(e)
22141     {   
22142         if(this.fireEvent('click', this, e) !== false){
22143             this.setChecked(!this.checked);
22144         }
22145         
22146     },
22147     
22148     setChecked : function(state,suppressEvent)
22149     {
22150         this.startValue = this.getValue();
22151
22152         if(this.inputType == 'radio'){
22153             
22154             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22155                 e.dom.checked = false;
22156             });
22157             
22158             this.inputEl().dom.checked = true;
22159             
22160             this.inputEl().dom.value = this.inputValue;
22161             
22162             if(suppressEvent !== true){
22163                 this.fireEvent('check', this, true);
22164             }
22165             
22166             this.validate();
22167             
22168             return;
22169         }
22170         
22171         this.checked = state;
22172         
22173         this.inputEl().dom.checked = state;
22174         
22175         
22176         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
22177         
22178         if(suppressEvent !== true){
22179             this.fireEvent('check', this, state);
22180         }
22181         
22182         this.validate();
22183     },
22184     
22185     getValue : function()
22186     {
22187         if(this.inputType == 'radio'){
22188             return this.getGroupValue();
22189         }
22190         
22191         return this.hiddenEl().dom.value;
22192         
22193     },
22194     
22195     getGroupValue : function()
22196     {
22197         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
22198             return '';
22199         }
22200         
22201         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
22202     },
22203     
22204     setValue : function(v,suppressEvent)
22205     {
22206         if(this.inputType == 'radio'){
22207             this.setGroupValue(v, suppressEvent);
22208             return;
22209         }
22210         
22211         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
22212         
22213         this.validate();
22214     },
22215     
22216     setGroupValue : function(v, suppressEvent)
22217     {
22218         this.startValue = this.getValue();
22219         
22220         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22221             e.dom.checked = false;
22222             
22223             if(e.dom.value == v){
22224                 e.dom.checked = true;
22225             }
22226         });
22227         
22228         if(suppressEvent !== true){
22229             this.fireEvent('check', this, true);
22230         }
22231
22232         this.validate();
22233         
22234         return;
22235     },
22236     
22237     validate : function()
22238     {
22239         if(this.getVisibilityEl().hasClass('hidden')){
22240             return true;
22241         }
22242         
22243         if(
22244                 this.disabled || 
22245                 (this.inputType == 'radio' && this.validateRadio()) ||
22246                 (this.inputType == 'checkbox' && this.validateCheckbox())
22247         ){
22248             this.markValid();
22249             return true;
22250         }
22251         
22252         this.markInvalid();
22253         return false;
22254     },
22255     
22256     validateRadio : function()
22257     {
22258         if(this.getVisibilityEl().hasClass('hidden')){
22259             return true;
22260         }
22261         
22262         if(this.allowBlank){
22263             return true;
22264         }
22265         
22266         var valid = false;
22267         
22268         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22269             if(!e.dom.checked){
22270                 return;
22271             }
22272             
22273             valid = true;
22274             
22275             return false;
22276         });
22277         
22278         return valid;
22279     },
22280     
22281     validateCheckbox : function()
22282     {
22283         if(!this.groupId){
22284             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
22285             //return (this.getValue() == this.inputValue) ? true : false;
22286         }
22287         
22288         var group = Roo.bootstrap.CheckBox.get(this.groupId);
22289         
22290         if(!group){
22291             return false;
22292         }
22293         
22294         var r = false;
22295         
22296         for(var i in group){
22297             if(group[i].el.isVisible(true)){
22298                 r = false;
22299                 break;
22300             }
22301             
22302             r = true;
22303         }
22304         
22305         for(var i in group){
22306             if(r){
22307                 break;
22308             }
22309             
22310             r = (group[i].getValue() == group[i].inputValue) ? true : false;
22311         }
22312         
22313         return r;
22314     },
22315     
22316     /**
22317      * Mark this field as valid
22318      */
22319     markValid : function()
22320     {
22321         var _this = this;
22322         
22323         this.fireEvent('valid', this);
22324         
22325         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
22326         
22327         if(this.groupId){
22328             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
22329         }
22330         
22331         if(label){
22332             label.markValid();
22333         }
22334
22335         if(this.inputType == 'radio'){
22336             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22337                 var fg = e.findParent('.form-group', false, true);
22338                 if (Roo.bootstrap.version == 3) {
22339                     fg.removeClass([_this.invalidClass, _this.validClass]);
22340                     fg.addClass(_this.validClass);
22341                 } else {
22342                     fg.removeClass(['is-valid', 'is-invalid']);
22343                     fg.addClass('is-valid');
22344                 }
22345             });
22346             
22347             return;
22348         }
22349
22350         if(!this.groupId){
22351             var fg = this.el.findParent('.form-group', false, true);
22352             if (Roo.bootstrap.version == 3) {
22353                 fg.removeClass([this.invalidClass, this.validClass]);
22354                 fg.addClass(this.validClass);
22355             } else {
22356                 fg.removeClass(['is-valid', 'is-invalid']);
22357                 fg.addClass('is-valid');
22358             }
22359             return;
22360         }
22361         
22362         var group = Roo.bootstrap.CheckBox.get(this.groupId);
22363         
22364         if(!group){
22365             return;
22366         }
22367         
22368         for(var i in group){
22369             var fg = group[i].el.findParent('.form-group', false, true);
22370             if (Roo.bootstrap.version == 3) {
22371                 fg.removeClass([this.invalidClass, this.validClass]);
22372                 fg.addClass(this.validClass);
22373             } else {
22374                 fg.removeClass(['is-valid', 'is-invalid']);
22375                 fg.addClass('is-valid');
22376             }
22377         }
22378     },
22379     
22380      /**
22381      * Mark this field as invalid
22382      * @param {String} msg The validation message
22383      */
22384     markInvalid : function(msg)
22385     {
22386         if(this.allowBlank){
22387             return;
22388         }
22389         
22390         var _this = this;
22391         
22392         this.fireEvent('invalid', this, msg);
22393         
22394         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
22395         
22396         if(this.groupId){
22397             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
22398         }
22399         
22400         if(label){
22401             label.markInvalid();
22402         }
22403             
22404         if(this.inputType == 'radio'){
22405             
22406             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22407                 var fg = e.findParent('.form-group', false, true);
22408                 if (Roo.bootstrap.version == 3) {
22409                     fg.removeClass([_this.invalidClass, _this.validClass]);
22410                     fg.addClass(_this.invalidClass);
22411                 } else {
22412                     fg.removeClass(['is-invalid', 'is-valid']);
22413                     fg.addClass('is-invalid');
22414                 }
22415             });
22416             
22417             return;
22418         }
22419         
22420         if(!this.groupId){
22421             var fg = this.el.findParent('.form-group', false, true);
22422             if (Roo.bootstrap.version == 3) {
22423                 fg.removeClass([_this.invalidClass, _this.validClass]);
22424                 fg.addClass(_this.invalidClass);
22425             } else {
22426                 fg.removeClass(['is-invalid', 'is-valid']);
22427                 fg.addClass('is-invalid');
22428             }
22429             return;
22430         }
22431         
22432         var group = Roo.bootstrap.CheckBox.get(this.groupId);
22433         
22434         if(!group){
22435             return;
22436         }
22437         
22438         for(var i in group){
22439             var fg = group[i].el.findParent('.form-group', false, true);
22440             if (Roo.bootstrap.version == 3) {
22441                 fg.removeClass([_this.invalidClass, _this.validClass]);
22442                 fg.addClass(_this.invalidClass);
22443             } else {
22444                 fg.removeClass(['is-invalid', 'is-valid']);
22445                 fg.addClass('is-invalid');
22446             }
22447         }
22448         
22449     },
22450     
22451     clearInvalid : function()
22452     {
22453         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
22454         
22455         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
22456         
22457         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
22458         
22459         if (label && label.iconEl) {
22460             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
22461             label.iconEl.removeClass(['is-invalid', 'is-valid']);
22462         }
22463     },
22464     
22465     disable : function()
22466     {
22467         if(this.inputType != 'radio'){
22468             Roo.bootstrap.CheckBox.superclass.disable.call(this);
22469             return;
22470         }
22471         
22472         var _this = this;
22473         
22474         if(this.rendered){
22475             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22476                 _this.getActionEl().addClass(this.disabledClass);
22477                 e.dom.disabled = true;
22478             });
22479         }
22480         
22481         this.disabled = true;
22482         this.fireEvent("disable", this);
22483         return this;
22484     },
22485
22486     enable : function()
22487     {
22488         if(this.inputType != 'radio'){
22489             Roo.bootstrap.CheckBox.superclass.enable.call(this);
22490             return;
22491         }
22492         
22493         var _this = this;
22494         
22495         if(this.rendered){
22496             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22497                 _this.getActionEl().removeClass(this.disabledClass);
22498                 e.dom.disabled = false;
22499             });
22500         }
22501         
22502         this.disabled = false;
22503         this.fireEvent("enable", this);
22504         return this;
22505     },
22506     
22507     setBoxLabel : function(v)
22508     {
22509         this.boxLabel = v;
22510         
22511         if(this.rendered){
22512             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
22513         }
22514     }
22515
22516 });
22517
22518 Roo.apply(Roo.bootstrap.CheckBox, {
22519     
22520     groups: {},
22521     
22522      /**
22523     * register a CheckBox Group
22524     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
22525     */
22526     register : function(checkbox)
22527     {
22528         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
22529             this.groups[checkbox.groupId] = {};
22530         }
22531         
22532         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
22533             return;
22534         }
22535         
22536         this.groups[checkbox.groupId][checkbox.name] = checkbox;
22537         
22538     },
22539     /**
22540     * fetch a CheckBox Group based on the group ID
22541     * @param {string} the group ID
22542     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
22543     */
22544     get: function(groupId) {
22545         if (typeof(this.groups[groupId]) == 'undefined') {
22546             return false;
22547         }
22548         
22549         return this.groups[groupId] ;
22550     }
22551     
22552     
22553 });
22554 /*
22555  * - LGPL
22556  *
22557  * RadioItem
22558  * 
22559  */
22560
22561 /**
22562  * @class Roo.bootstrap.Radio
22563  * @extends Roo.bootstrap.Component
22564  * Bootstrap Radio class
22565  * @cfg {String} boxLabel - the label associated
22566  * @cfg {String} value - the value of radio
22567  * 
22568  * @constructor
22569  * Create a new Radio
22570  * @param {Object} config The config object
22571  */
22572 Roo.bootstrap.Radio = function(config){
22573     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
22574     
22575 };
22576
22577 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
22578     
22579     boxLabel : '',
22580     
22581     value : '',
22582     
22583     getAutoCreate : function()
22584     {
22585         var cfg = {
22586             tag : 'div',
22587             cls : 'form-group radio',
22588             cn : [
22589                 {
22590                     tag : 'label',
22591                     cls : 'box-label',
22592                     html : this.boxLabel
22593                 }
22594             ]
22595         };
22596         
22597         return cfg;
22598     },
22599     
22600     initEvents : function() 
22601     {
22602         this.parent().register(this);
22603         
22604         this.el.on('click', this.onClick, this);
22605         
22606     },
22607     
22608     onClick : function(e)
22609     {
22610         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
22611             this.setChecked(true);
22612         }
22613     },
22614     
22615     setChecked : function(state, suppressEvent)
22616     {
22617         this.parent().setValue(this.value, suppressEvent);
22618         
22619     },
22620     
22621     setBoxLabel : function(v)
22622     {
22623         this.boxLabel = v;
22624         
22625         if(this.rendered){
22626             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
22627         }
22628     }
22629     
22630 });
22631  
22632
22633  /*
22634  * - LGPL
22635  *
22636  * Input
22637  * 
22638  */
22639
22640 /**
22641  * @class Roo.bootstrap.SecurePass
22642  * @extends Roo.bootstrap.Input
22643  * Bootstrap SecurePass class
22644  *
22645  * 
22646  * @constructor
22647  * Create a new SecurePass
22648  * @param {Object} config The config object
22649  */
22650  
22651 Roo.bootstrap.SecurePass = function (config) {
22652     // these go here, so the translation tool can replace them..
22653     this.errors = {
22654         PwdEmpty: "Please type a password, and then retype it to confirm.",
22655         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
22656         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
22657         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
22658         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
22659         FNInPwd: "Your password can't contain your first name. Please type a different password.",
22660         LNInPwd: "Your password can't contain your last name. Please type a different password.",
22661         TooWeak: "Your password is Too Weak."
22662     },
22663     this.meterLabel = "Password strength:";
22664     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
22665     this.meterClass = [
22666         "roo-password-meter-tooweak", 
22667         "roo-password-meter-weak", 
22668         "roo-password-meter-medium", 
22669         "roo-password-meter-strong", 
22670         "roo-password-meter-grey"
22671     ];
22672     
22673     this.errors = {};
22674     
22675     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
22676 }
22677
22678 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
22679     /**
22680      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
22681      * {
22682      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
22683      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
22684      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
22685      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
22686      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
22687      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
22688      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
22689      * })
22690      */
22691     // private
22692     
22693     meterWidth: 300,
22694     errorMsg :'',    
22695     errors: false,
22696     imageRoot: '/',
22697     /**
22698      * @cfg {String/Object} Label for the strength meter (defaults to
22699      * 'Password strength:')
22700      */
22701     // private
22702     meterLabel: '',
22703     /**
22704      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
22705      * ['Weak', 'Medium', 'Strong'])
22706      */
22707     // private    
22708     pwdStrengths: false,    
22709     // private
22710     strength: 0,
22711     // private
22712     _lastPwd: null,
22713     // private
22714     kCapitalLetter: 0,
22715     kSmallLetter: 1,
22716     kDigit: 2,
22717     kPunctuation: 3,
22718     
22719     insecure: false,
22720     // private
22721     initEvents: function ()
22722     {
22723         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
22724
22725         if (this.el.is('input[type=password]') && Roo.isSafari) {
22726             this.el.on('keydown', this.SafariOnKeyDown, this);
22727         }
22728
22729         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
22730     },
22731     // private
22732     onRender: function (ct, position)
22733     {
22734         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
22735         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
22736         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
22737
22738         this.trigger.createChild({
22739                    cn: [
22740                     {
22741                     //id: 'PwdMeter',
22742                     tag: 'div',
22743                     cls: 'roo-password-meter-grey col-xs-12',
22744                     style: {
22745                         //width: 0,
22746                         //width: this.meterWidth + 'px'                                                
22747                         }
22748                     },
22749                     {                            
22750                          cls: 'roo-password-meter-text'                          
22751                     }
22752                 ]            
22753         });
22754
22755          
22756         if (this.hideTrigger) {
22757             this.trigger.setDisplayed(false);
22758         }
22759         this.setSize(this.width || '', this.height || '');
22760     },
22761     // private
22762     onDestroy: function ()
22763     {
22764         if (this.trigger) {
22765             this.trigger.removeAllListeners();
22766             this.trigger.remove();
22767         }
22768         if (this.wrap) {
22769             this.wrap.remove();
22770         }
22771         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
22772     },
22773     // private
22774     checkStrength: function ()
22775     {
22776         var pwd = this.inputEl().getValue();
22777         if (pwd == this._lastPwd) {
22778             return;
22779         }
22780
22781         var strength;
22782         if (this.ClientSideStrongPassword(pwd)) {
22783             strength = 3;
22784         } else if (this.ClientSideMediumPassword(pwd)) {
22785             strength = 2;
22786         } else if (this.ClientSideWeakPassword(pwd)) {
22787             strength = 1;
22788         } else {
22789             strength = 0;
22790         }
22791         
22792         Roo.log('strength1: ' + strength);
22793         
22794         //var pm = this.trigger.child('div/div/div').dom;
22795         var pm = this.trigger.child('div/div');
22796         pm.removeClass(this.meterClass);
22797         pm.addClass(this.meterClass[strength]);
22798                 
22799         
22800         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
22801                 
22802         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
22803         
22804         this._lastPwd = pwd;
22805     },
22806     reset: function ()
22807     {
22808         Roo.bootstrap.SecurePass.superclass.reset.call(this);
22809         
22810         this._lastPwd = '';
22811         
22812         var pm = this.trigger.child('div/div');
22813         pm.removeClass(this.meterClass);
22814         pm.addClass('roo-password-meter-grey');        
22815         
22816         
22817         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
22818         
22819         pt.innerHTML = '';
22820         this.inputEl().dom.type='password';
22821     },
22822     // private
22823     validateValue: function (value)
22824     {
22825         
22826         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
22827             return false;
22828         }
22829         if (value.length == 0) {
22830             if (this.allowBlank) {
22831                 this.clearInvalid();
22832                 return true;
22833             }
22834
22835             this.markInvalid(this.errors.PwdEmpty);
22836             this.errorMsg = this.errors.PwdEmpty;
22837             return false;
22838         }
22839         
22840         if(this.insecure){
22841             return true;
22842         }
22843         
22844         if ('[\x21-\x7e]*'.match(value)) {
22845             this.markInvalid(this.errors.PwdBadChar);
22846             this.errorMsg = this.errors.PwdBadChar;
22847             return false;
22848         }
22849         if (value.length < 6) {
22850             this.markInvalid(this.errors.PwdShort);
22851             this.errorMsg = this.errors.PwdShort;
22852             return false;
22853         }
22854         if (value.length > 16) {
22855             this.markInvalid(this.errors.PwdLong);
22856             this.errorMsg = this.errors.PwdLong;
22857             return false;
22858         }
22859         var strength;
22860         if (this.ClientSideStrongPassword(value)) {
22861             strength = 3;
22862         } else if (this.ClientSideMediumPassword(value)) {
22863             strength = 2;
22864         } else if (this.ClientSideWeakPassword(value)) {
22865             strength = 1;
22866         } else {
22867             strength = 0;
22868         }
22869
22870         
22871         if (strength < 2) {
22872             //this.markInvalid(this.errors.TooWeak);
22873             this.errorMsg = this.errors.TooWeak;
22874             //return false;
22875         }
22876         
22877         
22878         console.log('strength2: ' + strength);
22879         
22880         //var pm = this.trigger.child('div/div/div').dom;
22881         
22882         var pm = this.trigger.child('div/div');
22883         pm.removeClass(this.meterClass);
22884         pm.addClass(this.meterClass[strength]);
22885                 
22886         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
22887                 
22888         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
22889         
22890         this.errorMsg = ''; 
22891         return true;
22892     },
22893     // private
22894     CharacterSetChecks: function (type)
22895     {
22896         this.type = type;
22897         this.fResult = false;
22898     },
22899     // private
22900     isctype: function (character, type)
22901     {
22902         switch (type) {  
22903             case this.kCapitalLetter:
22904                 if (character >= 'A' && character <= 'Z') {
22905                     return true;
22906                 }
22907                 break;
22908             
22909             case this.kSmallLetter:
22910                 if (character >= 'a' && character <= 'z') {
22911                     return true;
22912                 }
22913                 break;
22914             
22915             case this.kDigit:
22916                 if (character >= '0' && character <= '9') {
22917                     return true;
22918                 }
22919                 break;
22920             
22921             case this.kPunctuation:
22922                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
22923                     return true;
22924                 }
22925                 break;
22926             
22927             default:
22928                 return false;
22929         }
22930
22931     },
22932     // private
22933     IsLongEnough: function (pwd, size)
22934     {
22935         return !(pwd == null || isNaN(size) || pwd.length < size);
22936     },
22937     // private
22938     SpansEnoughCharacterSets: function (word, nb)
22939     {
22940         if (!this.IsLongEnough(word, nb))
22941         {
22942             return false;
22943         }
22944
22945         var characterSetChecks = new Array(
22946             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
22947             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
22948         );
22949         
22950         for (var index = 0; index < word.length; ++index) {
22951             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
22952                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
22953                     characterSetChecks[nCharSet].fResult = true;
22954                     break;
22955                 }
22956             }
22957         }
22958
22959         var nCharSets = 0;
22960         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
22961             if (characterSetChecks[nCharSet].fResult) {
22962                 ++nCharSets;
22963             }
22964         }
22965
22966         if (nCharSets < nb) {
22967             return false;
22968         }
22969         return true;
22970     },
22971     // private
22972     ClientSideStrongPassword: function (pwd)
22973     {
22974         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
22975     },
22976     // private
22977     ClientSideMediumPassword: function (pwd)
22978     {
22979         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
22980     },
22981     // private
22982     ClientSideWeakPassword: function (pwd)
22983     {
22984         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
22985     }
22986           
22987 })//<script type="text/javascript">
22988
22989 /*
22990  * Based  Ext JS Library 1.1.1
22991  * Copyright(c) 2006-2007, Ext JS, LLC.
22992  * LGPL
22993  *
22994  */
22995  
22996 /**
22997  * @class Roo.HtmlEditorCore
22998  * @extends Roo.Component
22999  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
23000  *
23001  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23002  */
23003
23004 Roo.HtmlEditorCore = function(config){
23005     
23006     
23007     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
23008     
23009     
23010     this.addEvents({
23011         /**
23012          * @event initialize
23013          * Fires when the editor is fully initialized (including the iframe)
23014          * @param {Roo.HtmlEditorCore} this
23015          */
23016         initialize: true,
23017         /**
23018          * @event activate
23019          * Fires when the editor is first receives the focus. Any insertion must wait
23020          * until after this event.
23021          * @param {Roo.HtmlEditorCore} this
23022          */
23023         activate: true,
23024          /**
23025          * @event beforesync
23026          * Fires before the textarea is updated with content from the editor iframe. Return false
23027          * to cancel the sync.
23028          * @param {Roo.HtmlEditorCore} this
23029          * @param {String} html
23030          */
23031         beforesync: true,
23032          /**
23033          * @event beforepush
23034          * Fires before the iframe editor is updated with content from the textarea. Return false
23035          * to cancel the push.
23036          * @param {Roo.HtmlEditorCore} this
23037          * @param {String} html
23038          */
23039         beforepush: true,
23040          /**
23041          * @event sync
23042          * Fires when the textarea is updated with content from the editor iframe.
23043          * @param {Roo.HtmlEditorCore} this
23044          * @param {String} html
23045          */
23046         sync: true,
23047          /**
23048          * @event push
23049          * Fires when the iframe editor is updated with content from the textarea.
23050          * @param {Roo.HtmlEditorCore} this
23051          * @param {String} html
23052          */
23053         push: true,
23054         
23055         /**
23056          * @event editorevent
23057          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23058          * @param {Roo.HtmlEditorCore} this
23059          */
23060         editorevent: true
23061         
23062     });
23063     
23064     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
23065     
23066     // defaults : white / black...
23067     this.applyBlacklists();
23068     
23069     
23070     
23071 };
23072
23073
23074 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
23075
23076
23077      /**
23078      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
23079      */
23080     
23081     owner : false,
23082     
23083      /**
23084      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23085      *                        Roo.resizable.
23086      */
23087     resizable : false,
23088      /**
23089      * @cfg {Number} height (in pixels)
23090      */   
23091     height: 300,
23092    /**
23093      * @cfg {Number} width (in pixels)
23094      */   
23095     width: 500,
23096     
23097     /**
23098      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23099      * 
23100      */
23101     stylesheets: false,
23102     
23103     // id of frame..
23104     frameId: false,
23105     
23106     // private properties
23107     validationEvent : false,
23108     deferHeight: true,
23109     initialized : false,
23110     activated : false,
23111     sourceEditMode : false,
23112     onFocus : Roo.emptyFn,
23113     iframePad:3,
23114     hideMode:'offsets',
23115     
23116     clearUp: true,
23117     
23118     // blacklist + whitelisted elements..
23119     black: false,
23120     white: false,
23121      
23122     bodyCls : '',
23123
23124     /**
23125      * Protected method that will not generally be called directly. It
23126      * is called when the editor initializes the iframe with HTML contents. Override this method if you
23127      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
23128      */
23129     getDocMarkup : function(){
23130         // body styles..
23131         var st = '';
23132         
23133         // inherit styels from page...?? 
23134         if (this.stylesheets === false) {
23135             
23136             Roo.get(document.head).select('style').each(function(node) {
23137                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23138             });
23139             
23140             Roo.get(document.head).select('link').each(function(node) { 
23141                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23142             });
23143             
23144         } else if (!this.stylesheets.length) {
23145                 // simple..
23146                 st = '<style type="text/css">' +
23147                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23148                    '</style>';
23149         } else { 
23150             st = '<style type="text/css">' +
23151                     this.stylesheets +
23152                 '</style>';
23153         }
23154         
23155         st +=  '<style type="text/css">' +
23156             'IMG { cursor: pointer } ' +
23157         '</style>';
23158
23159         var cls = 'roo-htmleditor-body';
23160         
23161         if(this.bodyCls.length){
23162             cls += ' ' + this.bodyCls;
23163         }
23164         
23165         return '<html><head>' + st  +
23166             //<style type="text/css">' +
23167             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23168             //'</style>' +
23169             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
23170     },
23171
23172     // private
23173     onRender : function(ct, position)
23174     {
23175         var _t = this;
23176         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
23177         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
23178         
23179         
23180         this.el.dom.style.border = '0 none';
23181         this.el.dom.setAttribute('tabIndex', -1);
23182         this.el.addClass('x-hidden hide');
23183         
23184         
23185         
23186         if(Roo.isIE){ // fix IE 1px bogus margin
23187             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
23188         }
23189        
23190         
23191         this.frameId = Roo.id();
23192         
23193          
23194         
23195         var iframe = this.owner.wrap.createChild({
23196             tag: 'iframe',
23197             cls: 'form-control', // bootstrap..
23198             id: this.frameId,
23199             name: this.frameId,
23200             frameBorder : 'no',
23201             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
23202         }, this.el
23203         );
23204         
23205         
23206         this.iframe = iframe.dom;
23207
23208          this.assignDocWin();
23209         
23210         this.doc.designMode = 'on';
23211        
23212         this.doc.open();
23213         this.doc.write(this.getDocMarkup());
23214         this.doc.close();
23215
23216         
23217         var task = { // must defer to wait for browser to be ready
23218             run : function(){
23219                 //console.log("run task?" + this.doc.readyState);
23220                 this.assignDocWin();
23221                 if(this.doc.body || this.doc.readyState == 'complete'){
23222                     try {
23223                         this.doc.designMode="on";
23224                     } catch (e) {
23225                         return;
23226                     }
23227                     Roo.TaskMgr.stop(task);
23228                     this.initEditor.defer(10, this);
23229                 }
23230             },
23231             interval : 10,
23232             duration: 10000,
23233             scope: this
23234         };
23235         Roo.TaskMgr.start(task);
23236
23237     },
23238
23239     // private
23240     onResize : function(w, h)
23241     {
23242          Roo.log('resize: ' +w + ',' + h );
23243         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
23244         if(!this.iframe){
23245             return;
23246         }
23247         if(typeof w == 'number'){
23248             
23249             this.iframe.style.width = w + 'px';
23250         }
23251         if(typeof h == 'number'){
23252             
23253             this.iframe.style.height = h + 'px';
23254             if(this.doc){
23255                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
23256             }
23257         }
23258         
23259     },
23260
23261     /**
23262      * Toggles the editor between standard and source edit mode.
23263      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23264      */
23265     toggleSourceEdit : function(sourceEditMode){
23266         
23267         this.sourceEditMode = sourceEditMode === true;
23268         
23269         if(this.sourceEditMode){
23270  
23271             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
23272             
23273         }else{
23274             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
23275             //this.iframe.className = '';
23276             this.deferFocus();
23277         }
23278         //this.setSize(this.owner.wrap.getSize());
23279         //this.fireEvent('editmodechange', this, this.sourceEditMode);
23280     },
23281
23282     
23283   
23284
23285     /**
23286      * Protected method that will not generally be called directly. If you need/want
23287      * custom HTML cleanup, this is the method you should override.
23288      * @param {String} html The HTML to be cleaned
23289      * return {String} The cleaned HTML
23290      */
23291     cleanHtml : function(html){
23292         html = String(html);
23293         if(html.length > 5){
23294             if(Roo.isSafari){ // strip safari nonsense
23295                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
23296             }
23297         }
23298         if(html == '&nbsp;'){
23299             html = '';
23300         }
23301         return html;
23302     },
23303
23304     /**
23305      * HTML Editor -> Textarea
23306      * Protected method that will not generally be called directly. Syncs the contents
23307      * of the editor iframe with the textarea.
23308      */
23309     syncValue : function(){
23310         if(this.initialized){
23311             var bd = (this.doc.body || this.doc.documentElement);
23312             //this.cleanUpPaste(); -- this is done else where and causes havoc..
23313             var html = bd.innerHTML;
23314             if(Roo.isSafari){
23315                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
23316                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
23317                 if(m && m[1]){
23318                     html = '<div style="'+m[0]+'">' + html + '</div>';
23319                 }
23320             }
23321             html = this.cleanHtml(html);
23322             // fix up the special chars.. normaly like back quotes in word...
23323             // however we do not want to do this with chinese..
23324             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
23325                 
23326                 var cc = match.charCodeAt();
23327
23328                 // Get the character value, handling surrogate pairs
23329                 if (match.length == 2) {
23330                     // It's a surrogate pair, calculate the Unicode code point
23331                     var high = match.charCodeAt(0) - 0xD800;
23332                     var low  = match.charCodeAt(1) - 0xDC00;
23333                     cc = (high * 0x400) + low + 0x10000;
23334                 }  else if (
23335                     (cc >= 0x4E00 && cc < 0xA000 ) ||
23336                     (cc >= 0x3400 && cc < 0x4E00 ) ||
23337                     (cc >= 0xf900 && cc < 0xfb00 )
23338                 ) {
23339                         return match;
23340                 }  
23341          
23342                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
23343                 return "&#" + cc + ";";
23344                 
23345                 
23346             });
23347             
23348             
23349              
23350             if(this.owner.fireEvent('beforesync', this, html) !== false){
23351                 this.el.dom.value = html;
23352                 this.owner.fireEvent('sync', this, html);
23353             }
23354         }
23355     },
23356
23357     /**
23358      * Protected method that will not generally be called directly. Pushes the value of the textarea
23359      * into the iframe editor.
23360      */
23361     pushValue : function(){
23362         if(this.initialized){
23363             var v = this.el.dom.value.trim();
23364             
23365 //            if(v.length < 1){
23366 //                v = '&#160;';
23367 //            }
23368             
23369             if(this.owner.fireEvent('beforepush', this, v) !== false){
23370                 var d = (this.doc.body || this.doc.documentElement);
23371                 d.innerHTML = v;
23372                 this.cleanUpPaste();
23373                 this.el.dom.value = d.innerHTML;
23374                 this.owner.fireEvent('push', this, v);
23375             }
23376         }
23377     },
23378
23379     // private
23380     deferFocus : function(){
23381         this.focus.defer(10, this);
23382     },
23383
23384     // doc'ed in Field
23385     focus : function(){
23386         if(this.win && !this.sourceEditMode){
23387             this.win.focus();
23388         }else{
23389             this.el.focus();
23390         }
23391     },
23392     
23393     assignDocWin: function()
23394     {
23395         var iframe = this.iframe;
23396         
23397          if(Roo.isIE){
23398             this.doc = iframe.contentWindow.document;
23399             this.win = iframe.contentWindow;
23400         } else {
23401 //            if (!Roo.get(this.frameId)) {
23402 //                return;
23403 //            }
23404 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
23405 //            this.win = Roo.get(this.frameId).dom.contentWindow;
23406             
23407             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
23408                 return;
23409             }
23410             
23411             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
23412             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
23413         }
23414     },
23415     
23416     // private
23417     initEditor : function(){
23418         //console.log("INIT EDITOR");
23419         this.assignDocWin();
23420         
23421         
23422         
23423         this.doc.designMode="on";
23424         this.doc.open();
23425         this.doc.write(this.getDocMarkup());
23426         this.doc.close();
23427         
23428         var dbody = (this.doc.body || this.doc.documentElement);
23429         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
23430         // this copies styles from the containing element into thsi one..
23431         // not sure why we need all of this..
23432         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
23433         
23434         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
23435         //ss['background-attachment'] = 'fixed'; // w3c
23436         dbody.bgProperties = 'fixed'; // ie
23437         //Roo.DomHelper.applyStyles(dbody, ss);
23438         Roo.EventManager.on(this.doc, {
23439             //'mousedown': this.onEditorEvent,
23440             'mouseup': this.onEditorEvent,
23441             'dblclick': this.onEditorEvent,
23442             'click': this.onEditorEvent,
23443             'keyup': this.onEditorEvent,
23444             buffer:100,
23445             scope: this
23446         });
23447         if(Roo.isGecko){
23448             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
23449         }
23450         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
23451             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
23452         }
23453         this.initialized = true;
23454
23455         this.owner.fireEvent('initialize', this);
23456         this.pushValue();
23457     },
23458
23459     // private
23460     onDestroy : function(){
23461         
23462         
23463         
23464         if(this.rendered){
23465             
23466             //for (var i =0; i < this.toolbars.length;i++) {
23467             //    // fixme - ask toolbars for heights?
23468             //    this.toolbars[i].onDestroy();
23469            // }
23470             
23471             //this.wrap.dom.innerHTML = '';
23472             //this.wrap.remove();
23473         }
23474     },
23475
23476     // private
23477     onFirstFocus : function(){
23478         
23479         this.assignDocWin();
23480         
23481         
23482         this.activated = true;
23483          
23484     
23485         if(Roo.isGecko){ // prevent silly gecko errors
23486             this.win.focus();
23487             var s = this.win.getSelection();
23488             if(!s.focusNode || s.focusNode.nodeType != 3){
23489                 var r = s.getRangeAt(0);
23490                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
23491                 r.collapse(true);
23492                 this.deferFocus();
23493             }
23494             try{
23495                 this.execCmd('useCSS', true);
23496                 this.execCmd('styleWithCSS', false);
23497             }catch(e){}
23498         }
23499         this.owner.fireEvent('activate', this);
23500     },
23501
23502     // private
23503     adjustFont: function(btn){
23504         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
23505         //if(Roo.isSafari){ // safari
23506         //    adjust *= 2;
23507        // }
23508         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
23509         if(Roo.isSafari){ // safari
23510             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
23511             v =  (v < 10) ? 10 : v;
23512             v =  (v > 48) ? 48 : v;
23513             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
23514             
23515         }
23516         
23517         
23518         v = Math.max(1, v+adjust);
23519         
23520         this.execCmd('FontSize', v  );
23521     },
23522
23523     onEditorEvent : function(e)
23524     {
23525         this.owner.fireEvent('editorevent', this, e);
23526       //  this.updateToolbar();
23527         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
23528     },
23529
23530     insertTag : function(tg)
23531     {
23532         // could be a bit smarter... -> wrap the current selected tRoo..
23533         if (tg.toLowerCase() == 'span' ||
23534             tg.toLowerCase() == 'code' ||
23535             tg.toLowerCase() == 'sup' ||
23536             tg.toLowerCase() == 'sub' 
23537             ) {
23538             
23539             range = this.createRange(this.getSelection());
23540             var wrappingNode = this.doc.createElement(tg.toLowerCase());
23541             wrappingNode.appendChild(range.extractContents());
23542             range.insertNode(wrappingNode);
23543
23544             return;
23545             
23546             
23547             
23548         }
23549         this.execCmd("formatblock",   tg);
23550         
23551     },
23552     
23553     insertText : function(txt)
23554     {
23555         
23556         
23557         var range = this.createRange();
23558         range.deleteContents();
23559                //alert(Sender.getAttribute('label'));
23560                
23561         range.insertNode(this.doc.createTextNode(txt));
23562     } ,
23563     
23564      
23565
23566     /**
23567      * Executes a Midas editor command on the editor document and performs necessary focus and
23568      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
23569      * @param {String} cmd The Midas command
23570      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
23571      */
23572     relayCmd : function(cmd, value){
23573         this.win.focus();
23574         this.execCmd(cmd, value);
23575         this.owner.fireEvent('editorevent', this);
23576         //this.updateToolbar();
23577         this.owner.deferFocus();
23578     },
23579
23580     /**
23581      * Executes a Midas editor command directly on the editor document.
23582      * For visual commands, you should use {@link #relayCmd} instead.
23583      * <b>This should only be called after the editor is initialized.</b>
23584      * @param {String} cmd The Midas command
23585      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
23586      */
23587     execCmd : function(cmd, value){
23588         this.doc.execCommand(cmd, false, value === undefined ? null : value);
23589         this.syncValue();
23590     },
23591  
23592  
23593    
23594     /**
23595      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
23596      * to insert tRoo.
23597      * @param {String} text | dom node.. 
23598      */
23599     insertAtCursor : function(text)
23600     {
23601         
23602         if(!this.activated){
23603             return;
23604         }
23605         /*
23606         if(Roo.isIE){
23607             this.win.focus();
23608             var r = this.doc.selection.createRange();
23609             if(r){
23610                 r.collapse(true);
23611                 r.pasteHTML(text);
23612                 this.syncValue();
23613                 this.deferFocus();
23614             
23615             }
23616             return;
23617         }
23618         */
23619         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
23620             this.win.focus();
23621             
23622             
23623             // from jquery ui (MIT licenced)
23624             var range, node;
23625             var win = this.win;
23626             
23627             if (win.getSelection && win.getSelection().getRangeAt) {
23628                 range = win.getSelection().getRangeAt(0);
23629                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
23630                 range.insertNode(node);
23631             } else if (win.document.selection && win.document.selection.createRange) {
23632                 // no firefox support
23633                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
23634                 win.document.selection.createRange().pasteHTML(txt);
23635             } else {
23636                 // no firefox support
23637                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
23638                 this.execCmd('InsertHTML', txt);
23639             } 
23640             
23641             this.syncValue();
23642             
23643             this.deferFocus();
23644         }
23645     },
23646  // private
23647     mozKeyPress : function(e){
23648         if(e.ctrlKey){
23649             var c = e.getCharCode(), cmd;
23650           
23651             if(c > 0){
23652                 c = String.fromCharCode(c).toLowerCase();
23653                 switch(c){
23654                     case 'b':
23655                         cmd = 'bold';
23656                         break;
23657                     case 'i':
23658                         cmd = 'italic';
23659                         break;
23660                     
23661                     case 'u':
23662                         cmd = 'underline';
23663                         break;
23664                     
23665                     case 'v':
23666                         this.cleanUpPaste.defer(100, this);
23667                         return;
23668                         
23669                 }
23670                 if(cmd){
23671                     this.win.focus();
23672                     this.execCmd(cmd);
23673                     this.deferFocus();
23674                     e.preventDefault();
23675                 }
23676                 
23677             }
23678         }
23679     },
23680
23681     // private
23682     fixKeys : function(){ // load time branching for fastest keydown performance
23683         if(Roo.isIE){
23684             return function(e){
23685                 var k = e.getKey(), r;
23686                 if(k == e.TAB){
23687                     e.stopEvent();
23688                     r = this.doc.selection.createRange();
23689                     if(r){
23690                         r.collapse(true);
23691                         r.pasteHTML('&#160;&#160;&#160;&#160;');
23692                         this.deferFocus();
23693                     }
23694                     return;
23695                 }
23696                 
23697                 if(k == e.ENTER){
23698                     r = this.doc.selection.createRange();
23699                     if(r){
23700                         var target = r.parentElement();
23701                         if(!target || target.tagName.toLowerCase() != 'li'){
23702                             e.stopEvent();
23703                             r.pasteHTML('<br />');
23704                             r.collapse(false);
23705                             r.select();
23706                         }
23707                     }
23708                 }
23709                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23710                     this.cleanUpPaste.defer(100, this);
23711                     return;
23712                 }
23713                 
23714                 
23715             };
23716         }else if(Roo.isOpera){
23717             return function(e){
23718                 var k = e.getKey();
23719                 if(k == e.TAB){
23720                     e.stopEvent();
23721                     this.win.focus();
23722                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
23723                     this.deferFocus();
23724                 }
23725                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23726                     this.cleanUpPaste.defer(100, this);
23727                     return;
23728                 }
23729                 
23730             };
23731         }else if(Roo.isSafari){
23732             return function(e){
23733                 var k = e.getKey();
23734                 
23735                 if(k == e.TAB){
23736                     e.stopEvent();
23737                     this.execCmd('InsertText','\t');
23738                     this.deferFocus();
23739                     return;
23740                 }
23741                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23742                     this.cleanUpPaste.defer(100, this);
23743                     return;
23744                 }
23745                 
23746              };
23747         }
23748     }(),
23749     
23750     getAllAncestors: function()
23751     {
23752         var p = this.getSelectedNode();
23753         var a = [];
23754         if (!p) {
23755             a.push(p); // push blank onto stack..
23756             p = this.getParentElement();
23757         }
23758         
23759         
23760         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
23761             a.push(p);
23762             p = p.parentNode;
23763         }
23764         a.push(this.doc.body);
23765         return a;
23766     },
23767     lastSel : false,
23768     lastSelNode : false,
23769     
23770     
23771     getSelection : function() 
23772     {
23773         this.assignDocWin();
23774         return Roo.isIE ? this.doc.selection : this.win.getSelection();
23775     },
23776     
23777     getSelectedNode: function() 
23778     {
23779         // this may only work on Gecko!!!
23780         
23781         // should we cache this!!!!
23782         
23783         
23784         
23785          
23786         var range = this.createRange(this.getSelection()).cloneRange();
23787         
23788         if (Roo.isIE) {
23789             var parent = range.parentElement();
23790             while (true) {
23791                 var testRange = range.duplicate();
23792                 testRange.moveToElementText(parent);
23793                 if (testRange.inRange(range)) {
23794                     break;
23795                 }
23796                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
23797                     break;
23798                 }
23799                 parent = parent.parentElement;
23800             }
23801             return parent;
23802         }
23803         
23804         // is ancestor a text element.
23805         var ac =  range.commonAncestorContainer;
23806         if (ac.nodeType == 3) {
23807             ac = ac.parentNode;
23808         }
23809         
23810         var ar = ac.childNodes;
23811          
23812         var nodes = [];
23813         var other_nodes = [];
23814         var has_other_nodes = false;
23815         for (var i=0;i<ar.length;i++) {
23816             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
23817                 continue;
23818             }
23819             // fullly contained node.
23820             
23821             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
23822                 nodes.push(ar[i]);
23823                 continue;
23824             }
23825             
23826             // probably selected..
23827             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
23828                 other_nodes.push(ar[i]);
23829                 continue;
23830             }
23831             // outer..
23832             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
23833                 continue;
23834             }
23835             
23836             
23837             has_other_nodes = true;
23838         }
23839         if (!nodes.length && other_nodes.length) {
23840             nodes= other_nodes;
23841         }
23842         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
23843             return false;
23844         }
23845         
23846         return nodes[0];
23847     },
23848     createRange: function(sel)
23849     {
23850         // this has strange effects when using with 
23851         // top toolbar - not sure if it's a great idea.
23852         //this.editor.contentWindow.focus();
23853         if (typeof sel != "undefined") {
23854             try {
23855                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
23856             } catch(e) {
23857                 return this.doc.createRange();
23858             }
23859         } else {
23860             return this.doc.createRange();
23861         }
23862     },
23863     getParentElement: function()
23864     {
23865         
23866         this.assignDocWin();
23867         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
23868         
23869         var range = this.createRange(sel);
23870          
23871         try {
23872             var p = range.commonAncestorContainer;
23873             while (p.nodeType == 3) { // text node
23874                 p = p.parentNode;
23875             }
23876             return p;
23877         } catch (e) {
23878             return null;
23879         }
23880     
23881     },
23882     /***
23883      *
23884      * Range intersection.. the hard stuff...
23885      *  '-1' = before
23886      *  '0' = hits..
23887      *  '1' = after.
23888      *         [ -- selected range --- ]
23889      *   [fail]                        [fail]
23890      *
23891      *    basically..
23892      *      if end is before start or  hits it. fail.
23893      *      if start is after end or hits it fail.
23894      *
23895      *   if either hits (but other is outside. - then it's not 
23896      *   
23897      *    
23898      **/
23899     
23900     
23901     // @see http://www.thismuchiknow.co.uk/?p=64.
23902     rangeIntersectsNode : function(range, node)
23903     {
23904         var nodeRange = node.ownerDocument.createRange();
23905         try {
23906             nodeRange.selectNode(node);
23907         } catch (e) {
23908             nodeRange.selectNodeContents(node);
23909         }
23910     
23911         var rangeStartRange = range.cloneRange();
23912         rangeStartRange.collapse(true);
23913     
23914         var rangeEndRange = range.cloneRange();
23915         rangeEndRange.collapse(false);
23916     
23917         var nodeStartRange = nodeRange.cloneRange();
23918         nodeStartRange.collapse(true);
23919     
23920         var nodeEndRange = nodeRange.cloneRange();
23921         nodeEndRange.collapse(false);
23922     
23923         return rangeStartRange.compareBoundaryPoints(
23924                  Range.START_TO_START, nodeEndRange) == -1 &&
23925                rangeEndRange.compareBoundaryPoints(
23926                  Range.START_TO_START, nodeStartRange) == 1;
23927         
23928          
23929     },
23930     rangeCompareNode : function(range, node)
23931     {
23932         var nodeRange = node.ownerDocument.createRange();
23933         try {
23934             nodeRange.selectNode(node);
23935         } catch (e) {
23936             nodeRange.selectNodeContents(node);
23937         }
23938         
23939         
23940         range.collapse(true);
23941     
23942         nodeRange.collapse(true);
23943      
23944         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
23945         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
23946          
23947         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
23948         
23949         var nodeIsBefore   =  ss == 1;
23950         var nodeIsAfter    = ee == -1;
23951         
23952         if (nodeIsBefore && nodeIsAfter) {
23953             return 0; // outer
23954         }
23955         if (!nodeIsBefore && nodeIsAfter) {
23956             return 1; //right trailed.
23957         }
23958         
23959         if (nodeIsBefore && !nodeIsAfter) {
23960             return 2;  // left trailed.
23961         }
23962         // fully contined.
23963         return 3;
23964     },
23965
23966     // private? - in a new class?
23967     cleanUpPaste :  function()
23968     {
23969         // cleans up the whole document..
23970         Roo.log('cleanuppaste');
23971         
23972         this.cleanUpChildren(this.doc.body);
23973         var clean = this.cleanWordChars(this.doc.body.innerHTML);
23974         if (clean != this.doc.body.innerHTML) {
23975             this.doc.body.innerHTML = clean;
23976         }
23977         
23978     },
23979     
23980     cleanWordChars : function(input) {// change the chars to hex code
23981         var he = Roo.HtmlEditorCore;
23982         
23983         var output = input;
23984         Roo.each(he.swapCodes, function(sw) { 
23985             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
23986             
23987             output = output.replace(swapper, sw[1]);
23988         });
23989         
23990         return output;
23991     },
23992     
23993     
23994     cleanUpChildren : function (n)
23995     {
23996         if (!n.childNodes.length) {
23997             return;
23998         }
23999         for (var i = n.childNodes.length-1; i > -1 ; i--) {
24000            this.cleanUpChild(n.childNodes[i]);
24001         }
24002     },
24003     
24004     
24005         
24006     
24007     cleanUpChild : function (node)
24008     {
24009         var ed = this;
24010         //console.log(node);
24011         if (node.nodeName == "#text") {
24012             // clean up silly Windows -- stuff?
24013             return; 
24014         }
24015         if (node.nodeName == "#comment") {
24016             node.parentNode.removeChild(node);
24017             // clean up silly Windows -- stuff?
24018             return; 
24019         }
24020         var lcname = node.tagName.toLowerCase();
24021         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
24022         // whitelist of tags..
24023         
24024         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
24025             // remove node.
24026             node.parentNode.removeChild(node);
24027             return;
24028             
24029         }
24030         
24031         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
24032         
24033         // spans with no attributes - just remove them..
24034         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
24035             remove_keep_children = true;
24036         }
24037         
24038         // remove <a name=....> as rendering on yahoo mailer is borked with this.
24039         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
24040         
24041         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
24042         //    remove_keep_children = true;
24043         //}
24044         
24045         if (remove_keep_children) {
24046             this.cleanUpChildren(node);
24047             // inserts everything just before this node...
24048             while (node.childNodes.length) {
24049                 var cn = node.childNodes[0];
24050                 node.removeChild(cn);
24051                 node.parentNode.insertBefore(cn, node);
24052             }
24053             node.parentNode.removeChild(node);
24054             return;
24055         }
24056         
24057         if (!node.attributes || !node.attributes.length) {
24058             
24059           
24060             
24061             
24062             this.cleanUpChildren(node);
24063             return;
24064         }
24065         
24066         function cleanAttr(n,v)
24067         {
24068             
24069             if (v.match(/^\./) || v.match(/^\//)) {
24070                 return;
24071             }
24072             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
24073                 return;
24074             }
24075             if (v.match(/^#/)) {
24076                 return;
24077             }
24078 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
24079             node.removeAttribute(n);
24080             
24081         }
24082         
24083         var cwhite = this.cwhite;
24084         var cblack = this.cblack;
24085             
24086         function cleanStyle(n,v)
24087         {
24088             if (v.match(/expression/)) { //XSS?? should we even bother..
24089                 node.removeAttribute(n);
24090                 return;
24091             }
24092             
24093             var parts = v.split(/;/);
24094             var clean = [];
24095             
24096             Roo.each(parts, function(p) {
24097                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
24098                 if (!p.length) {
24099                     return true;
24100                 }
24101                 var l = p.split(':').shift().replace(/\s+/g,'');
24102                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
24103                 
24104                 if ( cwhite.length && cblack.indexOf(l) > -1) {
24105 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24106                     //node.removeAttribute(n);
24107                     return true;
24108                 }
24109                 //Roo.log()
24110                 // only allow 'c whitelisted system attributes'
24111                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
24112 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24113                     //node.removeAttribute(n);
24114                     return true;
24115                 }
24116                 
24117                 
24118                  
24119                 
24120                 clean.push(p);
24121                 return true;
24122             });
24123             if (clean.length) { 
24124                 node.setAttribute(n, clean.join(';'));
24125             } else {
24126                 node.removeAttribute(n);
24127             }
24128             
24129         }
24130         
24131         
24132         for (var i = node.attributes.length-1; i > -1 ; i--) {
24133             var a = node.attributes[i];
24134             //console.log(a);
24135             
24136             if (a.name.toLowerCase().substr(0,2)=='on')  {
24137                 node.removeAttribute(a.name);
24138                 continue;
24139             }
24140             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
24141                 node.removeAttribute(a.name);
24142                 continue;
24143             }
24144             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
24145                 cleanAttr(a.name,a.value); // fixme..
24146                 continue;
24147             }
24148             if (a.name == 'style') {
24149                 cleanStyle(a.name,a.value);
24150                 continue;
24151             }
24152             /// clean up MS crap..
24153             // tecnically this should be a list of valid class'es..
24154             
24155             
24156             if (a.name == 'class') {
24157                 if (a.value.match(/^Mso/)) {
24158                     node.removeAttribute('class');
24159                 }
24160                 
24161                 if (a.value.match(/^body$/)) {
24162                     node.removeAttribute('class');
24163                 }
24164                 continue;
24165             }
24166             
24167             // style cleanup!?
24168             // class cleanup?
24169             
24170         }
24171         
24172         
24173         this.cleanUpChildren(node);
24174         
24175         
24176     },
24177     
24178     /**
24179      * Clean up MS wordisms...
24180      */
24181     cleanWord : function(node)
24182     {
24183         if (!node) {
24184             this.cleanWord(this.doc.body);
24185             return;
24186         }
24187         
24188         if(
24189                 node.nodeName == 'SPAN' &&
24190                 !node.hasAttributes() &&
24191                 node.childNodes.length == 1 &&
24192                 node.firstChild.nodeName == "#text"  
24193         ) {
24194             var textNode = node.firstChild;
24195             node.removeChild(textNode);
24196             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
24197                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
24198             }
24199             node.parentNode.insertBefore(textNode, node);
24200             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
24201                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
24202             }
24203             node.parentNode.removeChild(node);
24204         }
24205         
24206         if (node.nodeName == "#text") {
24207             // clean up silly Windows -- stuff?
24208             return; 
24209         }
24210         if (node.nodeName == "#comment") {
24211             node.parentNode.removeChild(node);
24212             // clean up silly Windows -- stuff?
24213             return; 
24214         }
24215         
24216         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
24217             node.parentNode.removeChild(node);
24218             return;
24219         }
24220         //Roo.log(node.tagName);
24221         // remove - but keep children..
24222         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
24223             //Roo.log('-- removed');
24224             while (node.childNodes.length) {
24225                 var cn = node.childNodes[0];
24226                 node.removeChild(cn);
24227                 node.parentNode.insertBefore(cn, node);
24228                 // move node to parent - and clean it..
24229                 this.cleanWord(cn);
24230             }
24231             node.parentNode.removeChild(node);
24232             /// no need to iterate chidlren = it's got none..
24233             //this.iterateChildren(node, this.cleanWord);
24234             return;
24235         }
24236         // clean styles
24237         if (node.className.length) {
24238             
24239             var cn = node.className.split(/\W+/);
24240             var cna = [];
24241             Roo.each(cn, function(cls) {
24242                 if (cls.match(/Mso[a-zA-Z]+/)) {
24243                     return;
24244                 }
24245                 cna.push(cls);
24246             });
24247             node.className = cna.length ? cna.join(' ') : '';
24248             if (!cna.length) {
24249                 node.removeAttribute("class");
24250             }
24251         }
24252         
24253         if (node.hasAttribute("lang")) {
24254             node.removeAttribute("lang");
24255         }
24256         
24257         if (node.hasAttribute("style")) {
24258             
24259             var styles = node.getAttribute("style").split(";");
24260             var nstyle = [];
24261             Roo.each(styles, function(s) {
24262                 if (!s.match(/:/)) {
24263                     return;
24264                 }
24265                 var kv = s.split(":");
24266                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
24267                     return;
24268                 }
24269                 // what ever is left... we allow.
24270                 nstyle.push(s);
24271             });
24272             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
24273             if (!nstyle.length) {
24274                 node.removeAttribute('style');
24275             }
24276         }
24277         this.iterateChildren(node, this.cleanWord);
24278         
24279         
24280         
24281     },
24282     /**
24283      * iterateChildren of a Node, calling fn each time, using this as the scole..
24284      * @param {DomNode} node node to iterate children of.
24285      * @param {Function} fn method of this class to call on each item.
24286      */
24287     iterateChildren : function(node, fn)
24288     {
24289         if (!node.childNodes.length) {
24290                 return;
24291         }
24292         for (var i = node.childNodes.length-1; i > -1 ; i--) {
24293            fn.call(this, node.childNodes[i])
24294         }
24295     },
24296     
24297     
24298     /**
24299      * cleanTableWidths.
24300      *
24301      * Quite often pasting from word etc.. results in tables with column and widths.
24302      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
24303      *
24304      */
24305     cleanTableWidths : function(node)
24306     {
24307          
24308          
24309         if (!node) {
24310             this.cleanTableWidths(this.doc.body);
24311             return;
24312         }
24313         
24314         // ignore list...
24315         if (node.nodeName == "#text" || node.nodeName == "#comment") {
24316             return; 
24317         }
24318         Roo.log(node.tagName);
24319         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
24320             this.iterateChildren(node, this.cleanTableWidths);
24321             return;
24322         }
24323         if (node.hasAttribute('width')) {
24324             node.removeAttribute('width');
24325         }
24326         
24327          
24328         if (node.hasAttribute("style")) {
24329             // pretty basic...
24330             
24331             var styles = node.getAttribute("style").split(";");
24332             var nstyle = [];
24333             Roo.each(styles, function(s) {
24334                 if (!s.match(/:/)) {
24335                     return;
24336                 }
24337                 var kv = s.split(":");
24338                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
24339                     return;
24340                 }
24341                 // what ever is left... we allow.
24342                 nstyle.push(s);
24343             });
24344             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
24345             if (!nstyle.length) {
24346                 node.removeAttribute('style');
24347             }
24348         }
24349         
24350         this.iterateChildren(node, this.cleanTableWidths);
24351         
24352         
24353     },
24354     
24355     
24356     
24357     
24358     domToHTML : function(currentElement, depth, nopadtext) {
24359         
24360         depth = depth || 0;
24361         nopadtext = nopadtext || false;
24362     
24363         if (!currentElement) {
24364             return this.domToHTML(this.doc.body);
24365         }
24366         
24367         //Roo.log(currentElement);
24368         var j;
24369         var allText = false;
24370         var nodeName = currentElement.nodeName;
24371         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
24372         
24373         if  (nodeName == '#text') {
24374             
24375             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
24376         }
24377         
24378         
24379         var ret = '';
24380         if (nodeName != 'BODY') {
24381              
24382             var i = 0;
24383             // Prints the node tagName, such as <A>, <IMG>, etc
24384             if (tagName) {
24385                 var attr = [];
24386                 for(i = 0; i < currentElement.attributes.length;i++) {
24387                     // quoting?
24388                     var aname = currentElement.attributes.item(i).name;
24389                     if (!currentElement.attributes.item(i).value.length) {
24390                         continue;
24391                     }
24392                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
24393                 }
24394                 
24395                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
24396             } 
24397             else {
24398                 
24399                 // eack
24400             }
24401         } else {
24402             tagName = false;
24403         }
24404         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
24405             return ret;
24406         }
24407         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
24408             nopadtext = true;
24409         }
24410         
24411         
24412         // Traverse the tree
24413         i = 0;
24414         var currentElementChild = currentElement.childNodes.item(i);
24415         var allText = true;
24416         var innerHTML  = '';
24417         lastnode = '';
24418         while (currentElementChild) {
24419             // Formatting code (indent the tree so it looks nice on the screen)
24420             var nopad = nopadtext;
24421             if (lastnode == 'SPAN') {
24422                 nopad  = true;
24423             }
24424             // text
24425             if  (currentElementChild.nodeName == '#text') {
24426                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
24427                 toadd = nopadtext ? toadd : toadd.trim();
24428                 if (!nopad && toadd.length > 80) {
24429                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
24430                 }
24431                 innerHTML  += toadd;
24432                 
24433                 i++;
24434                 currentElementChild = currentElement.childNodes.item(i);
24435                 lastNode = '';
24436                 continue;
24437             }
24438             allText = false;
24439             
24440             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
24441                 
24442             // Recursively traverse the tree structure of the child node
24443             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
24444             lastnode = currentElementChild.nodeName;
24445             i++;
24446             currentElementChild=currentElement.childNodes.item(i);
24447         }
24448         
24449         ret += innerHTML;
24450         
24451         if (!allText) {
24452                 // The remaining code is mostly for formatting the tree
24453             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
24454         }
24455         
24456         
24457         if (tagName) {
24458             ret+= "</"+tagName+">";
24459         }
24460         return ret;
24461         
24462     },
24463         
24464     applyBlacklists : function()
24465     {
24466         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
24467         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
24468         
24469         this.white = [];
24470         this.black = [];
24471         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
24472             if (b.indexOf(tag) > -1) {
24473                 return;
24474             }
24475             this.white.push(tag);
24476             
24477         }, this);
24478         
24479         Roo.each(w, function(tag) {
24480             if (b.indexOf(tag) > -1) {
24481                 return;
24482             }
24483             if (this.white.indexOf(tag) > -1) {
24484                 return;
24485             }
24486             this.white.push(tag);
24487             
24488         }, this);
24489         
24490         
24491         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
24492             if (w.indexOf(tag) > -1) {
24493                 return;
24494             }
24495             this.black.push(tag);
24496             
24497         }, this);
24498         
24499         Roo.each(b, function(tag) {
24500             if (w.indexOf(tag) > -1) {
24501                 return;
24502             }
24503             if (this.black.indexOf(tag) > -1) {
24504                 return;
24505             }
24506             this.black.push(tag);
24507             
24508         }, this);
24509         
24510         
24511         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
24512         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
24513         
24514         this.cwhite = [];
24515         this.cblack = [];
24516         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
24517             if (b.indexOf(tag) > -1) {
24518                 return;
24519             }
24520             this.cwhite.push(tag);
24521             
24522         }, this);
24523         
24524         Roo.each(w, function(tag) {
24525             if (b.indexOf(tag) > -1) {
24526                 return;
24527             }
24528             if (this.cwhite.indexOf(tag) > -1) {
24529                 return;
24530             }
24531             this.cwhite.push(tag);
24532             
24533         }, this);
24534         
24535         
24536         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
24537             if (w.indexOf(tag) > -1) {
24538                 return;
24539             }
24540             this.cblack.push(tag);
24541             
24542         }, this);
24543         
24544         Roo.each(b, function(tag) {
24545             if (w.indexOf(tag) > -1) {
24546                 return;
24547             }
24548             if (this.cblack.indexOf(tag) > -1) {
24549                 return;
24550             }
24551             this.cblack.push(tag);
24552             
24553         }, this);
24554     },
24555     
24556     setStylesheets : function(stylesheets)
24557     {
24558         if(typeof(stylesheets) == 'string'){
24559             Roo.get(this.iframe.contentDocument.head).createChild({
24560                 tag : 'link',
24561                 rel : 'stylesheet',
24562                 type : 'text/css',
24563                 href : stylesheets
24564             });
24565             
24566             return;
24567         }
24568         var _this = this;
24569      
24570         Roo.each(stylesheets, function(s) {
24571             if(!s.length){
24572                 return;
24573             }
24574             
24575             Roo.get(_this.iframe.contentDocument.head).createChild({
24576                 tag : 'link',
24577                 rel : 'stylesheet',
24578                 type : 'text/css',
24579                 href : s
24580             });
24581         });
24582
24583         
24584     },
24585     
24586     removeStylesheets : function()
24587     {
24588         var _this = this;
24589         
24590         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
24591             s.remove();
24592         });
24593     },
24594     
24595     setStyle : function(style)
24596     {
24597         Roo.get(this.iframe.contentDocument.head).createChild({
24598             tag : 'style',
24599             type : 'text/css',
24600             html : style
24601         });
24602
24603         return;
24604     }
24605     
24606     // hide stuff that is not compatible
24607     /**
24608      * @event blur
24609      * @hide
24610      */
24611     /**
24612      * @event change
24613      * @hide
24614      */
24615     /**
24616      * @event focus
24617      * @hide
24618      */
24619     /**
24620      * @event specialkey
24621      * @hide
24622      */
24623     /**
24624      * @cfg {String} fieldClass @hide
24625      */
24626     /**
24627      * @cfg {String} focusClass @hide
24628      */
24629     /**
24630      * @cfg {String} autoCreate @hide
24631      */
24632     /**
24633      * @cfg {String} inputType @hide
24634      */
24635     /**
24636      * @cfg {String} invalidClass @hide
24637      */
24638     /**
24639      * @cfg {String} invalidText @hide
24640      */
24641     /**
24642      * @cfg {String} msgFx @hide
24643      */
24644     /**
24645      * @cfg {String} validateOnBlur @hide
24646      */
24647 });
24648
24649 Roo.HtmlEditorCore.white = [
24650         'area', 'br', 'img', 'input', 'hr', 'wbr',
24651         
24652        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
24653        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
24654        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
24655        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
24656        'table',   'ul',         'xmp', 
24657        
24658        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
24659       'thead',   'tr', 
24660      
24661       'dir', 'menu', 'ol', 'ul', 'dl',
24662        
24663       'embed',  'object'
24664 ];
24665
24666
24667 Roo.HtmlEditorCore.black = [
24668     //    'embed',  'object', // enable - backend responsiblity to clean thiese
24669         'applet', // 
24670         'base',   'basefont', 'bgsound', 'blink',  'body', 
24671         'frame',  'frameset', 'head',    'html',   'ilayer', 
24672         'iframe', 'layer',  'link',     'meta',    'object',   
24673         'script', 'style' ,'title',  'xml' // clean later..
24674 ];
24675 Roo.HtmlEditorCore.clean = [
24676     'script', 'style', 'title', 'xml'
24677 ];
24678 Roo.HtmlEditorCore.remove = [
24679     'font'
24680 ];
24681 // attributes..
24682
24683 Roo.HtmlEditorCore.ablack = [
24684     'on'
24685 ];
24686     
24687 Roo.HtmlEditorCore.aclean = [ 
24688     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
24689 ];
24690
24691 // protocols..
24692 Roo.HtmlEditorCore.pwhite= [
24693         'http',  'https',  'mailto'
24694 ];
24695
24696 // white listed style attributes.
24697 Roo.HtmlEditorCore.cwhite= [
24698       //  'text-align', /// default is to allow most things..
24699       
24700          
24701 //        'font-size'//??
24702 ];
24703
24704 // black listed style attributes.
24705 Roo.HtmlEditorCore.cblack= [
24706       //  'font-size' -- this can be set by the project 
24707 ];
24708
24709
24710 Roo.HtmlEditorCore.swapCodes   =[ 
24711     [    8211, "--" ], 
24712     [    8212, "--" ], 
24713     [    8216,  "'" ],  
24714     [    8217, "'" ],  
24715     [    8220, '"' ],  
24716     [    8221, '"' ],  
24717     [    8226, "*" ],  
24718     [    8230, "..." ]
24719 ]; 
24720
24721     /*
24722  * - LGPL
24723  *
24724  * HtmlEditor
24725  * 
24726  */
24727
24728 /**
24729  * @class Roo.bootstrap.HtmlEditor
24730  * @extends Roo.bootstrap.TextArea
24731  * Bootstrap HtmlEditor class
24732
24733  * @constructor
24734  * Create a new HtmlEditor
24735  * @param {Object} config The config object
24736  */
24737
24738 Roo.bootstrap.HtmlEditor = function(config){
24739     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
24740     if (!this.toolbars) {
24741         this.toolbars = [];
24742     }
24743     
24744     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
24745     this.addEvents({
24746             /**
24747              * @event initialize
24748              * Fires when the editor is fully initialized (including the iframe)
24749              * @param {HtmlEditor} this
24750              */
24751             initialize: true,
24752             /**
24753              * @event activate
24754              * Fires when the editor is first receives the focus. Any insertion must wait
24755              * until after this event.
24756              * @param {HtmlEditor} this
24757              */
24758             activate: true,
24759              /**
24760              * @event beforesync
24761              * Fires before the textarea is updated with content from the editor iframe. Return false
24762              * to cancel the sync.
24763              * @param {HtmlEditor} this
24764              * @param {String} html
24765              */
24766             beforesync: true,
24767              /**
24768              * @event beforepush
24769              * Fires before the iframe editor is updated with content from the textarea. Return false
24770              * to cancel the push.
24771              * @param {HtmlEditor} this
24772              * @param {String} html
24773              */
24774             beforepush: true,
24775              /**
24776              * @event sync
24777              * Fires when the textarea is updated with content from the editor iframe.
24778              * @param {HtmlEditor} this
24779              * @param {String} html
24780              */
24781             sync: true,
24782              /**
24783              * @event push
24784              * Fires when the iframe editor is updated with content from the textarea.
24785              * @param {HtmlEditor} this
24786              * @param {String} html
24787              */
24788             push: true,
24789              /**
24790              * @event editmodechange
24791              * Fires when the editor switches edit modes
24792              * @param {HtmlEditor} this
24793              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
24794              */
24795             editmodechange: true,
24796             /**
24797              * @event editorevent
24798              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24799              * @param {HtmlEditor} this
24800              */
24801             editorevent: true,
24802             /**
24803              * @event firstfocus
24804              * Fires when on first focus - needed by toolbars..
24805              * @param {HtmlEditor} this
24806              */
24807             firstfocus: true,
24808             /**
24809              * @event autosave
24810              * Auto save the htmlEditor value as a file into Events
24811              * @param {HtmlEditor} this
24812              */
24813             autosave: true,
24814             /**
24815              * @event savedpreview
24816              * preview the saved version of htmlEditor
24817              * @param {HtmlEditor} this
24818              */
24819             savedpreview: true
24820         });
24821 };
24822
24823
24824 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
24825     
24826     
24827       /**
24828      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
24829      */
24830     toolbars : false,
24831     
24832      /**
24833     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
24834     */
24835     btns : [],
24836    
24837      /**
24838      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24839      *                        Roo.resizable.
24840      */
24841     resizable : false,
24842      /**
24843      * @cfg {Number} height (in pixels)
24844      */   
24845     height: 300,
24846    /**
24847      * @cfg {Number} width (in pixels)
24848      */   
24849     width: false,
24850     
24851     /**
24852      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24853      * 
24854      */
24855     stylesheets: false,
24856     
24857     // id of frame..
24858     frameId: false,
24859     
24860     // private properties
24861     validationEvent : false,
24862     deferHeight: true,
24863     initialized : false,
24864     activated : false,
24865     
24866     onFocus : Roo.emptyFn,
24867     iframePad:3,
24868     hideMode:'offsets',
24869     
24870     tbContainer : false,
24871     
24872     bodyCls : '',
24873     
24874     toolbarContainer :function() {
24875         return this.wrap.select('.x-html-editor-tb',true).first();
24876     },
24877
24878     /**
24879      * Protected method that will not generally be called directly. It
24880      * is called when the editor creates its toolbar. Override this method if you need to
24881      * add custom toolbar buttons.
24882      * @param {HtmlEditor} editor
24883      */
24884     createToolbar : function(){
24885         Roo.log('renewing');
24886         Roo.log("create toolbars");
24887         
24888         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
24889         this.toolbars[0].render(this.toolbarContainer());
24890         
24891         return;
24892         
24893 //        if (!editor.toolbars || !editor.toolbars.length) {
24894 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
24895 //        }
24896 //        
24897 //        for (var i =0 ; i < editor.toolbars.length;i++) {
24898 //            editor.toolbars[i] = Roo.factory(
24899 //                    typeof(editor.toolbars[i]) == 'string' ?
24900 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
24901 //                Roo.bootstrap.HtmlEditor);
24902 //            editor.toolbars[i].init(editor);
24903 //        }
24904     },
24905
24906      
24907     // private
24908     onRender : function(ct, position)
24909     {
24910        // Roo.log("Call onRender: " + this.xtype);
24911         var _t = this;
24912         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
24913       
24914         this.wrap = this.inputEl().wrap({
24915             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
24916         });
24917         
24918         this.editorcore.onRender(ct, position);
24919          
24920         if (this.resizable) {
24921             this.resizeEl = new Roo.Resizable(this.wrap, {
24922                 pinned : true,
24923                 wrap: true,
24924                 dynamic : true,
24925                 minHeight : this.height,
24926                 height: this.height,
24927                 handles : this.resizable,
24928                 width: this.width,
24929                 listeners : {
24930                     resize : function(r, w, h) {
24931                         _t.onResize(w,h); // -something
24932                     }
24933                 }
24934             });
24935             
24936         }
24937         this.createToolbar(this);
24938        
24939         
24940         if(!this.width && this.resizable){
24941             this.setSize(this.wrap.getSize());
24942         }
24943         if (this.resizeEl) {
24944             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
24945             // should trigger onReize..
24946         }
24947         
24948     },
24949
24950     // private
24951     onResize : function(w, h)
24952     {
24953         Roo.log('resize: ' +w + ',' + h );
24954         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
24955         var ew = false;
24956         var eh = false;
24957         
24958         if(this.inputEl() ){
24959             if(typeof w == 'number'){
24960                 var aw = w - this.wrap.getFrameWidth('lr');
24961                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
24962                 ew = aw;
24963             }
24964             if(typeof h == 'number'){
24965                  var tbh = -11;  // fixme it needs to tool bar size!
24966                 for (var i =0; i < this.toolbars.length;i++) {
24967                     // fixme - ask toolbars for heights?
24968                     tbh += this.toolbars[i].el.getHeight();
24969                     //if (this.toolbars[i].footer) {
24970                     //    tbh += this.toolbars[i].footer.el.getHeight();
24971                     //}
24972                 }
24973               
24974                 
24975                 
24976                 
24977                 
24978                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
24979                 ah -= 5; // knock a few pixes off for look..
24980                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
24981                 var eh = ah;
24982             }
24983         }
24984         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
24985         this.editorcore.onResize(ew,eh);
24986         
24987     },
24988
24989     /**
24990      * Toggles the editor between standard and source edit mode.
24991      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24992      */
24993     toggleSourceEdit : function(sourceEditMode)
24994     {
24995         this.editorcore.toggleSourceEdit(sourceEditMode);
24996         
24997         if(this.editorcore.sourceEditMode){
24998             Roo.log('editor - showing textarea');
24999             
25000 //            Roo.log('in');
25001 //            Roo.log(this.syncValue());
25002             this.syncValue();
25003             this.inputEl().removeClass(['hide', 'x-hidden']);
25004             this.inputEl().dom.removeAttribute('tabIndex');
25005             this.inputEl().focus();
25006         }else{
25007             Roo.log('editor - hiding textarea');
25008 //            Roo.log('out')
25009 //            Roo.log(this.pushValue()); 
25010             this.pushValue();
25011             
25012             this.inputEl().addClass(['hide', 'x-hidden']);
25013             this.inputEl().dom.setAttribute('tabIndex', -1);
25014             //this.deferFocus();
25015         }
25016          
25017         if(this.resizable){
25018             this.setSize(this.wrap.getSize());
25019         }
25020         
25021         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
25022     },
25023  
25024     // private (for BoxComponent)
25025     adjustSize : Roo.BoxComponent.prototype.adjustSize,
25026
25027     // private (for BoxComponent)
25028     getResizeEl : function(){
25029         return this.wrap;
25030     },
25031
25032     // private (for BoxComponent)
25033     getPositionEl : function(){
25034         return this.wrap;
25035     },
25036
25037     // private
25038     initEvents : function(){
25039         this.originalValue = this.getValue();
25040     },
25041
25042 //    /**
25043 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25044 //     * @method
25045 //     */
25046 //    markInvalid : Roo.emptyFn,
25047 //    /**
25048 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25049 //     * @method
25050 //     */
25051 //    clearInvalid : Roo.emptyFn,
25052
25053     setValue : function(v){
25054         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
25055         this.editorcore.pushValue();
25056     },
25057
25058      
25059     // private
25060     deferFocus : function(){
25061         this.focus.defer(10, this);
25062     },
25063
25064     // doc'ed in Field
25065     focus : function(){
25066         this.editorcore.focus();
25067         
25068     },
25069       
25070
25071     // private
25072     onDestroy : function(){
25073         
25074         
25075         
25076         if(this.rendered){
25077             
25078             for (var i =0; i < this.toolbars.length;i++) {
25079                 // fixme - ask toolbars for heights?
25080                 this.toolbars[i].onDestroy();
25081             }
25082             
25083             this.wrap.dom.innerHTML = '';
25084             this.wrap.remove();
25085         }
25086     },
25087
25088     // private
25089     onFirstFocus : function(){
25090         //Roo.log("onFirstFocus");
25091         this.editorcore.onFirstFocus();
25092          for (var i =0; i < this.toolbars.length;i++) {
25093             this.toolbars[i].onFirstFocus();
25094         }
25095         
25096     },
25097     
25098     // private
25099     syncValue : function()
25100     {   
25101         this.editorcore.syncValue();
25102     },
25103     
25104     pushValue : function()
25105     {   
25106         this.editorcore.pushValue();
25107     }
25108      
25109     
25110     // hide stuff that is not compatible
25111     /**
25112      * @event blur
25113      * @hide
25114      */
25115     /**
25116      * @event change
25117      * @hide
25118      */
25119     /**
25120      * @event focus
25121      * @hide
25122      */
25123     /**
25124      * @event specialkey
25125      * @hide
25126      */
25127     /**
25128      * @cfg {String} fieldClass @hide
25129      */
25130     /**
25131      * @cfg {String} focusClass @hide
25132      */
25133     /**
25134      * @cfg {String} autoCreate @hide
25135      */
25136     /**
25137      * @cfg {String} inputType @hide
25138      */
25139      
25140     /**
25141      * @cfg {String} invalidText @hide
25142      */
25143     /**
25144      * @cfg {String} msgFx @hide
25145      */
25146     /**
25147      * @cfg {String} validateOnBlur @hide
25148      */
25149 });
25150  
25151     
25152    
25153    
25154    
25155       
25156 Roo.namespace('Roo.bootstrap.htmleditor');
25157 /**
25158  * @class Roo.bootstrap.HtmlEditorToolbar1
25159  * Basic Toolbar
25160  * 
25161  * @example
25162  * Usage:
25163  *
25164  new Roo.bootstrap.HtmlEditor({
25165     ....
25166     toolbars : [
25167         new Roo.bootstrap.HtmlEditorToolbar1({
25168             disable : { fonts: 1 , format: 1, ..., ... , ...],
25169             btns : [ .... ]
25170         })
25171     }
25172      
25173  * 
25174  * @cfg {Object} disable List of elements to disable..
25175  * @cfg {Array} btns List of additional buttons.
25176  * 
25177  * 
25178  * NEEDS Extra CSS? 
25179  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
25180  */
25181  
25182 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
25183 {
25184     
25185     Roo.apply(this, config);
25186     
25187     // default disabled, based on 'good practice'..
25188     this.disable = this.disable || {};
25189     Roo.applyIf(this.disable, {
25190         fontSize : true,
25191         colors : true,
25192         specialElements : true
25193     });
25194     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
25195     
25196     this.editor = config.editor;
25197     this.editorcore = config.editor.editorcore;
25198     
25199     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
25200     
25201     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25202     // dont call parent... till later.
25203 }
25204 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
25205      
25206     bar : true,
25207     
25208     editor : false,
25209     editorcore : false,
25210     
25211     
25212     formats : [
25213         "p" ,  
25214         "h1","h2","h3","h4","h5","h6", 
25215         "pre", "code", 
25216         "abbr", "acronym", "address", "cite", "samp", "var",
25217         'div','span'
25218     ],
25219     
25220     onRender : function(ct, position)
25221     {
25222        // Roo.log("Call onRender: " + this.xtype);
25223         
25224        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
25225        Roo.log(this.el);
25226        this.el.dom.style.marginBottom = '0';
25227        var _this = this;
25228        var editorcore = this.editorcore;
25229        var editor= this.editor;
25230        
25231        var children = [];
25232        var btn = function(id,cmd , toggle, handler, html){
25233        
25234             var  event = toggle ? 'toggle' : 'click';
25235        
25236             var a = {
25237                 size : 'sm',
25238                 xtype: 'Button',
25239                 xns: Roo.bootstrap,
25240                 //glyphicon : id,
25241                 fa: id,
25242                 cmd : id || cmd,
25243                 enableToggle:toggle !== false,
25244                 html : html || '',
25245                 pressed : toggle ? false : null,
25246                 listeners : {}
25247             };
25248             a.listeners[toggle ? 'toggle' : 'click'] = function() {
25249                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
25250             };
25251             children.push(a);
25252             return a;
25253        }
25254        
25255     //    var cb_box = function...
25256         
25257         var style = {
25258                 xtype: 'Button',
25259                 size : 'sm',
25260                 xns: Roo.bootstrap,
25261                 fa : 'font',
25262                 //html : 'submit'
25263                 menu : {
25264                     xtype: 'Menu',
25265                     xns: Roo.bootstrap,
25266                     items:  []
25267                 }
25268         };
25269         Roo.each(this.formats, function(f) {
25270             style.menu.items.push({
25271                 xtype :'MenuItem',
25272                 xns: Roo.bootstrap,
25273                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
25274                 tagname : f,
25275                 listeners : {
25276                     click : function()
25277                     {
25278                         editorcore.insertTag(this.tagname);
25279                         editor.focus();
25280                     }
25281                 }
25282                 
25283             });
25284         });
25285         children.push(style);   
25286         
25287         btn('bold',false,true);
25288         btn('italic',false,true);
25289         btn('align-left', 'justifyleft',true);
25290         btn('align-center', 'justifycenter',true);
25291         btn('align-right' , 'justifyright',true);
25292         btn('link', false, false, function(btn) {
25293             //Roo.log("create link?");
25294             var url = prompt(this.createLinkText, this.defaultLinkValue);
25295             if(url && url != 'http:/'+'/'){
25296                 this.editorcore.relayCmd('createlink', url);
25297             }
25298         }),
25299         btn('list','insertunorderedlist',true);
25300         btn('pencil', false,true, function(btn){
25301                 Roo.log(this);
25302                 this.toggleSourceEdit(btn.pressed);
25303         });
25304         
25305         if (this.editor.btns.length > 0) {
25306             for (var i = 0; i<this.editor.btns.length; i++) {
25307                 children.push(this.editor.btns[i]);
25308             }
25309         }
25310         
25311         /*
25312         var cog = {
25313                 xtype: 'Button',
25314                 size : 'sm',
25315                 xns: Roo.bootstrap,
25316                 glyphicon : 'cog',
25317                 //html : 'submit'
25318                 menu : {
25319                     xtype: 'Menu',
25320                     xns: Roo.bootstrap,
25321                     items:  []
25322                 }
25323         };
25324         
25325         cog.menu.items.push({
25326             xtype :'MenuItem',
25327             xns: Roo.bootstrap,
25328             html : Clean styles,
25329             tagname : f,
25330             listeners : {
25331                 click : function()
25332                 {
25333                     editorcore.insertTag(this.tagname);
25334                     editor.focus();
25335                 }
25336             }
25337             
25338         });
25339        */
25340         
25341          
25342        this.xtype = 'NavSimplebar';
25343         
25344         for(var i=0;i< children.length;i++) {
25345             
25346             this.buttons.add(this.addxtypeChild(children[i]));
25347             
25348         }
25349         
25350         editor.on('editorevent', this.updateToolbar, this);
25351     },
25352     onBtnClick : function(id)
25353     {
25354        this.editorcore.relayCmd(id);
25355        this.editorcore.focus();
25356     },
25357     
25358     /**
25359      * Protected method that will not generally be called directly. It triggers
25360      * a toolbar update by reading the markup state of the current selection in the editor.
25361      */
25362     updateToolbar: function(){
25363
25364         if(!this.editorcore.activated){
25365             this.editor.onFirstFocus(); // is this neeed?
25366             return;
25367         }
25368
25369         var btns = this.buttons; 
25370         var doc = this.editorcore.doc;
25371         btns.get('bold').setActive(doc.queryCommandState('bold'));
25372         btns.get('italic').setActive(doc.queryCommandState('italic'));
25373         //btns.get('underline').setActive(doc.queryCommandState('underline'));
25374         
25375         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
25376         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
25377         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
25378         
25379         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
25380         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
25381          /*
25382         
25383         var ans = this.editorcore.getAllAncestors();
25384         if (this.formatCombo) {
25385             
25386             
25387             var store = this.formatCombo.store;
25388             this.formatCombo.setValue("");
25389             for (var i =0; i < ans.length;i++) {
25390                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
25391                     // select it..
25392                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
25393                     break;
25394                 }
25395             }
25396         }
25397         
25398         
25399         
25400         // hides menus... - so this cant be on a menu...
25401         Roo.bootstrap.MenuMgr.hideAll();
25402         */
25403         Roo.bootstrap.MenuMgr.hideAll();
25404         //this.editorsyncValue();
25405     },
25406     onFirstFocus: function() {
25407         this.buttons.each(function(item){
25408            item.enable();
25409         });
25410     },
25411     toggleSourceEdit : function(sourceEditMode){
25412         
25413           
25414         if(sourceEditMode){
25415             Roo.log("disabling buttons");
25416            this.buttons.each( function(item){
25417                 if(item.cmd != 'pencil'){
25418                     item.disable();
25419                 }
25420             });
25421           
25422         }else{
25423             Roo.log("enabling buttons");
25424             if(this.editorcore.initialized){
25425                 this.buttons.each( function(item){
25426                     item.enable();
25427                 });
25428             }
25429             
25430         }
25431         Roo.log("calling toggole on editor");
25432         // tell the editor that it's been pressed..
25433         this.editor.toggleSourceEdit(sourceEditMode);
25434        
25435     }
25436 });
25437
25438
25439
25440
25441
25442 /**
25443  * @class Roo.bootstrap.Table.AbstractSelectionModel
25444  * @extends Roo.util.Observable
25445  * Abstract base class for grid SelectionModels.  It provides the interface that should be
25446  * implemented by descendant classes.  This class should not be directly instantiated.
25447  * @constructor
25448  */
25449 Roo.bootstrap.Table.AbstractSelectionModel = function(){
25450     this.locked = false;
25451     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
25452 };
25453
25454
25455 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
25456     /** @ignore Called by the grid automatically. Do not call directly. */
25457     init : function(grid){
25458         this.grid = grid;
25459         this.initEvents();
25460     },
25461
25462     /**
25463      * Locks the selections.
25464      */
25465     lock : function(){
25466         this.locked = true;
25467     },
25468
25469     /**
25470      * Unlocks the selections.
25471      */
25472     unlock : function(){
25473         this.locked = false;
25474     },
25475
25476     /**
25477      * Returns true if the selections are locked.
25478      * @return {Boolean}
25479      */
25480     isLocked : function(){
25481         return this.locked;
25482     },
25483     
25484     
25485     initEvents : function ()
25486     {
25487         
25488     }
25489 });
25490 /**
25491  * @extends Roo.bootstrap.Table.AbstractSelectionModel
25492  * @class Roo.bootstrap.Table.RowSelectionModel
25493  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
25494  * It supports multiple selections and keyboard selection/navigation. 
25495  * @constructor
25496  * @param {Object} config
25497  */
25498
25499 Roo.bootstrap.Table.RowSelectionModel = function(config){
25500     Roo.apply(this, config);
25501     this.selections = new Roo.util.MixedCollection(false, function(o){
25502         return o.id;
25503     });
25504
25505     this.last = false;
25506     this.lastActive = false;
25507
25508     this.addEvents({
25509         /**
25510              * @event selectionchange
25511              * Fires when the selection changes
25512              * @param {SelectionModel} this
25513              */
25514             "selectionchange" : true,
25515         /**
25516              * @event afterselectionchange
25517              * Fires after the selection changes (eg. by key press or clicking)
25518              * @param {SelectionModel} this
25519              */
25520             "afterselectionchange" : true,
25521         /**
25522              * @event beforerowselect
25523              * Fires when a row is selected being selected, return false to cancel.
25524              * @param {SelectionModel} this
25525              * @param {Number} rowIndex The selected index
25526              * @param {Boolean} keepExisting False if other selections will be cleared
25527              */
25528             "beforerowselect" : true,
25529         /**
25530              * @event rowselect
25531              * Fires when a row is selected.
25532              * @param {SelectionModel} this
25533              * @param {Number} rowIndex The selected index
25534              * @param {Roo.data.Record} r The record
25535              */
25536             "rowselect" : true,
25537         /**
25538              * @event rowdeselect
25539              * Fires when a row is deselected.
25540              * @param {SelectionModel} this
25541              * @param {Number} rowIndex The selected index
25542              */
25543         "rowdeselect" : true
25544     });
25545     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
25546     this.locked = false;
25547  };
25548
25549 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
25550     /**
25551      * @cfg {Boolean} singleSelect
25552      * True to allow selection of only one row at a time (defaults to false)
25553      */
25554     singleSelect : false,
25555
25556     // private
25557     initEvents : function()
25558     {
25559
25560         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
25561         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
25562         //}else{ // allow click to work like normal
25563          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
25564         //}
25565         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
25566         this.grid.on("rowclick", this.handleMouseDown, this);
25567         
25568         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
25569             "up" : function(e){
25570                 if(!e.shiftKey){
25571                     this.selectPrevious(e.shiftKey);
25572                 }else if(this.last !== false && this.lastActive !== false){
25573                     var last = this.last;
25574                     this.selectRange(this.last,  this.lastActive-1);
25575                     this.grid.getView().focusRow(this.lastActive);
25576                     if(last !== false){
25577                         this.last = last;
25578                     }
25579                 }else{
25580                     this.selectFirstRow();
25581                 }
25582                 this.fireEvent("afterselectionchange", this);
25583             },
25584             "down" : function(e){
25585                 if(!e.shiftKey){
25586                     this.selectNext(e.shiftKey);
25587                 }else if(this.last !== false && this.lastActive !== false){
25588                     var last = this.last;
25589                     this.selectRange(this.last,  this.lastActive+1);
25590                     this.grid.getView().focusRow(this.lastActive);
25591                     if(last !== false){
25592                         this.last = last;
25593                     }
25594                 }else{
25595                     this.selectFirstRow();
25596                 }
25597                 this.fireEvent("afterselectionchange", this);
25598             },
25599             scope: this
25600         });
25601         this.grid.store.on('load', function(){
25602             this.selections.clear();
25603         },this);
25604         /*
25605         var view = this.grid.view;
25606         view.on("refresh", this.onRefresh, this);
25607         view.on("rowupdated", this.onRowUpdated, this);
25608         view.on("rowremoved", this.onRemove, this);
25609         */
25610     },
25611
25612     // private
25613     onRefresh : function()
25614     {
25615         var ds = this.grid.store, i, v = this.grid.view;
25616         var s = this.selections;
25617         s.each(function(r){
25618             if((i = ds.indexOfId(r.id)) != -1){
25619                 v.onRowSelect(i);
25620             }else{
25621                 s.remove(r);
25622             }
25623         });
25624     },
25625
25626     // private
25627     onRemove : function(v, index, r){
25628         this.selections.remove(r);
25629     },
25630
25631     // private
25632     onRowUpdated : function(v, index, r){
25633         if(this.isSelected(r)){
25634             v.onRowSelect(index);
25635         }
25636     },
25637
25638     /**
25639      * Select records.
25640      * @param {Array} records The records to select
25641      * @param {Boolean} keepExisting (optional) True to keep existing selections
25642      */
25643     selectRecords : function(records, keepExisting)
25644     {
25645         if(!keepExisting){
25646             this.clearSelections();
25647         }
25648             var ds = this.grid.store;
25649         for(var i = 0, len = records.length; i < len; i++){
25650             this.selectRow(ds.indexOf(records[i]), true);
25651         }
25652     },
25653
25654     /**
25655      * Gets the number of selected rows.
25656      * @return {Number}
25657      */
25658     getCount : function(){
25659         return this.selections.length;
25660     },
25661
25662     /**
25663      * Selects the first row in the grid.
25664      */
25665     selectFirstRow : function(){
25666         this.selectRow(0);
25667     },
25668
25669     /**
25670      * Select the last row.
25671      * @param {Boolean} keepExisting (optional) True to keep existing selections
25672      */
25673     selectLastRow : function(keepExisting){
25674         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
25675         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
25676     },
25677
25678     /**
25679      * Selects the row immediately following the last selected row.
25680      * @param {Boolean} keepExisting (optional) True to keep existing selections
25681      */
25682     selectNext : function(keepExisting)
25683     {
25684             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
25685             this.selectRow(this.last+1, keepExisting);
25686             this.grid.getView().focusRow(this.last);
25687         }
25688     },
25689
25690     /**
25691      * Selects the row that precedes the last selected row.
25692      * @param {Boolean} keepExisting (optional) True to keep existing selections
25693      */
25694     selectPrevious : function(keepExisting){
25695         if(this.last){
25696             this.selectRow(this.last-1, keepExisting);
25697             this.grid.getView().focusRow(this.last);
25698         }
25699     },
25700
25701     /**
25702      * Returns the selected records
25703      * @return {Array} Array of selected records
25704      */
25705     getSelections : function(){
25706         return [].concat(this.selections.items);
25707     },
25708
25709     /**
25710      * Returns the first selected record.
25711      * @return {Record}
25712      */
25713     getSelected : function(){
25714         return this.selections.itemAt(0);
25715     },
25716
25717
25718     /**
25719      * Clears all selections.
25720      */
25721     clearSelections : function(fast)
25722     {
25723         if(this.locked) {
25724             return;
25725         }
25726         if(fast !== true){
25727                 var ds = this.grid.store;
25728             var s = this.selections;
25729             s.each(function(r){
25730                 this.deselectRow(ds.indexOfId(r.id));
25731             }, this);
25732             s.clear();
25733         }else{
25734             this.selections.clear();
25735         }
25736         this.last = false;
25737     },
25738
25739
25740     /**
25741      * Selects all rows.
25742      */
25743     selectAll : function(){
25744         if(this.locked) {
25745             return;
25746         }
25747         this.selections.clear();
25748         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
25749             this.selectRow(i, true);
25750         }
25751     },
25752
25753     /**
25754      * Returns True if there is a selection.
25755      * @return {Boolean}
25756      */
25757     hasSelection : function(){
25758         return this.selections.length > 0;
25759     },
25760
25761     /**
25762      * Returns True if the specified row is selected.
25763      * @param {Number/Record} record The record or index of the record to check
25764      * @return {Boolean}
25765      */
25766     isSelected : function(index){
25767             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
25768         return (r && this.selections.key(r.id) ? true : false);
25769     },
25770
25771     /**
25772      * Returns True if the specified record id is selected.
25773      * @param {String} id The id of record to check
25774      * @return {Boolean}
25775      */
25776     isIdSelected : function(id){
25777         return (this.selections.key(id) ? true : false);
25778     },
25779
25780
25781     // private
25782     handleMouseDBClick : function(e, t){
25783         
25784     },
25785     // private
25786     handleMouseDown : function(e, t)
25787     {
25788             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
25789         if(this.isLocked() || rowIndex < 0 ){
25790             return;
25791         };
25792         if(e.shiftKey && this.last !== false){
25793             var last = this.last;
25794             this.selectRange(last, rowIndex, e.ctrlKey);
25795             this.last = last; // reset the last
25796             t.focus();
25797     
25798         }else{
25799             var isSelected = this.isSelected(rowIndex);
25800             //Roo.log("select row:" + rowIndex);
25801             if(isSelected){
25802                 this.deselectRow(rowIndex);
25803             } else {
25804                         this.selectRow(rowIndex, true);
25805             }
25806     
25807             /*
25808                 if(e.button !== 0 && isSelected){
25809                 alert('rowIndex 2: ' + rowIndex);
25810                     view.focusRow(rowIndex);
25811                 }else if(e.ctrlKey && isSelected){
25812                     this.deselectRow(rowIndex);
25813                 }else if(!isSelected){
25814                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
25815                     view.focusRow(rowIndex);
25816                 }
25817             */
25818         }
25819         this.fireEvent("afterselectionchange", this);
25820     },
25821     // private
25822     handleDragableRowClick :  function(grid, rowIndex, e) 
25823     {
25824         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
25825             this.selectRow(rowIndex, false);
25826             grid.view.focusRow(rowIndex);
25827              this.fireEvent("afterselectionchange", this);
25828         }
25829     },
25830     
25831     /**
25832      * Selects multiple rows.
25833      * @param {Array} rows Array of the indexes of the row to select
25834      * @param {Boolean} keepExisting (optional) True to keep existing selections
25835      */
25836     selectRows : function(rows, keepExisting){
25837         if(!keepExisting){
25838             this.clearSelections();
25839         }
25840         for(var i = 0, len = rows.length; i < len; i++){
25841             this.selectRow(rows[i], true);
25842         }
25843     },
25844
25845     /**
25846      * Selects a range of rows. All rows in between startRow and endRow are also selected.
25847      * @param {Number} startRow The index of the first row in the range
25848      * @param {Number} endRow The index of the last row in the range
25849      * @param {Boolean} keepExisting (optional) True to retain existing selections
25850      */
25851     selectRange : function(startRow, endRow, keepExisting){
25852         if(this.locked) {
25853             return;
25854         }
25855         if(!keepExisting){
25856             this.clearSelections();
25857         }
25858         if(startRow <= endRow){
25859             for(var i = startRow; i <= endRow; i++){
25860                 this.selectRow(i, true);
25861             }
25862         }else{
25863             for(var i = startRow; i >= endRow; i--){
25864                 this.selectRow(i, true);
25865             }
25866         }
25867     },
25868
25869     /**
25870      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
25871      * @param {Number} startRow The index of the first row in the range
25872      * @param {Number} endRow The index of the last row in the range
25873      */
25874     deselectRange : function(startRow, endRow, preventViewNotify){
25875         if(this.locked) {
25876             return;
25877         }
25878         for(var i = startRow; i <= endRow; i++){
25879             this.deselectRow(i, preventViewNotify);
25880         }
25881     },
25882
25883     /**
25884      * Selects a row.
25885      * @param {Number} row The index of the row to select
25886      * @param {Boolean} keepExisting (optional) True to keep existing selections
25887      */
25888     selectRow : function(index, keepExisting, preventViewNotify)
25889     {
25890             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
25891             return;
25892         }
25893         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
25894             if(!keepExisting || this.singleSelect){
25895                 this.clearSelections();
25896             }
25897             
25898             var r = this.grid.store.getAt(index);
25899             //console.log('selectRow - record id :' + r.id);
25900             
25901             this.selections.add(r);
25902             this.last = this.lastActive = index;
25903             if(!preventViewNotify){
25904                 var proxy = new Roo.Element(
25905                                 this.grid.getRowDom(index)
25906                 );
25907                 proxy.addClass('bg-info info');
25908             }
25909             this.fireEvent("rowselect", this, index, r);
25910             this.fireEvent("selectionchange", this);
25911         }
25912     },
25913
25914     /**
25915      * Deselects a row.
25916      * @param {Number} row The index of the row to deselect
25917      */
25918     deselectRow : function(index, preventViewNotify)
25919     {
25920         if(this.locked) {
25921             return;
25922         }
25923         if(this.last == index){
25924             this.last = false;
25925         }
25926         if(this.lastActive == index){
25927             this.lastActive = false;
25928         }
25929         
25930         var r = this.grid.store.getAt(index);
25931         if (!r) {
25932             return;
25933         }
25934         
25935         this.selections.remove(r);
25936         //.console.log('deselectRow - record id :' + r.id);
25937         if(!preventViewNotify){
25938         
25939             var proxy = new Roo.Element(
25940                 this.grid.getRowDom(index)
25941             );
25942             proxy.removeClass('bg-info info');
25943         }
25944         this.fireEvent("rowdeselect", this, index);
25945         this.fireEvent("selectionchange", this);
25946     },
25947
25948     // private
25949     restoreLast : function(){
25950         if(this._last){
25951             this.last = this._last;
25952         }
25953     },
25954
25955     // private
25956     acceptsNav : function(row, col, cm){
25957         return !cm.isHidden(col) && cm.isCellEditable(col, row);
25958     },
25959
25960     // private
25961     onEditorKey : function(field, e){
25962         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
25963         if(k == e.TAB){
25964             e.stopEvent();
25965             ed.completeEdit();
25966             if(e.shiftKey){
25967                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
25968             }else{
25969                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
25970             }
25971         }else if(k == e.ENTER && !e.ctrlKey){
25972             e.stopEvent();
25973             ed.completeEdit();
25974             if(e.shiftKey){
25975                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
25976             }else{
25977                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
25978             }
25979         }else if(k == e.ESC){
25980             ed.cancelEdit();
25981         }
25982         if(newCell){
25983             g.startEditing(newCell[0], newCell[1]);
25984         }
25985     }
25986 });
25987 /*
25988  * Based on:
25989  * Ext JS Library 1.1.1
25990  * Copyright(c) 2006-2007, Ext JS, LLC.
25991  *
25992  * Originally Released Under LGPL - original licence link has changed is not relivant.
25993  *
25994  * Fork - LGPL
25995  * <script type="text/javascript">
25996  */
25997  
25998 /**
25999  * @class Roo.bootstrap.PagingToolbar
26000  * @extends Roo.bootstrap.NavSimplebar
26001  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
26002  * @constructor
26003  * Create a new PagingToolbar
26004  * @param {Object} config The config object
26005  * @param {Roo.data.Store} store
26006  */
26007 Roo.bootstrap.PagingToolbar = function(config)
26008 {
26009     // old args format still supported... - xtype is prefered..
26010         // created from xtype...
26011     
26012     this.ds = config.dataSource;
26013     
26014     if (config.store && !this.ds) {
26015         this.store= Roo.factory(config.store, Roo.data);
26016         this.ds = this.store;
26017         this.ds.xmodule = this.xmodule || false;
26018     }
26019     
26020     this.toolbarItems = [];
26021     if (config.items) {
26022         this.toolbarItems = config.items;
26023     }
26024     
26025     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
26026     
26027     this.cursor = 0;
26028     
26029     if (this.ds) { 
26030         this.bind(this.ds);
26031     }
26032     
26033     if (Roo.bootstrap.version == 4) {
26034         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
26035     } else {
26036         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
26037     }
26038     
26039 };
26040
26041 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
26042     /**
26043      * @cfg {Roo.data.Store} dataSource
26044      * The underlying data store providing the paged data
26045      */
26046     /**
26047      * @cfg {String/HTMLElement/Element} container
26048      * container The id or element that will contain the toolbar
26049      */
26050     /**
26051      * @cfg {Boolean} displayInfo
26052      * True to display the displayMsg (defaults to false)
26053      */
26054     /**
26055      * @cfg {Number} pageSize
26056      * The number of records to display per page (defaults to 20)
26057      */
26058     pageSize: 20,
26059     /**
26060      * @cfg {String} displayMsg
26061      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
26062      */
26063     displayMsg : 'Displaying {0} - {1} of {2}',
26064     /**
26065      * @cfg {String} emptyMsg
26066      * The message to display when no records are found (defaults to "No data to display")
26067      */
26068     emptyMsg : 'No data to display',
26069     /**
26070      * Customizable piece of the default paging text (defaults to "Page")
26071      * @type String
26072      */
26073     beforePageText : "Page",
26074     /**
26075      * Customizable piece of the default paging text (defaults to "of %0")
26076      * @type String
26077      */
26078     afterPageText : "of {0}",
26079     /**
26080      * Customizable piece of the default paging text (defaults to "First Page")
26081      * @type String
26082      */
26083     firstText : "First Page",
26084     /**
26085      * Customizable piece of the default paging text (defaults to "Previous Page")
26086      * @type String
26087      */
26088     prevText : "Previous Page",
26089     /**
26090      * Customizable piece of the default paging text (defaults to "Next Page")
26091      * @type String
26092      */
26093     nextText : "Next Page",
26094     /**
26095      * Customizable piece of the default paging text (defaults to "Last Page")
26096      * @type String
26097      */
26098     lastText : "Last Page",
26099     /**
26100      * Customizable piece of the default paging text (defaults to "Refresh")
26101      * @type String
26102      */
26103     refreshText : "Refresh",
26104
26105     buttons : false,
26106     // private
26107     onRender : function(ct, position) 
26108     {
26109         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
26110         this.navgroup.parentId = this.id;
26111         this.navgroup.onRender(this.el, null);
26112         // add the buttons to the navgroup
26113         
26114         if(this.displayInfo){
26115             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
26116             this.displayEl = this.el.select('.x-paging-info', true).first();
26117 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
26118 //            this.displayEl = navel.el.select('span',true).first();
26119         }
26120         
26121         var _this = this;
26122         
26123         if(this.buttons){
26124             Roo.each(_this.buttons, function(e){ // this might need to use render????
26125                Roo.factory(e).render(_this.el);
26126             });
26127         }
26128             
26129         Roo.each(_this.toolbarItems, function(e) {
26130             _this.navgroup.addItem(e);
26131         });
26132         
26133         
26134         this.first = this.navgroup.addItem({
26135             tooltip: this.firstText,
26136             cls: "prev btn-outline-secondary",
26137             html : ' <i class="fa fa-step-backward"></i>',
26138             disabled: true,
26139             preventDefault: true,
26140             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
26141         });
26142         
26143         this.prev =  this.navgroup.addItem({
26144             tooltip: this.prevText,
26145             cls: "prev btn-outline-secondary",
26146             html : ' <i class="fa fa-backward"></i>',
26147             disabled: true,
26148             preventDefault: true,
26149             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
26150         });
26151     //this.addSeparator();
26152         
26153         
26154         var field = this.navgroup.addItem( {
26155             tagtype : 'span',
26156             cls : 'x-paging-position  btn-outline-secondary',
26157              disabled: true,
26158             html : this.beforePageText  +
26159                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
26160                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
26161          } ); //?? escaped?
26162         
26163         this.field = field.el.select('input', true).first();
26164         this.field.on("keydown", this.onPagingKeydown, this);
26165         this.field.on("focus", function(){this.dom.select();});
26166     
26167     
26168         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
26169         //this.field.setHeight(18);
26170         //this.addSeparator();
26171         this.next = this.navgroup.addItem({
26172             tooltip: this.nextText,
26173             cls: "next btn-outline-secondary",
26174             html : ' <i class="fa fa-forward"></i>',
26175             disabled: true,
26176             preventDefault: true,
26177             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
26178         });
26179         this.last = this.navgroup.addItem({
26180             tooltip: this.lastText,
26181             html : ' <i class="fa fa-step-forward"></i>',
26182             cls: "next btn-outline-secondary",
26183             disabled: true,
26184             preventDefault: true,
26185             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
26186         });
26187     //this.addSeparator();
26188         this.loading = this.navgroup.addItem({
26189             tooltip: this.refreshText,
26190             cls: "btn-outline-secondary",
26191             html : ' <i class="fa fa-refresh"></i>',
26192             preventDefault: true,
26193             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
26194         });
26195         
26196     },
26197
26198     // private
26199     updateInfo : function(){
26200         if(this.displayEl){
26201             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
26202             var msg = count == 0 ?
26203                 this.emptyMsg :
26204                 String.format(
26205                     this.displayMsg,
26206                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
26207                 );
26208             this.displayEl.update(msg);
26209         }
26210     },
26211
26212     // private
26213     onLoad : function(ds, r, o)
26214     {
26215         this.cursor = o.params.start ? o.params.start : 0;
26216         
26217         var d = this.getPageData(),
26218             ap = d.activePage,
26219             ps = d.pages;
26220         
26221         
26222         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
26223         this.field.dom.value = ap;
26224         this.first.setDisabled(ap == 1);
26225         this.prev.setDisabled(ap == 1);
26226         this.next.setDisabled(ap == ps);
26227         this.last.setDisabled(ap == ps);
26228         this.loading.enable();
26229         this.updateInfo();
26230     },
26231
26232     // private
26233     getPageData : function(){
26234         var total = this.ds.getTotalCount();
26235         return {
26236             total : total,
26237             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
26238             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
26239         };
26240     },
26241
26242     // private
26243     onLoadError : function(){
26244         this.loading.enable();
26245     },
26246
26247     // private
26248     onPagingKeydown : function(e){
26249         var k = e.getKey();
26250         var d = this.getPageData();
26251         if(k == e.RETURN){
26252             var v = this.field.dom.value, pageNum;
26253             if(!v || isNaN(pageNum = parseInt(v, 10))){
26254                 this.field.dom.value = d.activePage;
26255                 return;
26256             }
26257             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
26258             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
26259             e.stopEvent();
26260         }
26261         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))
26262         {
26263           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
26264           this.field.dom.value = pageNum;
26265           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
26266           e.stopEvent();
26267         }
26268         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
26269         {
26270           var v = this.field.dom.value, pageNum; 
26271           var increment = (e.shiftKey) ? 10 : 1;
26272           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
26273                 increment *= -1;
26274           }
26275           if(!v || isNaN(pageNum = parseInt(v, 10))) {
26276             this.field.dom.value = d.activePage;
26277             return;
26278           }
26279           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
26280           {
26281             this.field.dom.value = parseInt(v, 10) + increment;
26282             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
26283             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
26284           }
26285           e.stopEvent();
26286         }
26287     },
26288
26289     // private
26290     beforeLoad : function(){
26291         if(this.loading){
26292             this.loading.disable();
26293         }
26294     },
26295
26296     // private
26297     onClick : function(which){
26298         
26299         var ds = this.ds;
26300         if (!ds) {
26301             return;
26302         }
26303         
26304         switch(which){
26305             case "first":
26306                 ds.load({params:{start: 0, limit: this.pageSize}});
26307             break;
26308             case "prev":
26309                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
26310             break;
26311             case "next":
26312                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
26313             break;
26314             case "last":
26315                 var total = ds.getTotalCount();
26316                 var extra = total % this.pageSize;
26317                 var lastStart = extra ? (total - extra) : total-this.pageSize;
26318                 ds.load({params:{start: lastStart, limit: this.pageSize}});
26319             break;
26320             case "refresh":
26321                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
26322             break;
26323         }
26324     },
26325
26326     /**
26327      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
26328      * @param {Roo.data.Store} store The data store to unbind
26329      */
26330     unbind : function(ds){
26331         ds.un("beforeload", this.beforeLoad, this);
26332         ds.un("load", this.onLoad, this);
26333         ds.un("loadexception", this.onLoadError, this);
26334         ds.un("remove", this.updateInfo, this);
26335         ds.un("add", this.updateInfo, this);
26336         this.ds = undefined;
26337     },
26338
26339     /**
26340      * Binds the paging toolbar to the specified {@link Roo.data.Store}
26341      * @param {Roo.data.Store} store The data store to bind
26342      */
26343     bind : function(ds){
26344         ds.on("beforeload", this.beforeLoad, this);
26345         ds.on("load", this.onLoad, this);
26346         ds.on("loadexception", this.onLoadError, this);
26347         ds.on("remove", this.updateInfo, this);
26348         ds.on("add", this.updateInfo, this);
26349         this.ds = ds;
26350     }
26351 });/*
26352  * - LGPL
26353  *
26354  * element
26355  * 
26356  */
26357
26358 /**
26359  * @class Roo.bootstrap.MessageBar
26360  * @extends Roo.bootstrap.Component
26361  * Bootstrap MessageBar class
26362  * @cfg {String} html contents of the MessageBar
26363  * @cfg {String} weight (info | success | warning | danger) default info
26364  * @cfg {String} beforeClass insert the bar before the given class
26365  * @cfg {Boolean} closable (true | false) default false
26366  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
26367  * 
26368  * @constructor
26369  * Create a new Element
26370  * @param {Object} config The config object
26371  */
26372
26373 Roo.bootstrap.MessageBar = function(config){
26374     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
26375 };
26376
26377 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
26378     
26379     html: '',
26380     weight: 'info',
26381     closable: false,
26382     fixed: false,
26383     beforeClass: 'bootstrap-sticky-wrap',
26384     
26385     getAutoCreate : function(){
26386         
26387         var cfg = {
26388             tag: 'div',
26389             cls: 'alert alert-dismissable alert-' + this.weight,
26390             cn: [
26391                 {
26392                     tag: 'span',
26393                     cls: 'message',
26394                     html: this.html || ''
26395                 }
26396             ]
26397         };
26398         
26399         if(this.fixed){
26400             cfg.cls += ' alert-messages-fixed';
26401         }
26402         
26403         if(this.closable){
26404             cfg.cn.push({
26405                 tag: 'button',
26406                 cls: 'close',
26407                 html: 'x'
26408             });
26409         }
26410         
26411         return cfg;
26412     },
26413     
26414     onRender : function(ct, position)
26415     {
26416         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
26417         
26418         if(!this.el){
26419             var cfg = Roo.apply({},  this.getAutoCreate());
26420             cfg.id = Roo.id();
26421             
26422             if (this.cls) {
26423                 cfg.cls += ' ' + this.cls;
26424             }
26425             if (this.style) {
26426                 cfg.style = this.style;
26427             }
26428             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
26429             
26430             this.el.setVisibilityMode(Roo.Element.DISPLAY);
26431         }
26432         
26433         this.el.select('>button.close').on('click', this.hide, this);
26434         
26435     },
26436     
26437     show : function()
26438     {
26439         if (!this.rendered) {
26440             this.render();
26441         }
26442         
26443         this.el.show();
26444         
26445         this.fireEvent('show', this);
26446         
26447     },
26448     
26449     hide : function()
26450     {
26451         if (!this.rendered) {
26452             this.render();
26453         }
26454         
26455         this.el.hide();
26456         
26457         this.fireEvent('hide', this);
26458     },
26459     
26460     update : function()
26461     {
26462 //        var e = this.el.dom.firstChild;
26463 //        
26464 //        if(this.closable){
26465 //            e = e.nextSibling;
26466 //        }
26467 //        
26468 //        e.data = this.html || '';
26469
26470         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
26471     }
26472    
26473 });
26474
26475  
26476
26477      /*
26478  * - LGPL
26479  *
26480  * Graph
26481  * 
26482  */
26483
26484
26485 /**
26486  * @class Roo.bootstrap.Graph
26487  * @extends Roo.bootstrap.Component
26488  * Bootstrap Graph class
26489 > Prameters
26490  -sm {number} sm 4
26491  -md {number} md 5
26492  @cfg {String} graphtype  bar | vbar | pie
26493  @cfg {number} g_x coodinator | centre x (pie)
26494  @cfg {number} g_y coodinator | centre y (pie)
26495  @cfg {number} g_r radius (pie)
26496  @cfg {number} g_height height of the chart (respected by all elements in the set)
26497  @cfg {number} g_width width of the chart (respected by all elements in the set)
26498  @cfg {Object} title The title of the chart
26499     
26500  -{Array}  values
26501  -opts (object) options for the chart 
26502      o {
26503      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
26504      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
26505      o vgutter (number)
26506      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.
26507      o stacked (boolean) whether or not to tread values as in a stacked bar chart
26508      o to
26509      o stretch (boolean)
26510      o }
26511  -opts (object) options for the pie
26512      o{
26513      o cut
26514      o startAngle (number)
26515      o endAngle (number)
26516      } 
26517  *
26518  * @constructor
26519  * Create a new Input
26520  * @param {Object} config The config object
26521  */
26522
26523 Roo.bootstrap.Graph = function(config){
26524     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
26525     
26526     this.addEvents({
26527         // img events
26528         /**
26529          * @event click
26530          * The img click event for the img.
26531          * @param {Roo.EventObject} e
26532          */
26533         "click" : true
26534     });
26535 };
26536
26537 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
26538     
26539     sm: 4,
26540     md: 5,
26541     graphtype: 'bar',
26542     g_height: 250,
26543     g_width: 400,
26544     g_x: 50,
26545     g_y: 50,
26546     g_r: 30,
26547     opts:{
26548         //g_colors: this.colors,
26549         g_type: 'soft',
26550         g_gutter: '20%'
26551
26552     },
26553     title : false,
26554
26555     getAutoCreate : function(){
26556         
26557         var cfg = {
26558             tag: 'div',
26559             html : null
26560         };
26561         
26562         
26563         return  cfg;
26564     },
26565
26566     onRender : function(ct,position){
26567         
26568         
26569         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
26570         
26571         if (typeof(Raphael) == 'undefined') {
26572             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
26573             return;
26574         }
26575         
26576         this.raphael = Raphael(this.el.dom);
26577         
26578                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
26579                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
26580                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
26581                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
26582                 /*
26583                 r.text(160, 10, "Single Series Chart").attr(txtattr);
26584                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
26585                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
26586                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
26587                 
26588                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
26589                 r.barchart(330, 10, 300, 220, data1);
26590                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
26591                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
26592                 */
26593                 
26594                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
26595                 // r.barchart(30, 30, 560, 250,  xdata, {
26596                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
26597                 //     axis : "0 0 1 1",
26598                 //     axisxlabels :  xdata
26599                 //     //yvalues : cols,
26600                    
26601                 // });
26602 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
26603 //        
26604 //        this.load(null,xdata,{
26605 //                axis : "0 0 1 1",
26606 //                axisxlabels :  xdata
26607 //                });
26608
26609     },
26610
26611     load : function(graphtype,xdata,opts)
26612     {
26613         this.raphael.clear();
26614         if(!graphtype) {
26615             graphtype = this.graphtype;
26616         }
26617         if(!opts){
26618             opts = this.opts;
26619         }
26620         var r = this.raphael,
26621             fin = function () {
26622                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
26623             },
26624             fout = function () {
26625                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
26626             },
26627             pfin = function() {
26628                 this.sector.stop();
26629                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
26630
26631                 if (this.label) {
26632                     this.label[0].stop();
26633                     this.label[0].attr({ r: 7.5 });
26634                     this.label[1].attr({ "font-weight": 800 });
26635                 }
26636             },
26637             pfout = function() {
26638                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
26639
26640                 if (this.label) {
26641                     this.label[0].animate({ r: 5 }, 500, "bounce");
26642                     this.label[1].attr({ "font-weight": 400 });
26643                 }
26644             };
26645
26646         switch(graphtype){
26647             case 'bar':
26648                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
26649                 break;
26650             case 'hbar':
26651                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
26652                 break;
26653             case 'pie':
26654 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
26655 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
26656 //            
26657                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
26658                 
26659                 break;
26660
26661         }
26662         
26663         if(this.title){
26664             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
26665         }
26666         
26667     },
26668     
26669     setTitle: function(o)
26670     {
26671         this.title = o;
26672     },
26673     
26674     initEvents: function() {
26675         
26676         if(!this.href){
26677             this.el.on('click', this.onClick, this);
26678         }
26679     },
26680     
26681     onClick : function(e)
26682     {
26683         Roo.log('img onclick');
26684         this.fireEvent('click', this, e);
26685     }
26686    
26687 });
26688
26689  
26690 /*
26691  * - LGPL
26692  *
26693  * numberBox
26694  * 
26695  */
26696 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
26697
26698 /**
26699  * @class Roo.bootstrap.dash.NumberBox
26700  * @extends Roo.bootstrap.Component
26701  * Bootstrap NumberBox class
26702  * @cfg {String} headline Box headline
26703  * @cfg {String} content Box content
26704  * @cfg {String} icon Box icon
26705  * @cfg {String} footer Footer text
26706  * @cfg {String} fhref Footer href
26707  * 
26708  * @constructor
26709  * Create a new NumberBox
26710  * @param {Object} config The config object
26711  */
26712
26713
26714 Roo.bootstrap.dash.NumberBox = function(config){
26715     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
26716     
26717 };
26718
26719 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
26720     
26721     headline : '',
26722     content : '',
26723     icon : '',
26724     footer : '',
26725     fhref : '',
26726     ficon : '',
26727     
26728     getAutoCreate : function(){
26729         
26730         var cfg = {
26731             tag : 'div',
26732             cls : 'small-box ',
26733             cn : [
26734                 {
26735                     tag : 'div',
26736                     cls : 'inner',
26737                     cn :[
26738                         {
26739                             tag : 'h3',
26740                             cls : 'roo-headline',
26741                             html : this.headline
26742                         },
26743                         {
26744                             tag : 'p',
26745                             cls : 'roo-content',
26746                             html : this.content
26747                         }
26748                     ]
26749                 }
26750             ]
26751         };
26752         
26753         if(this.icon){
26754             cfg.cn.push({
26755                 tag : 'div',
26756                 cls : 'icon',
26757                 cn :[
26758                     {
26759                         tag : 'i',
26760                         cls : 'ion ' + this.icon
26761                     }
26762                 ]
26763             });
26764         }
26765         
26766         if(this.footer){
26767             var footer = {
26768                 tag : 'a',
26769                 cls : 'small-box-footer',
26770                 href : this.fhref || '#',
26771                 html : this.footer
26772             };
26773             
26774             cfg.cn.push(footer);
26775             
26776         }
26777         
26778         return  cfg;
26779     },
26780
26781     onRender : function(ct,position){
26782         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
26783
26784
26785        
26786                 
26787     },
26788
26789     setHeadline: function (value)
26790     {
26791         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
26792     },
26793     
26794     setFooter: function (value, href)
26795     {
26796         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
26797         
26798         if(href){
26799             this.el.select('a.small-box-footer',true).first().attr('href', href);
26800         }
26801         
26802     },
26803
26804     setContent: function (value)
26805     {
26806         this.el.select('.roo-content',true).first().dom.innerHTML = value;
26807     },
26808
26809     initEvents: function() 
26810     {   
26811         
26812     }
26813     
26814 });
26815
26816  
26817 /*
26818  * - LGPL
26819  *
26820  * TabBox
26821  * 
26822  */
26823 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
26824
26825 /**
26826  * @class Roo.bootstrap.dash.TabBox
26827  * @extends Roo.bootstrap.Component
26828  * Bootstrap TabBox class
26829  * @cfg {String} title Title of the TabBox
26830  * @cfg {String} icon Icon of the TabBox
26831  * @cfg {Boolean} showtabs (true|false) show the tabs default true
26832  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
26833  * 
26834  * @constructor
26835  * Create a new TabBox
26836  * @param {Object} config The config object
26837  */
26838
26839
26840 Roo.bootstrap.dash.TabBox = function(config){
26841     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
26842     this.addEvents({
26843         // raw events
26844         /**
26845          * @event addpane
26846          * When a pane is added
26847          * @param {Roo.bootstrap.dash.TabPane} pane
26848          */
26849         "addpane" : true,
26850         /**
26851          * @event activatepane
26852          * When a pane is activated
26853          * @param {Roo.bootstrap.dash.TabPane} pane
26854          */
26855         "activatepane" : true
26856         
26857          
26858     });
26859     
26860     this.panes = [];
26861 };
26862
26863 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
26864
26865     title : '',
26866     icon : false,
26867     showtabs : true,
26868     tabScrollable : false,
26869     
26870     getChildContainer : function()
26871     {
26872         return this.el.select('.tab-content', true).first();
26873     },
26874     
26875     getAutoCreate : function(){
26876         
26877         var header = {
26878             tag: 'li',
26879             cls: 'pull-left header',
26880             html: this.title,
26881             cn : []
26882         };
26883         
26884         if(this.icon){
26885             header.cn.push({
26886                 tag: 'i',
26887                 cls: 'fa ' + this.icon
26888             });
26889         }
26890         
26891         var h = {
26892             tag: 'ul',
26893             cls: 'nav nav-tabs pull-right',
26894             cn: [
26895                 header
26896             ]
26897         };
26898         
26899         if(this.tabScrollable){
26900             h = {
26901                 tag: 'div',
26902                 cls: 'tab-header',
26903                 cn: [
26904                     {
26905                         tag: 'ul',
26906                         cls: 'nav nav-tabs pull-right',
26907                         cn: [
26908                             header
26909                         ]
26910                     }
26911                 ]
26912             };
26913         }
26914         
26915         var cfg = {
26916             tag: 'div',
26917             cls: 'nav-tabs-custom',
26918             cn: [
26919                 h,
26920                 {
26921                     tag: 'div',
26922                     cls: 'tab-content no-padding',
26923                     cn: []
26924                 }
26925             ]
26926         };
26927
26928         return  cfg;
26929     },
26930     initEvents : function()
26931     {
26932         //Roo.log('add add pane handler');
26933         this.on('addpane', this.onAddPane, this);
26934     },
26935      /**
26936      * Updates the box title
26937      * @param {String} html to set the title to.
26938      */
26939     setTitle : function(value)
26940     {
26941         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
26942     },
26943     onAddPane : function(pane)
26944     {
26945         this.panes.push(pane);
26946         //Roo.log('addpane');
26947         //Roo.log(pane);
26948         // tabs are rendere left to right..
26949         if(!this.showtabs){
26950             return;
26951         }
26952         
26953         var ctr = this.el.select('.nav-tabs', true).first();
26954          
26955          
26956         var existing = ctr.select('.nav-tab',true);
26957         var qty = existing.getCount();;
26958         
26959         
26960         var tab = ctr.createChild({
26961             tag : 'li',
26962             cls : 'nav-tab' + (qty ? '' : ' active'),
26963             cn : [
26964                 {
26965                     tag : 'a',
26966                     href:'#',
26967                     html : pane.title
26968                 }
26969             ]
26970         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
26971         pane.tab = tab;
26972         
26973         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
26974         if (!qty) {
26975             pane.el.addClass('active');
26976         }
26977         
26978                 
26979     },
26980     onTabClick : function(ev,un,ob,pane)
26981     {
26982         //Roo.log('tab - prev default');
26983         ev.preventDefault();
26984         
26985         
26986         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
26987         pane.tab.addClass('active');
26988         //Roo.log(pane.title);
26989         this.getChildContainer().select('.tab-pane',true).removeClass('active');
26990         // technically we should have a deactivate event.. but maybe add later.
26991         // and it should not de-activate the selected tab...
26992         this.fireEvent('activatepane', pane);
26993         pane.el.addClass('active');
26994         pane.fireEvent('activate');
26995         
26996         
26997     },
26998     
26999     getActivePane : function()
27000     {
27001         var r = false;
27002         Roo.each(this.panes, function(p) {
27003             if(p.el.hasClass('active')){
27004                 r = p;
27005                 return false;
27006             }
27007             
27008             return;
27009         });
27010         
27011         return r;
27012     }
27013     
27014     
27015 });
27016
27017  
27018 /*
27019  * - LGPL
27020  *
27021  * Tab pane
27022  * 
27023  */
27024 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27025 /**
27026  * @class Roo.bootstrap.TabPane
27027  * @extends Roo.bootstrap.Component
27028  * Bootstrap TabPane class
27029  * @cfg {Boolean} active (false | true) Default false
27030  * @cfg {String} title title of panel
27031
27032  * 
27033  * @constructor
27034  * Create a new TabPane
27035  * @param {Object} config The config object
27036  */
27037
27038 Roo.bootstrap.dash.TabPane = function(config){
27039     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
27040     
27041     this.addEvents({
27042         // raw events
27043         /**
27044          * @event activate
27045          * When a pane is activated
27046          * @param {Roo.bootstrap.dash.TabPane} pane
27047          */
27048         "activate" : true
27049          
27050     });
27051 };
27052
27053 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
27054     
27055     active : false,
27056     title : '',
27057     
27058     // the tabBox that this is attached to.
27059     tab : false,
27060      
27061     getAutoCreate : function() 
27062     {
27063         var cfg = {
27064             tag: 'div',
27065             cls: 'tab-pane'
27066         };
27067         
27068         if(this.active){
27069             cfg.cls += ' active';
27070         }
27071         
27072         return cfg;
27073     },
27074     initEvents  : function()
27075     {
27076         //Roo.log('trigger add pane handler');
27077         this.parent().fireEvent('addpane', this)
27078     },
27079     
27080      /**
27081      * Updates the tab title 
27082      * @param {String} html to set the title to.
27083      */
27084     setTitle: function(str)
27085     {
27086         if (!this.tab) {
27087             return;
27088         }
27089         this.title = str;
27090         this.tab.select('a', true).first().dom.innerHTML = str;
27091         
27092     }
27093     
27094     
27095     
27096 });
27097
27098  
27099
27100
27101  /*
27102  * - LGPL
27103  *
27104  * menu
27105  * 
27106  */
27107 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
27108
27109 /**
27110  * @class Roo.bootstrap.menu.Menu
27111  * @extends Roo.bootstrap.Component
27112  * Bootstrap Menu class - container for Menu
27113  * @cfg {String} html Text of the menu
27114  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
27115  * @cfg {String} icon Font awesome icon
27116  * @cfg {String} pos Menu align to (top | bottom) default bottom
27117  * 
27118  * 
27119  * @constructor
27120  * Create a new Menu
27121  * @param {Object} config The config object
27122  */
27123
27124
27125 Roo.bootstrap.menu.Menu = function(config){
27126     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
27127     
27128     this.addEvents({
27129         /**
27130          * @event beforeshow
27131          * Fires before this menu is displayed
27132          * @param {Roo.bootstrap.menu.Menu} this
27133          */
27134         beforeshow : true,
27135         /**
27136          * @event beforehide
27137          * Fires before this menu is hidden
27138          * @param {Roo.bootstrap.menu.Menu} this
27139          */
27140         beforehide : true,
27141         /**
27142          * @event show
27143          * Fires after this menu is displayed
27144          * @param {Roo.bootstrap.menu.Menu} this
27145          */
27146         show : true,
27147         /**
27148          * @event hide
27149          * Fires after this menu is hidden
27150          * @param {Roo.bootstrap.menu.Menu} this
27151          */
27152         hide : true,
27153         /**
27154          * @event click
27155          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
27156          * @param {Roo.bootstrap.menu.Menu} this
27157          * @param {Roo.EventObject} e
27158          */
27159         click : true
27160     });
27161     
27162 };
27163
27164 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
27165     
27166     submenu : false,
27167     html : '',
27168     weight : 'default',
27169     icon : false,
27170     pos : 'bottom',
27171     
27172     
27173     getChildContainer : function() {
27174         if(this.isSubMenu){
27175             return this.el;
27176         }
27177         
27178         return this.el.select('ul.dropdown-menu', true).first();  
27179     },
27180     
27181     getAutoCreate : function()
27182     {
27183         var text = [
27184             {
27185                 tag : 'span',
27186                 cls : 'roo-menu-text',
27187                 html : this.html
27188             }
27189         ];
27190         
27191         if(this.icon){
27192             text.unshift({
27193                 tag : 'i',
27194                 cls : 'fa ' + this.icon
27195             })
27196         }
27197         
27198         
27199         var cfg = {
27200             tag : 'div',
27201             cls : 'btn-group',
27202             cn : [
27203                 {
27204                     tag : 'button',
27205                     cls : 'dropdown-button btn btn-' + this.weight,
27206                     cn : text
27207                 },
27208                 {
27209                     tag : 'button',
27210                     cls : 'dropdown-toggle btn btn-' + this.weight,
27211                     cn : [
27212                         {
27213                             tag : 'span',
27214                             cls : 'caret'
27215                         }
27216                     ]
27217                 },
27218                 {
27219                     tag : 'ul',
27220                     cls : 'dropdown-menu'
27221                 }
27222             ]
27223             
27224         };
27225         
27226         if(this.pos == 'top'){
27227             cfg.cls += ' dropup';
27228         }
27229         
27230         if(this.isSubMenu){
27231             cfg = {
27232                 tag : 'ul',
27233                 cls : 'dropdown-menu'
27234             }
27235         }
27236         
27237         return cfg;
27238     },
27239     
27240     onRender : function(ct, position)
27241     {
27242         this.isSubMenu = ct.hasClass('dropdown-submenu');
27243         
27244         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
27245     },
27246     
27247     initEvents : function() 
27248     {
27249         if(this.isSubMenu){
27250             return;
27251         }
27252         
27253         this.hidden = true;
27254         
27255         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
27256         this.triggerEl.on('click', this.onTriggerPress, this);
27257         
27258         this.buttonEl = this.el.select('button.dropdown-button', true).first();
27259         this.buttonEl.on('click', this.onClick, this);
27260         
27261     },
27262     
27263     list : function()
27264     {
27265         if(this.isSubMenu){
27266             return this.el;
27267         }
27268         
27269         return this.el.select('ul.dropdown-menu', true).first();
27270     },
27271     
27272     onClick : function(e)
27273     {
27274         this.fireEvent("click", this, e);
27275     },
27276     
27277     onTriggerPress  : function(e)
27278     {   
27279         if (this.isVisible()) {
27280             this.hide();
27281         } else {
27282             this.show();
27283         }
27284     },
27285     
27286     isVisible : function(){
27287         return !this.hidden;
27288     },
27289     
27290     show : function()
27291     {
27292         this.fireEvent("beforeshow", this);
27293         
27294         this.hidden = false;
27295         this.el.addClass('open');
27296         
27297         Roo.get(document).on("mouseup", this.onMouseUp, this);
27298         
27299         this.fireEvent("show", this);
27300         
27301         
27302     },
27303     
27304     hide : function()
27305     {
27306         this.fireEvent("beforehide", this);
27307         
27308         this.hidden = true;
27309         this.el.removeClass('open');
27310         
27311         Roo.get(document).un("mouseup", this.onMouseUp);
27312         
27313         this.fireEvent("hide", this);
27314     },
27315     
27316     onMouseUp : function()
27317     {
27318         this.hide();
27319     }
27320     
27321 });
27322
27323  
27324  /*
27325  * - LGPL
27326  *
27327  * menu item
27328  * 
27329  */
27330 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
27331
27332 /**
27333  * @class Roo.bootstrap.menu.Item
27334  * @extends Roo.bootstrap.Component
27335  * Bootstrap MenuItem class
27336  * @cfg {Boolean} submenu (true | false) default false
27337  * @cfg {String} html text of the item
27338  * @cfg {String} href the link
27339  * @cfg {Boolean} disable (true | false) default false
27340  * @cfg {Boolean} preventDefault (true | false) default true
27341  * @cfg {String} icon Font awesome icon
27342  * @cfg {String} pos Submenu align to (left | right) default right 
27343  * 
27344  * 
27345  * @constructor
27346  * Create a new Item
27347  * @param {Object} config The config object
27348  */
27349
27350
27351 Roo.bootstrap.menu.Item = function(config){
27352     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
27353     this.addEvents({
27354         /**
27355          * @event mouseover
27356          * Fires when the mouse is hovering over this menu
27357          * @param {Roo.bootstrap.menu.Item} this
27358          * @param {Roo.EventObject} e
27359          */
27360         mouseover : true,
27361         /**
27362          * @event mouseout
27363          * Fires when the mouse exits this menu
27364          * @param {Roo.bootstrap.menu.Item} this
27365          * @param {Roo.EventObject} e
27366          */
27367         mouseout : true,
27368         // raw events
27369         /**
27370          * @event click
27371          * The raw click event for the entire grid.
27372          * @param {Roo.EventObject} e
27373          */
27374         click : true
27375     });
27376 };
27377
27378 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
27379     
27380     submenu : false,
27381     href : '',
27382     html : '',
27383     preventDefault: true,
27384     disable : false,
27385     icon : false,
27386     pos : 'right',
27387     
27388     getAutoCreate : function()
27389     {
27390         var text = [
27391             {
27392                 tag : 'span',
27393                 cls : 'roo-menu-item-text',
27394                 html : this.html
27395             }
27396         ];
27397         
27398         if(this.icon){
27399             text.unshift({
27400                 tag : 'i',
27401                 cls : 'fa ' + this.icon
27402             })
27403         }
27404         
27405         var cfg = {
27406             tag : 'li',
27407             cn : [
27408                 {
27409                     tag : 'a',
27410                     href : this.href || '#',
27411                     cn : text
27412                 }
27413             ]
27414         };
27415         
27416         if(this.disable){
27417             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
27418         }
27419         
27420         if(this.submenu){
27421             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
27422             
27423             if(this.pos == 'left'){
27424                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
27425             }
27426         }
27427         
27428         return cfg;
27429     },
27430     
27431     initEvents : function() 
27432     {
27433         this.el.on('mouseover', this.onMouseOver, this);
27434         this.el.on('mouseout', this.onMouseOut, this);
27435         
27436         this.el.select('a', true).first().on('click', this.onClick, this);
27437         
27438     },
27439     
27440     onClick : function(e)
27441     {
27442         if(this.preventDefault){
27443             e.preventDefault();
27444         }
27445         
27446         this.fireEvent("click", this, e);
27447     },
27448     
27449     onMouseOver : function(e)
27450     {
27451         if(this.submenu && this.pos == 'left'){
27452             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
27453         }
27454         
27455         this.fireEvent("mouseover", this, e);
27456     },
27457     
27458     onMouseOut : function(e)
27459     {
27460         this.fireEvent("mouseout", this, e);
27461     }
27462 });
27463
27464  
27465
27466  /*
27467  * - LGPL
27468  *
27469  * menu separator
27470  * 
27471  */
27472 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
27473
27474 /**
27475  * @class Roo.bootstrap.menu.Separator
27476  * @extends Roo.bootstrap.Component
27477  * Bootstrap Separator class
27478  * 
27479  * @constructor
27480  * Create a new Separator
27481  * @param {Object} config The config object
27482  */
27483
27484
27485 Roo.bootstrap.menu.Separator = function(config){
27486     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
27487 };
27488
27489 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
27490     
27491     getAutoCreate : function(){
27492         var cfg = {
27493             tag : 'li',
27494             cls: 'divider'
27495         };
27496         
27497         return cfg;
27498     }
27499    
27500 });
27501
27502  
27503
27504  /*
27505  * - LGPL
27506  *
27507  * Tooltip
27508  * 
27509  */
27510
27511 /**
27512  * @class Roo.bootstrap.Tooltip
27513  * Bootstrap Tooltip class
27514  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
27515  * to determine which dom element triggers the tooltip.
27516  * 
27517  * It needs to add support for additional attributes like tooltip-position
27518  * 
27519  * @constructor
27520  * Create a new Toolti
27521  * @param {Object} config The config object
27522  */
27523
27524 Roo.bootstrap.Tooltip = function(config){
27525     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
27526     
27527     this.alignment = Roo.bootstrap.Tooltip.alignment;
27528     
27529     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
27530         this.alignment = config.alignment;
27531     }
27532     
27533 };
27534
27535 Roo.apply(Roo.bootstrap.Tooltip, {
27536     /**
27537      * @function init initialize tooltip monitoring.
27538      * @static
27539      */
27540     currentEl : false,
27541     currentTip : false,
27542     currentRegion : false,
27543     
27544     //  init : delay?
27545     
27546     init : function()
27547     {
27548         Roo.get(document).on('mouseover', this.enter ,this);
27549         Roo.get(document).on('mouseout', this.leave, this);
27550          
27551         
27552         this.currentTip = new Roo.bootstrap.Tooltip();
27553     },
27554     
27555     enter : function(ev)
27556     {
27557         var dom = ev.getTarget();
27558         
27559         //Roo.log(['enter',dom]);
27560         var el = Roo.fly(dom);
27561         if (this.currentEl) {
27562             //Roo.log(dom);
27563             //Roo.log(this.currentEl);
27564             //Roo.log(this.currentEl.contains(dom));
27565             if (this.currentEl == el) {
27566                 return;
27567             }
27568             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
27569                 return;
27570             }
27571
27572         }
27573         
27574         if (this.currentTip.el) {
27575             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
27576         }    
27577         //Roo.log(ev);
27578         
27579         if(!el || el.dom == document){
27580             return;
27581         }
27582         
27583         var bindEl = el;
27584         
27585         // you can not look for children, as if el is the body.. then everythign is the child..
27586         if (!el.attr('tooltip')) { //
27587             if (!el.select("[tooltip]").elements.length) {
27588                 return;
27589             }
27590             // is the mouse over this child...?
27591             bindEl = el.select("[tooltip]").first();
27592             var xy = ev.getXY();
27593             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
27594                 //Roo.log("not in region.");
27595                 return;
27596             }
27597             //Roo.log("child element over..");
27598             
27599         }
27600         this.currentEl = bindEl;
27601         this.currentTip.bind(bindEl);
27602         this.currentRegion = Roo.lib.Region.getRegion(dom);
27603         this.currentTip.enter();
27604         
27605     },
27606     leave : function(ev)
27607     {
27608         var dom = ev.getTarget();
27609         //Roo.log(['leave',dom]);
27610         if (!this.currentEl) {
27611             return;
27612         }
27613         
27614         
27615         if (dom != this.currentEl.dom) {
27616             return;
27617         }
27618         var xy = ev.getXY();
27619         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
27620             return;
27621         }
27622         // only activate leave if mouse cursor is outside... bounding box..
27623         
27624         
27625         
27626         
27627         if (this.currentTip) {
27628             this.currentTip.leave();
27629         }
27630         //Roo.log('clear currentEl');
27631         this.currentEl = false;
27632         
27633         
27634     },
27635     alignment : {
27636         'left' : ['r-l', [-2,0], 'right'],
27637         'right' : ['l-r', [2,0], 'left'],
27638         'bottom' : ['t-b', [0,2], 'top'],
27639         'top' : [ 'b-t', [0,-2], 'bottom']
27640     }
27641     
27642 });
27643
27644
27645 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
27646     
27647     
27648     bindEl : false,
27649     
27650     delay : null, // can be { show : 300 , hide: 500}
27651     
27652     timeout : null,
27653     
27654     hoverState : null, //???
27655     
27656     placement : 'bottom', 
27657     
27658     alignment : false,
27659     
27660     getAutoCreate : function(){
27661     
27662         var cfg = {
27663            cls : 'tooltip',
27664            role : 'tooltip',
27665            cn : [
27666                 {
27667                     cls : 'tooltip-arrow'
27668                 },
27669                 {
27670                     cls : 'tooltip-inner'
27671                 }
27672            ]
27673         };
27674         
27675         return cfg;
27676     },
27677     bind : function(el)
27678     {
27679         this.bindEl = el;
27680     },
27681       
27682     
27683     enter : function () {
27684        
27685         if (this.timeout != null) {
27686             clearTimeout(this.timeout);
27687         }
27688         
27689         this.hoverState = 'in';
27690          //Roo.log("enter - show");
27691         if (!this.delay || !this.delay.show) {
27692             this.show();
27693             return;
27694         }
27695         var _t = this;
27696         this.timeout = setTimeout(function () {
27697             if (_t.hoverState == 'in') {
27698                 _t.show();
27699             }
27700         }, this.delay.show);
27701     },
27702     leave : function()
27703     {
27704         clearTimeout(this.timeout);
27705     
27706         this.hoverState = 'out';
27707          if (!this.delay || !this.delay.hide) {
27708             this.hide();
27709             return;
27710         }
27711        
27712         var _t = this;
27713         this.timeout = setTimeout(function () {
27714             //Roo.log("leave - timeout");
27715             
27716             if (_t.hoverState == 'out') {
27717                 _t.hide();
27718                 Roo.bootstrap.Tooltip.currentEl = false;
27719             }
27720         }, delay);
27721     },
27722     
27723     show : function (msg)
27724     {
27725         if (!this.el) {
27726             this.render(document.body);
27727         }
27728         // set content.
27729         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
27730         
27731         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
27732         
27733         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
27734         
27735         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
27736         
27737         var placement = typeof this.placement == 'function' ?
27738             this.placement.call(this, this.el, on_el) :
27739             this.placement;
27740             
27741         var autoToken = /\s?auto?\s?/i;
27742         var autoPlace = autoToken.test(placement);
27743         if (autoPlace) {
27744             placement = placement.replace(autoToken, '') || 'top';
27745         }
27746         
27747         //this.el.detach()
27748         //this.el.setXY([0,0]);
27749         this.el.show();
27750         //this.el.dom.style.display='block';
27751         
27752         //this.el.appendTo(on_el);
27753         
27754         var p = this.getPosition();
27755         var box = this.el.getBox();
27756         
27757         if (autoPlace) {
27758             // fixme..
27759         }
27760         
27761         var align = this.alignment[placement];
27762         
27763         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
27764         
27765         if(placement == 'top' || placement == 'bottom'){
27766             if(xy[0] < 0){
27767                 placement = 'right';
27768             }
27769             
27770             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
27771                 placement = 'left';
27772             }
27773             
27774             var scroll = Roo.select('body', true).first().getScroll();
27775             
27776             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
27777                 placement = 'top';
27778             }
27779             
27780             align = this.alignment[placement];
27781         }
27782         
27783         this.el.alignTo(this.bindEl, align[0],align[1]);
27784         //var arrow = this.el.select('.arrow',true).first();
27785         //arrow.set(align[2], 
27786         
27787         this.el.addClass(placement);
27788         
27789         this.el.addClass('in fade');
27790         
27791         this.hoverState = null;
27792         
27793         if (this.el.hasClass('fade')) {
27794             // fade it?
27795         }
27796         
27797     },
27798     hide : function()
27799     {
27800          
27801         if (!this.el) {
27802             return;
27803         }
27804         //this.el.setXY([0,0]);
27805         this.el.removeClass('in');
27806         //this.el.hide();
27807         
27808     }
27809     
27810 });
27811  
27812
27813  /*
27814  * - LGPL
27815  *
27816  * Location Picker
27817  * 
27818  */
27819
27820 /**
27821  * @class Roo.bootstrap.LocationPicker
27822  * @extends Roo.bootstrap.Component
27823  * Bootstrap LocationPicker class
27824  * @cfg {Number} latitude Position when init default 0
27825  * @cfg {Number} longitude Position when init default 0
27826  * @cfg {Number} zoom default 15
27827  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
27828  * @cfg {Boolean} mapTypeControl default false
27829  * @cfg {Boolean} disableDoubleClickZoom default false
27830  * @cfg {Boolean} scrollwheel default true
27831  * @cfg {Boolean} streetViewControl default false
27832  * @cfg {Number} radius default 0
27833  * @cfg {String} locationName
27834  * @cfg {Boolean} draggable default true
27835  * @cfg {Boolean} enableAutocomplete default false
27836  * @cfg {Boolean} enableReverseGeocode default true
27837  * @cfg {String} markerTitle
27838  * 
27839  * @constructor
27840  * Create a new LocationPicker
27841  * @param {Object} config The config object
27842  */
27843
27844
27845 Roo.bootstrap.LocationPicker = function(config){
27846     
27847     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
27848     
27849     this.addEvents({
27850         /**
27851          * @event initial
27852          * Fires when the picker initialized.
27853          * @param {Roo.bootstrap.LocationPicker} this
27854          * @param {Google Location} location
27855          */
27856         initial : true,
27857         /**
27858          * @event positionchanged
27859          * Fires when the picker position changed.
27860          * @param {Roo.bootstrap.LocationPicker} this
27861          * @param {Google Location} location
27862          */
27863         positionchanged : true,
27864         /**
27865          * @event resize
27866          * Fires when the map resize.
27867          * @param {Roo.bootstrap.LocationPicker} this
27868          */
27869         resize : true,
27870         /**
27871          * @event show
27872          * Fires when the map show.
27873          * @param {Roo.bootstrap.LocationPicker} this
27874          */
27875         show : true,
27876         /**
27877          * @event hide
27878          * Fires when the map hide.
27879          * @param {Roo.bootstrap.LocationPicker} this
27880          */
27881         hide : true,
27882         /**
27883          * @event mapClick
27884          * Fires when click the map.
27885          * @param {Roo.bootstrap.LocationPicker} this
27886          * @param {Map event} e
27887          */
27888         mapClick : true,
27889         /**
27890          * @event mapRightClick
27891          * Fires when right click the map.
27892          * @param {Roo.bootstrap.LocationPicker} this
27893          * @param {Map event} e
27894          */
27895         mapRightClick : true,
27896         /**
27897          * @event markerClick
27898          * Fires when click the marker.
27899          * @param {Roo.bootstrap.LocationPicker} this
27900          * @param {Map event} e
27901          */
27902         markerClick : true,
27903         /**
27904          * @event markerRightClick
27905          * Fires when right click the marker.
27906          * @param {Roo.bootstrap.LocationPicker} this
27907          * @param {Map event} e
27908          */
27909         markerRightClick : true,
27910         /**
27911          * @event OverlayViewDraw
27912          * Fires when OverlayView Draw
27913          * @param {Roo.bootstrap.LocationPicker} this
27914          */
27915         OverlayViewDraw : true,
27916         /**
27917          * @event OverlayViewOnAdd
27918          * Fires when OverlayView Draw
27919          * @param {Roo.bootstrap.LocationPicker} this
27920          */
27921         OverlayViewOnAdd : true,
27922         /**
27923          * @event OverlayViewOnRemove
27924          * Fires when OverlayView Draw
27925          * @param {Roo.bootstrap.LocationPicker} this
27926          */
27927         OverlayViewOnRemove : true,
27928         /**
27929          * @event OverlayViewShow
27930          * Fires when OverlayView Draw
27931          * @param {Roo.bootstrap.LocationPicker} this
27932          * @param {Pixel} cpx
27933          */
27934         OverlayViewShow : true,
27935         /**
27936          * @event OverlayViewHide
27937          * Fires when OverlayView Draw
27938          * @param {Roo.bootstrap.LocationPicker} this
27939          */
27940         OverlayViewHide : true,
27941         /**
27942          * @event loadexception
27943          * Fires when load google lib failed.
27944          * @param {Roo.bootstrap.LocationPicker} this
27945          */
27946         loadexception : true
27947     });
27948         
27949 };
27950
27951 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
27952     
27953     gMapContext: false,
27954     
27955     latitude: 0,
27956     longitude: 0,
27957     zoom: 15,
27958     mapTypeId: false,
27959     mapTypeControl: false,
27960     disableDoubleClickZoom: false,
27961     scrollwheel: true,
27962     streetViewControl: false,
27963     radius: 0,
27964     locationName: '',
27965     draggable: true,
27966     enableAutocomplete: false,
27967     enableReverseGeocode: true,
27968     markerTitle: '',
27969     
27970     getAutoCreate: function()
27971     {
27972
27973         var cfg = {
27974             tag: 'div',
27975             cls: 'roo-location-picker'
27976         };
27977         
27978         return cfg
27979     },
27980     
27981     initEvents: function(ct, position)
27982     {       
27983         if(!this.el.getWidth() || this.isApplied()){
27984             return;
27985         }
27986         
27987         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27988         
27989         this.initial();
27990     },
27991     
27992     initial: function()
27993     {
27994         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
27995             this.fireEvent('loadexception', this);
27996             return;
27997         }
27998         
27999         if(!this.mapTypeId){
28000             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
28001         }
28002         
28003         this.gMapContext = this.GMapContext();
28004         
28005         this.initOverlayView();
28006         
28007         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
28008         
28009         var _this = this;
28010                 
28011         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
28012             _this.setPosition(_this.gMapContext.marker.position);
28013         });
28014         
28015         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
28016             _this.fireEvent('mapClick', this, event);
28017             
28018         });
28019
28020         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
28021             _this.fireEvent('mapRightClick', this, event);
28022             
28023         });
28024         
28025         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
28026             _this.fireEvent('markerClick', this, event);
28027             
28028         });
28029
28030         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
28031             _this.fireEvent('markerRightClick', this, event);
28032             
28033         });
28034         
28035         this.setPosition(this.gMapContext.location);
28036         
28037         this.fireEvent('initial', this, this.gMapContext.location);
28038     },
28039     
28040     initOverlayView: function()
28041     {
28042         var _this = this;
28043         
28044         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
28045             
28046             draw: function()
28047             {
28048                 _this.fireEvent('OverlayViewDraw', _this);
28049             },
28050             
28051             onAdd: function()
28052             {
28053                 _this.fireEvent('OverlayViewOnAdd', _this);
28054             },
28055             
28056             onRemove: function()
28057             {
28058                 _this.fireEvent('OverlayViewOnRemove', _this);
28059             },
28060             
28061             show: function(cpx)
28062             {
28063                 _this.fireEvent('OverlayViewShow', _this, cpx);
28064             },
28065             
28066             hide: function()
28067             {
28068                 _this.fireEvent('OverlayViewHide', _this);
28069             }
28070             
28071         });
28072     },
28073     
28074     fromLatLngToContainerPixel: function(event)
28075     {
28076         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
28077     },
28078     
28079     isApplied: function() 
28080     {
28081         return this.getGmapContext() == false ? false : true;
28082     },
28083     
28084     getGmapContext: function() 
28085     {
28086         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
28087     },
28088     
28089     GMapContext: function() 
28090     {
28091         var position = new google.maps.LatLng(this.latitude, this.longitude);
28092         
28093         var _map = new google.maps.Map(this.el.dom, {
28094             center: position,
28095             zoom: this.zoom,
28096             mapTypeId: this.mapTypeId,
28097             mapTypeControl: this.mapTypeControl,
28098             disableDoubleClickZoom: this.disableDoubleClickZoom,
28099             scrollwheel: this.scrollwheel,
28100             streetViewControl: this.streetViewControl,
28101             locationName: this.locationName,
28102             draggable: this.draggable,
28103             enableAutocomplete: this.enableAutocomplete,
28104             enableReverseGeocode: this.enableReverseGeocode
28105         });
28106         
28107         var _marker = new google.maps.Marker({
28108             position: position,
28109             map: _map,
28110             title: this.markerTitle,
28111             draggable: this.draggable
28112         });
28113         
28114         return {
28115             map: _map,
28116             marker: _marker,
28117             circle: null,
28118             location: position,
28119             radius: this.radius,
28120             locationName: this.locationName,
28121             addressComponents: {
28122                 formatted_address: null,
28123                 addressLine1: null,
28124                 addressLine2: null,
28125                 streetName: null,
28126                 streetNumber: null,
28127                 city: null,
28128                 district: null,
28129                 state: null,
28130                 stateOrProvince: null
28131             },
28132             settings: this,
28133             domContainer: this.el.dom,
28134             geodecoder: new google.maps.Geocoder()
28135         };
28136     },
28137     
28138     drawCircle: function(center, radius, options) 
28139     {
28140         if (this.gMapContext.circle != null) {
28141             this.gMapContext.circle.setMap(null);
28142         }
28143         if (radius > 0) {
28144             radius *= 1;
28145             options = Roo.apply({}, options, {
28146                 strokeColor: "#0000FF",
28147                 strokeOpacity: .35,
28148                 strokeWeight: 2,
28149                 fillColor: "#0000FF",
28150                 fillOpacity: .2
28151             });
28152             
28153             options.map = this.gMapContext.map;
28154             options.radius = radius;
28155             options.center = center;
28156             this.gMapContext.circle = new google.maps.Circle(options);
28157             return this.gMapContext.circle;
28158         }
28159         
28160         return null;
28161     },
28162     
28163     setPosition: function(location) 
28164     {
28165         this.gMapContext.location = location;
28166         this.gMapContext.marker.setPosition(location);
28167         this.gMapContext.map.panTo(location);
28168         this.drawCircle(location, this.gMapContext.radius, {});
28169         
28170         var _this = this;
28171         
28172         if (this.gMapContext.settings.enableReverseGeocode) {
28173             this.gMapContext.geodecoder.geocode({
28174                 latLng: this.gMapContext.location
28175             }, function(results, status) {
28176                 
28177                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
28178                     _this.gMapContext.locationName = results[0].formatted_address;
28179                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
28180                     
28181                     _this.fireEvent('positionchanged', this, location);
28182                 }
28183             });
28184             
28185             return;
28186         }
28187         
28188         this.fireEvent('positionchanged', this, location);
28189     },
28190     
28191     resize: function()
28192     {
28193         google.maps.event.trigger(this.gMapContext.map, "resize");
28194         
28195         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
28196         
28197         this.fireEvent('resize', this);
28198     },
28199     
28200     setPositionByLatLng: function(latitude, longitude)
28201     {
28202         this.setPosition(new google.maps.LatLng(latitude, longitude));
28203     },
28204     
28205     getCurrentPosition: function() 
28206     {
28207         return {
28208             latitude: this.gMapContext.location.lat(),
28209             longitude: this.gMapContext.location.lng()
28210         };
28211     },
28212     
28213     getAddressName: function() 
28214     {
28215         return this.gMapContext.locationName;
28216     },
28217     
28218     getAddressComponents: function() 
28219     {
28220         return this.gMapContext.addressComponents;
28221     },
28222     
28223     address_component_from_google_geocode: function(address_components) 
28224     {
28225         var result = {};
28226         
28227         for (var i = 0; i < address_components.length; i++) {
28228             var component = address_components[i];
28229             if (component.types.indexOf("postal_code") >= 0) {
28230                 result.postalCode = component.short_name;
28231             } else if (component.types.indexOf("street_number") >= 0) {
28232                 result.streetNumber = component.short_name;
28233             } else if (component.types.indexOf("route") >= 0) {
28234                 result.streetName = component.short_name;
28235             } else if (component.types.indexOf("neighborhood") >= 0) {
28236                 result.city = component.short_name;
28237             } else if (component.types.indexOf("locality") >= 0) {
28238                 result.city = component.short_name;
28239             } else if (component.types.indexOf("sublocality") >= 0) {
28240                 result.district = component.short_name;
28241             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
28242                 result.stateOrProvince = component.short_name;
28243             } else if (component.types.indexOf("country") >= 0) {
28244                 result.country = component.short_name;
28245             }
28246         }
28247         
28248         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
28249         result.addressLine2 = "";
28250         return result;
28251     },
28252     
28253     setZoomLevel: function(zoom)
28254     {
28255         this.gMapContext.map.setZoom(zoom);
28256     },
28257     
28258     show: function()
28259     {
28260         if(!this.el){
28261             return;
28262         }
28263         
28264         this.el.show();
28265         
28266         this.resize();
28267         
28268         this.fireEvent('show', this);
28269     },
28270     
28271     hide: function()
28272     {
28273         if(!this.el){
28274             return;
28275         }
28276         
28277         this.el.hide();
28278         
28279         this.fireEvent('hide', this);
28280     }
28281     
28282 });
28283
28284 Roo.apply(Roo.bootstrap.LocationPicker, {
28285     
28286     OverlayView : function(map, options)
28287     {
28288         options = options || {};
28289         
28290         this.setMap(map);
28291     }
28292     
28293     
28294 });/**
28295  * @class Roo.bootstrap.Alert
28296  * @extends Roo.bootstrap.Component
28297  * Bootstrap Alert class - shows an alert area box
28298  * eg
28299  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
28300   Enter a valid email address
28301 </div>
28302  * @licence LGPL
28303  * @cfg {String} title The title of alert
28304  * @cfg {String} html The content of alert
28305  * @cfg {String} weight (  success | info | warning | danger )
28306  * @cfg {String} faicon font-awesomeicon
28307  * 
28308  * @constructor
28309  * Create a new alert
28310  * @param {Object} config The config object
28311  */
28312
28313
28314 Roo.bootstrap.Alert = function(config){
28315     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
28316     
28317 };
28318
28319 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
28320     
28321     title: '',
28322     html: '',
28323     weight: false,
28324     faicon: false,
28325     
28326     getAutoCreate : function()
28327     {
28328         
28329         var cfg = {
28330             tag : 'div',
28331             cls : 'alert',
28332             cn : [
28333                 {
28334                     tag : 'i',
28335                     cls : 'roo-alert-icon'
28336                     
28337                 },
28338                 {
28339                     tag : 'b',
28340                     cls : 'roo-alert-title',
28341                     html : this.title
28342                 },
28343                 {
28344                     tag : 'span',
28345                     cls : 'roo-alert-text',
28346                     html : this.html
28347                 }
28348             ]
28349         };
28350         
28351         if(this.faicon){
28352             cfg.cn[0].cls += ' fa ' + this.faicon;
28353         }
28354         
28355         if(this.weight){
28356             cfg.cls += ' alert-' + this.weight;
28357         }
28358         
28359         return cfg;
28360     },
28361     
28362     initEvents: function() 
28363     {
28364         this.el.setVisibilityMode(Roo.Element.DISPLAY);
28365     },
28366     
28367     setTitle : function(str)
28368     {
28369         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
28370     },
28371     
28372     setText : function(str)
28373     {
28374         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
28375     },
28376     
28377     setWeight : function(weight)
28378     {
28379         if(this.weight){
28380             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
28381         }
28382         
28383         this.weight = weight;
28384         
28385         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
28386     },
28387     
28388     setIcon : function(icon)
28389     {
28390         if(this.faicon){
28391             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
28392         }
28393         
28394         this.faicon = icon;
28395         
28396         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
28397     },
28398     
28399     hide: function() 
28400     {
28401         this.el.hide();   
28402     },
28403     
28404     show: function() 
28405     {  
28406         this.el.show();   
28407     }
28408     
28409 });
28410
28411  
28412 /*
28413 * Licence: LGPL
28414 */
28415
28416 /**
28417  * @class Roo.bootstrap.UploadCropbox
28418  * @extends Roo.bootstrap.Component
28419  * Bootstrap UploadCropbox class
28420  * @cfg {String} emptyText show when image has been loaded
28421  * @cfg {String} rotateNotify show when image too small to rotate
28422  * @cfg {Number} errorTimeout default 3000
28423  * @cfg {Number} minWidth default 300
28424  * @cfg {Number} minHeight default 300
28425  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
28426  * @cfg {Boolean} isDocument (true|false) default false
28427  * @cfg {String} url action url
28428  * @cfg {String} paramName default 'imageUpload'
28429  * @cfg {String} method default POST
28430  * @cfg {Boolean} loadMask (true|false) default true
28431  * @cfg {Boolean} loadingText default 'Loading...'
28432  * 
28433  * @constructor
28434  * Create a new UploadCropbox
28435  * @param {Object} config The config object
28436  */
28437
28438 Roo.bootstrap.UploadCropbox = function(config){
28439     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
28440     
28441     this.addEvents({
28442         /**
28443          * @event beforeselectfile
28444          * Fire before select file
28445          * @param {Roo.bootstrap.UploadCropbox} this
28446          */
28447         "beforeselectfile" : true,
28448         /**
28449          * @event initial
28450          * Fire after initEvent
28451          * @param {Roo.bootstrap.UploadCropbox} this
28452          */
28453         "initial" : true,
28454         /**
28455          * @event crop
28456          * Fire after initEvent
28457          * @param {Roo.bootstrap.UploadCropbox} this
28458          * @param {String} data
28459          */
28460         "crop" : true,
28461         /**
28462          * @event prepare
28463          * Fire when preparing the file data
28464          * @param {Roo.bootstrap.UploadCropbox} this
28465          * @param {Object} file
28466          */
28467         "prepare" : true,
28468         /**
28469          * @event exception
28470          * Fire when get exception
28471          * @param {Roo.bootstrap.UploadCropbox} this
28472          * @param {XMLHttpRequest} xhr
28473          */
28474         "exception" : true,
28475         /**
28476          * @event beforeloadcanvas
28477          * Fire before load the canvas
28478          * @param {Roo.bootstrap.UploadCropbox} this
28479          * @param {String} src
28480          */
28481         "beforeloadcanvas" : true,
28482         /**
28483          * @event trash
28484          * Fire when trash image
28485          * @param {Roo.bootstrap.UploadCropbox} this
28486          */
28487         "trash" : true,
28488         /**
28489          * @event download
28490          * Fire when download the image
28491          * @param {Roo.bootstrap.UploadCropbox} this
28492          */
28493         "download" : true,
28494         /**
28495          * @event footerbuttonclick
28496          * Fire when footerbuttonclick
28497          * @param {Roo.bootstrap.UploadCropbox} this
28498          * @param {String} type
28499          */
28500         "footerbuttonclick" : true,
28501         /**
28502          * @event resize
28503          * Fire when resize
28504          * @param {Roo.bootstrap.UploadCropbox} this
28505          */
28506         "resize" : true,
28507         /**
28508          * @event rotate
28509          * Fire when rotate the image
28510          * @param {Roo.bootstrap.UploadCropbox} this
28511          * @param {String} pos
28512          */
28513         "rotate" : true,
28514         /**
28515          * @event inspect
28516          * Fire when inspect the file
28517          * @param {Roo.bootstrap.UploadCropbox} this
28518          * @param {Object} file
28519          */
28520         "inspect" : true,
28521         /**
28522          * @event upload
28523          * Fire when xhr upload the file
28524          * @param {Roo.bootstrap.UploadCropbox} this
28525          * @param {Object} data
28526          */
28527         "upload" : true,
28528         /**
28529          * @event arrange
28530          * Fire when arrange the file data
28531          * @param {Roo.bootstrap.UploadCropbox} this
28532          * @param {Object} formData
28533          */
28534         "arrange" : true
28535     });
28536     
28537     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
28538 };
28539
28540 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
28541     
28542     emptyText : 'Click to upload image',
28543     rotateNotify : 'Image is too small to rotate',
28544     errorTimeout : 3000,
28545     scale : 0,
28546     baseScale : 1,
28547     rotate : 0,
28548     dragable : false,
28549     pinching : false,
28550     mouseX : 0,
28551     mouseY : 0,
28552     cropData : false,
28553     minWidth : 300,
28554     minHeight : 300,
28555     file : false,
28556     exif : {},
28557     baseRotate : 1,
28558     cropType : 'image/jpeg',
28559     buttons : false,
28560     canvasLoaded : false,
28561     isDocument : false,
28562     method : 'POST',
28563     paramName : 'imageUpload',
28564     loadMask : true,
28565     loadingText : 'Loading...',
28566     maskEl : false,
28567     
28568     getAutoCreate : function()
28569     {
28570         var cfg = {
28571             tag : 'div',
28572             cls : 'roo-upload-cropbox',
28573             cn : [
28574                 {
28575                     tag : 'input',
28576                     cls : 'roo-upload-cropbox-selector',
28577                     type : 'file'
28578                 },
28579                 {
28580                     tag : 'div',
28581                     cls : 'roo-upload-cropbox-body',
28582                     style : 'cursor:pointer',
28583                     cn : [
28584                         {
28585                             tag : 'div',
28586                             cls : 'roo-upload-cropbox-preview'
28587                         },
28588                         {
28589                             tag : 'div',
28590                             cls : 'roo-upload-cropbox-thumb'
28591                         },
28592                         {
28593                             tag : 'div',
28594                             cls : 'roo-upload-cropbox-empty-notify',
28595                             html : this.emptyText
28596                         },
28597                         {
28598                             tag : 'div',
28599                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
28600                             html : this.rotateNotify
28601                         }
28602                     ]
28603                 },
28604                 {
28605                     tag : 'div',
28606                     cls : 'roo-upload-cropbox-footer',
28607                     cn : {
28608                         tag : 'div',
28609                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
28610                         cn : []
28611                     }
28612                 }
28613             ]
28614         };
28615         
28616         return cfg;
28617     },
28618     
28619     onRender : function(ct, position)
28620     {
28621         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
28622         
28623         if (this.buttons.length) {
28624             
28625             Roo.each(this.buttons, function(bb) {
28626                 
28627                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
28628                 
28629                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
28630                 
28631             }, this);
28632         }
28633         
28634         if(this.loadMask){
28635             this.maskEl = this.el;
28636         }
28637     },
28638     
28639     initEvents : function()
28640     {
28641         this.urlAPI = (window.createObjectURL && window) || 
28642                                 (window.URL && URL.revokeObjectURL && URL) || 
28643                                 (window.webkitURL && webkitURL);
28644                         
28645         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
28646         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28647         
28648         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
28649         this.selectorEl.hide();
28650         
28651         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
28652         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28653         
28654         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
28655         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28656         this.thumbEl.hide();
28657         
28658         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
28659         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28660         
28661         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
28662         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28663         this.errorEl.hide();
28664         
28665         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
28666         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28667         this.footerEl.hide();
28668         
28669         this.setThumbBoxSize();
28670         
28671         this.bind();
28672         
28673         this.resize();
28674         
28675         this.fireEvent('initial', this);
28676     },
28677
28678     bind : function()
28679     {
28680         var _this = this;
28681         
28682         window.addEventListener("resize", function() { _this.resize(); } );
28683         
28684         this.bodyEl.on('click', this.beforeSelectFile, this);
28685         
28686         if(Roo.isTouch){
28687             this.bodyEl.on('touchstart', this.onTouchStart, this);
28688             this.bodyEl.on('touchmove', this.onTouchMove, this);
28689             this.bodyEl.on('touchend', this.onTouchEnd, this);
28690         }
28691         
28692         if(!Roo.isTouch){
28693             this.bodyEl.on('mousedown', this.onMouseDown, this);
28694             this.bodyEl.on('mousemove', this.onMouseMove, this);
28695             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
28696             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
28697             Roo.get(document).on('mouseup', this.onMouseUp, this);
28698         }
28699         
28700         this.selectorEl.on('change', this.onFileSelected, this);
28701     },
28702     
28703     reset : function()
28704     {    
28705         this.scale = 0;
28706         this.baseScale = 1;
28707         this.rotate = 0;
28708         this.baseRotate = 1;
28709         this.dragable = false;
28710         this.pinching = false;
28711         this.mouseX = 0;
28712         this.mouseY = 0;
28713         this.cropData = false;
28714         this.notifyEl.dom.innerHTML = this.emptyText;
28715         
28716         this.selectorEl.dom.value = '';
28717         
28718     },
28719     
28720     resize : function()
28721     {
28722         if(this.fireEvent('resize', this) != false){
28723             this.setThumbBoxPosition();
28724             this.setCanvasPosition();
28725         }
28726     },
28727     
28728     onFooterButtonClick : function(e, el, o, type)
28729     {
28730         switch (type) {
28731             case 'rotate-left' :
28732                 this.onRotateLeft(e);
28733                 break;
28734             case 'rotate-right' :
28735                 this.onRotateRight(e);
28736                 break;
28737             case 'picture' :
28738                 this.beforeSelectFile(e);
28739                 break;
28740             case 'trash' :
28741                 this.trash(e);
28742                 break;
28743             case 'crop' :
28744                 this.crop(e);
28745                 break;
28746             case 'download' :
28747                 this.download(e);
28748                 break;
28749             default :
28750                 break;
28751         }
28752         
28753         this.fireEvent('footerbuttonclick', this, type);
28754     },
28755     
28756     beforeSelectFile : function(e)
28757     {
28758         e.preventDefault();
28759         
28760         if(this.fireEvent('beforeselectfile', this) != false){
28761             this.selectorEl.dom.click();
28762         }
28763     },
28764     
28765     onFileSelected : function(e)
28766     {
28767         e.preventDefault();
28768         
28769         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28770             return;
28771         }
28772         
28773         var file = this.selectorEl.dom.files[0];
28774         
28775         if(this.fireEvent('inspect', this, file) != false){
28776             this.prepare(file);
28777         }
28778         
28779     },
28780     
28781     trash : function(e)
28782     {
28783         this.fireEvent('trash', this);
28784     },
28785     
28786     download : function(e)
28787     {
28788         this.fireEvent('download', this);
28789     },
28790     
28791     loadCanvas : function(src)
28792     {   
28793         if(this.fireEvent('beforeloadcanvas', this, src) != false){
28794             
28795             this.reset();
28796             
28797             this.imageEl = document.createElement('img');
28798             
28799             var _this = this;
28800             
28801             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
28802             
28803             this.imageEl.src = src;
28804         }
28805     },
28806     
28807     onLoadCanvas : function()
28808     {   
28809         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
28810         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
28811         
28812         this.bodyEl.un('click', this.beforeSelectFile, this);
28813         
28814         this.notifyEl.hide();
28815         this.thumbEl.show();
28816         this.footerEl.show();
28817         
28818         this.baseRotateLevel();
28819         
28820         if(this.isDocument){
28821             this.setThumbBoxSize();
28822         }
28823         
28824         this.setThumbBoxPosition();
28825         
28826         this.baseScaleLevel();
28827         
28828         this.draw();
28829         
28830         this.resize();
28831         
28832         this.canvasLoaded = true;
28833         
28834         if(this.loadMask){
28835             this.maskEl.unmask();
28836         }
28837         
28838     },
28839     
28840     setCanvasPosition : function()
28841     {   
28842         if(!this.canvasEl){
28843             return;
28844         }
28845         
28846         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
28847         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
28848         
28849         this.previewEl.setLeft(pw);
28850         this.previewEl.setTop(ph);
28851         
28852     },
28853     
28854     onMouseDown : function(e)
28855     {   
28856         e.stopEvent();
28857         
28858         this.dragable = true;
28859         this.pinching = false;
28860         
28861         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
28862             this.dragable = false;
28863             return;
28864         }
28865         
28866         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
28867         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
28868         
28869     },
28870     
28871     onMouseMove : function(e)
28872     {   
28873         e.stopEvent();
28874         
28875         if(!this.canvasLoaded){
28876             return;
28877         }
28878         
28879         if (!this.dragable){
28880             return;
28881         }
28882         
28883         var minX = Math.ceil(this.thumbEl.getLeft(true));
28884         var minY = Math.ceil(this.thumbEl.getTop(true));
28885         
28886         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
28887         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
28888         
28889         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
28890         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
28891         
28892         x = x - this.mouseX;
28893         y = y - this.mouseY;
28894         
28895         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
28896         var bgY = Math.ceil(y + this.previewEl.getTop(true));
28897         
28898         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
28899         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
28900         
28901         this.previewEl.setLeft(bgX);
28902         this.previewEl.setTop(bgY);
28903         
28904         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
28905         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
28906     },
28907     
28908     onMouseUp : function(e)
28909     {   
28910         e.stopEvent();
28911         
28912         this.dragable = false;
28913     },
28914     
28915     onMouseWheel : function(e)
28916     {   
28917         e.stopEvent();
28918         
28919         this.startScale = this.scale;
28920         
28921         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
28922         
28923         if(!this.zoomable()){
28924             this.scale = this.startScale;
28925             return;
28926         }
28927         
28928         this.draw();
28929         
28930         return;
28931     },
28932     
28933     zoomable : function()
28934     {
28935         var minScale = this.thumbEl.getWidth() / this.minWidth;
28936         
28937         if(this.minWidth < this.minHeight){
28938             minScale = this.thumbEl.getHeight() / this.minHeight;
28939         }
28940         
28941         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
28942         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
28943         
28944         if(
28945                 this.isDocument &&
28946                 (this.rotate == 0 || this.rotate == 180) && 
28947                 (
28948                     width > this.imageEl.OriginWidth || 
28949                     height > this.imageEl.OriginHeight ||
28950                     (width < this.minWidth && height < this.minHeight)
28951                 )
28952         ){
28953             return false;
28954         }
28955         
28956         if(
28957                 this.isDocument &&
28958                 (this.rotate == 90 || this.rotate == 270) && 
28959                 (
28960                     width > this.imageEl.OriginWidth || 
28961                     height > this.imageEl.OriginHeight ||
28962                     (width < this.minHeight && height < this.minWidth)
28963                 )
28964         ){
28965             return false;
28966         }
28967         
28968         if(
28969                 !this.isDocument &&
28970                 (this.rotate == 0 || this.rotate == 180) && 
28971                 (
28972                     width < this.minWidth || 
28973                     width > this.imageEl.OriginWidth || 
28974                     height < this.minHeight || 
28975                     height > this.imageEl.OriginHeight
28976                 )
28977         ){
28978             return false;
28979         }
28980         
28981         if(
28982                 !this.isDocument &&
28983                 (this.rotate == 90 || this.rotate == 270) && 
28984                 (
28985                     width < this.minHeight || 
28986                     width > this.imageEl.OriginWidth || 
28987                     height < this.minWidth || 
28988                     height > this.imageEl.OriginHeight
28989                 )
28990         ){
28991             return false;
28992         }
28993         
28994         return true;
28995         
28996     },
28997     
28998     onRotateLeft : function(e)
28999     {   
29000         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29001             
29002             var minScale = this.thumbEl.getWidth() / this.minWidth;
29003             
29004             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29005             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29006             
29007             this.startScale = this.scale;
29008             
29009             while (this.getScaleLevel() < minScale){
29010             
29011                 this.scale = this.scale + 1;
29012                 
29013                 if(!this.zoomable()){
29014                     break;
29015                 }
29016                 
29017                 if(
29018                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29019                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29020                 ){
29021                     continue;
29022                 }
29023                 
29024                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29025
29026                 this.draw();
29027                 
29028                 return;
29029             }
29030             
29031             this.scale = this.startScale;
29032             
29033             this.onRotateFail();
29034             
29035             return false;
29036         }
29037         
29038         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29039
29040         if(this.isDocument){
29041             this.setThumbBoxSize();
29042             this.setThumbBoxPosition();
29043             this.setCanvasPosition();
29044         }
29045         
29046         this.draw();
29047         
29048         this.fireEvent('rotate', this, 'left');
29049         
29050     },
29051     
29052     onRotateRight : function(e)
29053     {
29054         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29055             
29056             var minScale = this.thumbEl.getWidth() / this.minWidth;
29057         
29058             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29059             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29060             
29061             this.startScale = this.scale;
29062             
29063             while (this.getScaleLevel() < minScale){
29064             
29065                 this.scale = this.scale + 1;
29066                 
29067                 if(!this.zoomable()){
29068                     break;
29069                 }
29070                 
29071                 if(
29072                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29073                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29074                 ){
29075                     continue;
29076                 }
29077                 
29078                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29079
29080                 this.draw();
29081                 
29082                 return;
29083             }
29084             
29085             this.scale = this.startScale;
29086             
29087             this.onRotateFail();
29088             
29089             return false;
29090         }
29091         
29092         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29093
29094         if(this.isDocument){
29095             this.setThumbBoxSize();
29096             this.setThumbBoxPosition();
29097             this.setCanvasPosition();
29098         }
29099         
29100         this.draw();
29101         
29102         this.fireEvent('rotate', this, 'right');
29103     },
29104     
29105     onRotateFail : function()
29106     {
29107         this.errorEl.show(true);
29108         
29109         var _this = this;
29110         
29111         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
29112     },
29113     
29114     draw : function()
29115     {
29116         this.previewEl.dom.innerHTML = '';
29117         
29118         var canvasEl = document.createElement("canvas");
29119         
29120         var contextEl = canvasEl.getContext("2d");
29121         
29122         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29123         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29124         var center = this.imageEl.OriginWidth / 2;
29125         
29126         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
29127             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29128             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29129             center = this.imageEl.OriginHeight / 2;
29130         }
29131         
29132         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
29133         
29134         contextEl.translate(center, center);
29135         contextEl.rotate(this.rotate * Math.PI / 180);
29136
29137         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
29138         
29139         this.canvasEl = document.createElement("canvas");
29140         
29141         this.contextEl = this.canvasEl.getContext("2d");
29142         
29143         switch (this.rotate) {
29144             case 0 :
29145                 
29146                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29147                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29148                 
29149                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29150                 
29151                 break;
29152             case 90 : 
29153                 
29154                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29155                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29156                 
29157                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29158                     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);
29159                     break;
29160                 }
29161                 
29162                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29163                 
29164                 break;
29165             case 180 :
29166                 
29167                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29168                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29169                 
29170                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29171                     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);
29172                     break;
29173                 }
29174                 
29175                 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);
29176                 
29177                 break;
29178             case 270 :
29179                 
29180                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29181                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29182         
29183                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29184                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29185                     break;
29186                 }
29187                 
29188                 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);
29189                 
29190                 break;
29191             default : 
29192                 break;
29193         }
29194         
29195         this.previewEl.appendChild(this.canvasEl);
29196         
29197         this.setCanvasPosition();
29198     },
29199     
29200     crop : function()
29201     {
29202         if(!this.canvasLoaded){
29203             return;
29204         }
29205         
29206         var imageCanvas = document.createElement("canvas");
29207         
29208         var imageContext = imageCanvas.getContext("2d");
29209         
29210         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
29211         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
29212         
29213         var center = imageCanvas.width / 2;
29214         
29215         imageContext.translate(center, center);
29216         
29217         imageContext.rotate(this.rotate * Math.PI / 180);
29218         
29219         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
29220         
29221         var canvas = document.createElement("canvas");
29222         
29223         var context = canvas.getContext("2d");
29224                 
29225         canvas.width = this.minWidth;
29226         canvas.height = this.minHeight;
29227
29228         switch (this.rotate) {
29229             case 0 :
29230                 
29231                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
29232                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
29233                 
29234                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29235                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29236                 
29237                 var targetWidth = this.minWidth - 2 * x;
29238                 var targetHeight = this.minHeight - 2 * y;
29239                 
29240                 var scale = 1;
29241                 
29242                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29243                     scale = targetWidth / width;
29244                 }
29245                 
29246                 if(x > 0 && y == 0){
29247                     scale = targetHeight / height;
29248                 }
29249                 
29250                 if(x > 0 && y > 0){
29251                     scale = targetWidth / width;
29252                     
29253                     if(width < height){
29254                         scale = targetHeight / height;
29255                     }
29256                 }
29257                 
29258                 context.scale(scale, scale);
29259                 
29260                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29261                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29262
29263                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29264                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29265
29266                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29267                 
29268                 break;
29269             case 90 : 
29270                 
29271                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
29272                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
29273                 
29274                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29275                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29276                 
29277                 var targetWidth = this.minWidth - 2 * x;
29278                 var targetHeight = this.minHeight - 2 * y;
29279                 
29280                 var scale = 1;
29281                 
29282                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29283                     scale = targetWidth / width;
29284                 }
29285                 
29286                 if(x > 0 && y == 0){
29287                     scale = targetHeight / height;
29288                 }
29289                 
29290                 if(x > 0 && y > 0){
29291                     scale = targetWidth / width;
29292                     
29293                     if(width < height){
29294                         scale = targetHeight / height;
29295                     }
29296                 }
29297                 
29298                 context.scale(scale, scale);
29299                 
29300                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29301                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29302
29303                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29304                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29305                 
29306                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
29307                 
29308                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29309                 
29310                 break;
29311             case 180 :
29312                 
29313                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
29314                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
29315                 
29316                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29317                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29318                 
29319                 var targetWidth = this.minWidth - 2 * x;
29320                 var targetHeight = this.minHeight - 2 * y;
29321                 
29322                 var scale = 1;
29323                 
29324                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29325                     scale = targetWidth / width;
29326                 }
29327                 
29328                 if(x > 0 && y == 0){
29329                     scale = targetHeight / height;
29330                 }
29331                 
29332                 if(x > 0 && y > 0){
29333                     scale = targetWidth / width;
29334                     
29335                     if(width < height){
29336                         scale = targetHeight / height;
29337                     }
29338                 }
29339                 
29340                 context.scale(scale, scale);
29341                 
29342                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29343                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29344
29345                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29346                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29347
29348                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
29349                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
29350                 
29351                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29352                 
29353                 break;
29354             case 270 :
29355                 
29356                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
29357                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
29358                 
29359                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29360                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29361                 
29362                 var targetWidth = this.minWidth - 2 * x;
29363                 var targetHeight = this.minHeight - 2 * y;
29364                 
29365                 var scale = 1;
29366                 
29367                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29368                     scale = targetWidth / width;
29369                 }
29370                 
29371                 if(x > 0 && y == 0){
29372                     scale = targetHeight / height;
29373                 }
29374                 
29375                 if(x > 0 && y > 0){
29376                     scale = targetWidth / width;
29377                     
29378                     if(width < height){
29379                         scale = targetHeight / height;
29380                     }
29381                 }
29382                 
29383                 context.scale(scale, scale);
29384                 
29385                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29386                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29387
29388                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29389                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29390                 
29391                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
29392                 
29393                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29394                 
29395                 break;
29396             default : 
29397                 break;
29398         }
29399         
29400         this.cropData = canvas.toDataURL(this.cropType);
29401         
29402         if(this.fireEvent('crop', this, this.cropData) !== false){
29403             this.process(this.file, this.cropData);
29404         }
29405         
29406         return;
29407         
29408     },
29409     
29410     setThumbBoxSize : function()
29411     {
29412         var width, height;
29413         
29414         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
29415             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
29416             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
29417             
29418             this.minWidth = width;
29419             this.minHeight = height;
29420             
29421             if(this.rotate == 90 || this.rotate == 270){
29422                 this.minWidth = height;
29423                 this.minHeight = width;
29424             }
29425         }
29426         
29427         height = 300;
29428         width = Math.ceil(this.minWidth * height / this.minHeight);
29429         
29430         if(this.minWidth > this.minHeight){
29431             width = 300;
29432             height = Math.ceil(this.minHeight * width / this.minWidth);
29433         }
29434         
29435         this.thumbEl.setStyle({
29436             width : width + 'px',
29437             height : height + 'px'
29438         });
29439
29440         return;
29441             
29442     },
29443     
29444     setThumbBoxPosition : function()
29445     {
29446         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
29447         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
29448         
29449         this.thumbEl.setLeft(x);
29450         this.thumbEl.setTop(y);
29451         
29452     },
29453     
29454     baseRotateLevel : function()
29455     {
29456         this.baseRotate = 1;
29457         
29458         if(
29459                 typeof(this.exif) != 'undefined' &&
29460                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
29461                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
29462         ){
29463             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
29464         }
29465         
29466         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
29467         
29468     },
29469     
29470     baseScaleLevel : function()
29471     {
29472         var width, height;
29473         
29474         if(this.isDocument){
29475             
29476             if(this.baseRotate == 6 || this.baseRotate == 8){
29477             
29478                 height = this.thumbEl.getHeight();
29479                 this.baseScale = height / this.imageEl.OriginWidth;
29480
29481                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
29482                     width = this.thumbEl.getWidth();
29483                     this.baseScale = width / this.imageEl.OriginHeight;
29484                 }
29485
29486                 return;
29487             }
29488
29489             height = this.thumbEl.getHeight();
29490             this.baseScale = height / this.imageEl.OriginHeight;
29491
29492             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
29493                 width = this.thumbEl.getWidth();
29494                 this.baseScale = width / this.imageEl.OriginWidth;
29495             }
29496
29497             return;
29498         }
29499         
29500         if(this.baseRotate == 6 || this.baseRotate == 8){
29501             
29502             width = this.thumbEl.getHeight();
29503             this.baseScale = width / this.imageEl.OriginHeight;
29504             
29505             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
29506                 height = this.thumbEl.getWidth();
29507                 this.baseScale = height / this.imageEl.OriginHeight;
29508             }
29509             
29510             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29511                 height = this.thumbEl.getWidth();
29512                 this.baseScale = height / this.imageEl.OriginHeight;
29513                 
29514                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
29515                     width = this.thumbEl.getHeight();
29516                     this.baseScale = width / this.imageEl.OriginWidth;
29517                 }
29518             }
29519             
29520             return;
29521         }
29522         
29523         width = this.thumbEl.getWidth();
29524         this.baseScale = width / this.imageEl.OriginWidth;
29525         
29526         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
29527             height = this.thumbEl.getHeight();
29528             this.baseScale = height / this.imageEl.OriginHeight;
29529         }
29530         
29531         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29532             
29533             height = this.thumbEl.getHeight();
29534             this.baseScale = height / this.imageEl.OriginHeight;
29535             
29536             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
29537                 width = this.thumbEl.getWidth();
29538                 this.baseScale = width / this.imageEl.OriginWidth;
29539             }
29540             
29541         }
29542         
29543         return;
29544     },
29545     
29546     getScaleLevel : function()
29547     {
29548         return this.baseScale * Math.pow(1.1, this.scale);
29549     },
29550     
29551     onTouchStart : function(e)
29552     {
29553         if(!this.canvasLoaded){
29554             this.beforeSelectFile(e);
29555             return;
29556         }
29557         
29558         var touches = e.browserEvent.touches;
29559         
29560         if(!touches){
29561             return;
29562         }
29563         
29564         if(touches.length == 1){
29565             this.onMouseDown(e);
29566             return;
29567         }
29568         
29569         if(touches.length != 2){
29570             return;
29571         }
29572         
29573         var coords = [];
29574         
29575         for(var i = 0, finger; finger = touches[i]; i++){
29576             coords.push(finger.pageX, finger.pageY);
29577         }
29578         
29579         var x = Math.pow(coords[0] - coords[2], 2);
29580         var y = Math.pow(coords[1] - coords[3], 2);
29581         
29582         this.startDistance = Math.sqrt(x + y);
29583         
29584         this.startScale = this.scale;
29585         
29586         this.pinching = true;
29587         this.dragable = false;
29588         
29589     },
29590     
29591     onTouchMove : function(e)
29592     {
29593         if(!this.pinching && !this.dragable){
29594             return;
29595         }
29596         
29597         var touches = e.browserEvent.touches;
29598         
29599         if(!touches){
29600             return;
29601         }
29602         
29603         if(this.dragable){
29604             this.onMouseMove(e);
29605             return;
29606         }
29607         
29608         var coords = [];
29609         
29610         for(var i = 0, finger; finger = touches[i]; i++){
29611             coords.push(finger.pageX, finger.pageY);
29612         }
29613         
29614         var x = Math.pow(coords[0] - coords[2], 2);
29615         var y = Math.pow(coords[1] - coords[3], 2);
29616         
29617         this.endDistance = Math.sqrt(x + y);
29618         
29619         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
29620         
29621         if(!this.zoomable()){
29622             this.scale = this.startScale;
29623             return;
29624         }
29625         
29626         this.draw();
29627         
29628     },
29629     
29630     onTouchEnd : function(e)
29631     {
29632         this.pinching = false;
29633         this.dragable = false;
29634         
29635     },
29636     
29637     process : function(file, crop)
29638     {
29639         if(this.loadMask){
29640             this.maskEl.mask(this.loadingText);
29641         }
29642         
29643         this.xhr = new XMLHttpRequest();
29644         
29645         file.xhr = this.xhr;
29646
29647         this.xhr.open(this.method, this.url, true);
29648         
29649         var headers = {
29650             "Accept": "application/json",
29651             "Cache-Control": "no-cache",
29652             "X-Requested-With": "XMLHttpRequest"
29653         };
29654         
29655         for (var headerName in headers) {
29656             var headerValue = headers[headerName];
29657             if (headerValue) {
29658                 this.xhr.setRequestHeader(headerName, headerValue);
29659             }
29660         }
29661         
29662         var _this = this;
29663         
29664         this.xhr.onload = function()
29665         {
29666             _this.xhrOnLoad(_this.xhr);
29667         }
29668         
29669         this.xhr.onerror = function()
29670         {
29671             _this.xhrOnError(_this.xhr);
29672         }
29673         
29674         var formData = new FormData();
29675
29676         formData.append('returnHTML', 'NO');
29677         
29678         if(crop){
29679             formData.append('crop', crop);
29680         }
29681         
29682         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
29683             formData.append(this.paramName, file, file.name);
29684         }
29685         
29686         if(typeof(file.filename) != 'undefined'){
29687             formData.append('filename', file.filename);
29688         }
29689         
29690         if(typeof(file.mimetype) != 'undefined'){
29691             formData.append('mimetype', file.mimetype);
29692         }
29693         
29694         if(this.fireEvent('arrange', this, formData) != false){
29695             this.xhr.send(formData);
29696         };
29697     },
29698     
29699     xhrOnLoad : function(xhr)
29700     {
29701         if(this.loadMask){
29702             this.maskEl.unmask();
29703         }
29704         
29705         if (xhr.readyState !== 4) {
29706             this.fireEvent('exception', this, xhr);
29707             return;
29708         }
29709
29710         var response = Roo.decode(xhr.responseText);
29711         
29712         if(!response.success){
29713             this.fireEvent('exception', this, xhr);
29714             return;
29715         }
29716         
29717         var response = Roo.decode(xhr.responseText);
29718         
29719         this.fireEvent('upload', this, response);
29720         
29721     },
29722     
29723     xhrOnError : function()
29724     {
29725         if(this.loadMask){
29726             this.maskEl.unmask();
29727         }
29728         
29729         Roo.log('xhr on error');
29730         
29731         var response = Roo.decode(xhr.responseText);
29732           
29733         Roo.log(response);
29734         
29735     },
29736     
29737     prepare : function(file)
29738     {   
29739         if(this.loadMask){
29740             this.maskEl.mask(this.loadingText);
29741         }
29742         
29743         this.file = false;
29744         this.exif = {};
29745         
29746         if(typeof(file) === 'string'){
29747             this.loadCanvas(file);
29748             return;
29749         }
29750         
29751         if(!file || !this.urlAPI){
29752             return;
29753         }
29754         
29755         this.file = file;
29756         this.cropType = file.type;
29757         
29758         var _this = this;
29759         
29760         if(this.fireEvent('prepare', this, this.file) != false){
29761             
29762             var reader = new FileReader();
29763             
29764             reader.onload = function (e) {
29765                 if (e.target.error) {
29766                     Roo.log(e.target.error);
29767                     return;
29768                 }
29769                 
29770                 var buffer = e.target.result,
29771                     dataView = new DataView(buffer),
29772                     offset = 2,
29773                     maxOffset = dataView.byteLength - 4,
29774                     markerBytes,
29775                     markerLength;
29776                 
29777                 if (dataView.getUint16(0) === 0xffd8) {
29778                     while (offset < maxOffset) {
29779                         markerBytes = dataView.getUint16(offset);
29780                         
29781                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
29782                             markerLength = dataView.getUint16(offset + 2) + 2;
29783                             if (offset + markerLength > dataView.byteLength) {
29784                                 Roo.log('Invalid meta data: Invalid segment size.');
29785                                 break;
29786                             }
29787                             
29788                             if(markerBytes == 0xffe1){
29789                                 _this.parseExifData(
29790                                     dataView,
29791                                     offset,
29792                                     markerLength
29793                                 );
29794                             }
29795                             
29796                             offset += markerLength;
29797                             
29798                             continue;
29799                         }
29800                         
29801                         break;
29802                     }
29803                     
29804                 }
29805                 
29806                 var url = _this.urlAPI.createObjectURL(_this.file);
29807                 
29808                 _this.loadCanvas(url);
29809                 
29810                 return;
29811             }
29812             
29813             reader.readAsArrayBuffer(this.file);
29814             
29815         }
29816         
29817     },
29818     
29819     parseExifData : function(dataView, offset, length)
29820     {
29821         var tiffOffset = offset + 10,
29822             littleEndian,
29823             dirOffset;
29824     
29825         if (dataView.getUint32(offset + 4) !== 0x45786966) {
29826             // No Exif data, might be XMP data instead
29827             return;
29828         }
29829         
29830         // Check for the ASCII code for "Exif" (0x45786966):
29831         if (dataView.getUint32(offset + 4) !== 0x45786966) {
29832             // No Exif data, might be XMP data instead
29833             return;
29834         }
29835         if (tiffOffset + 8 > dataView.byteLength) {
29836             Roo.log('Invalid Exif data: Invalid segment size.');
29837             return;
29838         }
29839         // Check for the two null bytes:
29840         if (dataView.getUint16(offset + 8) !== 0x0000) {
29841             Roo.log('Invalid Exif data: Missing byte alignment offset.');
29842             return;
29843         }
29844         // Check the byte alignment:
29845         switch (dataView.getUint16(tiffOffset)) {
29846         case 0x4949:
29847             littleEndian = true;
29848             break;
29849         case 0x4D4D:
29850             littleEndian = false;
29851             break;
29852         default:
29853             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
29854             return;
29855         }
29856         // Check for the TIFF tag marker (0x002A):
29857         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
29858             Roo.log('Invalid Exif data: Missing TIFF marker.');
29859             return;
29860         }
29861         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
29862         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
29863         
29864         this.parseExifTags(
29865             dataView,
29866             tiffOffset,
29867             tiffOffset + dirOffset,
29868             littleEndian
29869         );
29870     },
29871     
29872     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
29873     {
29874         var tagsNumber,
29875             dirEndOffset,
29876             i;
29877         if (dirOffset + 6 > dataView.byteLength) {
29878             Roo.log('Invalid Exif data: Invalid directory offset.');
29879             return;
29880         }
29881         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
29882         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
29883         if (dirEndOffset + 4 > dataView.byteLength) {
29884             Roo.log('Invalid Exif data: Invalid directory size.');
29885             return;
29886         }
29887         for (i = 0; i < tagsNumber; i += 1) {
29888             this.parseExifTag(
29889                 dataView,
29890                 tiffOffset,
29891                 dirOffset + 2 + 12 * i, // tag offset
29892                 littleEndian
29893             );
29894         }
29895         // Return the offset to the next directory:
29896         return dataView.getUint32(dirEndOffset, littleEndian);
29897     },
29898     
29899     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
29900     {
29901         var tag = dataView.getUint16(offset, littleEndian);
29902         
29903         this.exif[tag] = this.getExifValue(
29904             dataView,
29905             tiffOffset,
29906             offset,
29907             dataView.getUint16(offset + 2, littleEndian), // tag type
29908             dataView.getUint32(offset + 4, littleEndian), // tag length
29909             littleEndian
29910         );
29911     },
29912     
29913     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
29914     {
29915         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
29916             tagSize,
29917             dataOffset,
29918             values,
29919             i,
29920             str,
29921             c;
29922     
29923         if (!tagType) {
29924             Roo.log('Invalid Exif data: Invalid tag type.');
29925             return;
29926         }
29927         
29928         tagSize = tagType.size * length;
29929         // Determine if the value is contained in the dataOffset bytes,
29930         // or if the value at the dataOffset is a pointer to the actual data:
29931         dataOffset = tagSize > 4 ?
29932                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
29933         if (dataOffset + tagSize > dataView.byteLength) {
29934             Roo.log('Invalid Exif data: Invalid data offset.');
29935             return;
29936         }
29937         if (length === 1) {
29938             return tagType.getValue(dataView, dataOffset, littleEndian);
29939         }
29940         values = [];
29941         for (i = 0; i < length; i += 1) {
29942             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
29943         }
29944         
29945         if (tagType.ascii) {
29946             str = '';
29947             // Concatenate the chars:
29948             for (i = 0; i < values.length; i += 1) {
29949                 c = values[i];
29950                 // Ignore the terminating NULL byte(s):
29951                 if (c === '\u0000') {
29952                     break;
29953                 }
29954                 str += c;
29955             }
29956             return str;
29957         }
29958         return values;
29959     }
29960     
29961 });
29962
29963 Roo.apply(Roo.bootstrap.UploadCropbox, {
29964     tags : {
29965         'Orientation': 0x0112
29966     },
29967     
29968     Orientation: {
29969             1: 0, //'top-left',
29970 //            2: 'top-right',
29971             3: 180, //'bottom-right',
29972 //            4: 'bottom-left',
29973 //            5: 'left-top',
29974             6: 90, //'right-top',
29975 //            7: 'right-bottom',
29976             8: 270 //'left-bottom'
29977     },
29978     
29979     exifTagTypes : {
29980         // byte, 8-bit unsigned int:
29981         1: {
29982             getValue: function (dataView, dataOffset) {
29983                 return dataView.getUint8(dataOffset);
29984             },
29985             size: 1
29986         },
29987         // ascii, 8-bit byte:
29988         2: {
29989             getValue: function (dataView, dataOffset) {
29990                 return String.fromCharCode(dataView.getUint8(dataOffset));
29991             },
29992             size: 1,
29993             ascii: true
29994         },
29995         // short, 16 bit int:
29996         3: {
29997             getValue: function (dataView, dataOffset, littleEndian) {
29998                 return dataView.getUint16(dataOffset, littleEndian);
29999             },
30000             size: 2
30001         },
30002         // long, 32 bit int:
30003         4: {
30004             getValue: function (dataView, dataOffset, littleEndian) {
30005                 return dataView.getUint32(dataOffset, littleEndian);
30006             },
30007             size: 4
30008         },
30009         // rational = two long values, first is numerator, second is denominator:
30010         5: {
30011             getValue: function (dataView, dataOffset, littleEndian) {
30012                 return dataView.getUint32(dataOffset, littleEndian) /
30013                     dataView.getUint32(dataOffset + 4, littleEndian);
30014             },
30015             size: 8
30016         },
30017         // slong, 32 bit signed int:
30018         9: {
30019             getValue: function (dataView, dataOffset, littleEndian) {
30020                 return dataView.getInt32(dataOffset, littleEndian);
30021             },
30022             size: 4
30023         },
30024         // srational, two slongs, first is numerator, second is denominator:
30025         10: {
30026             getValue: function (dataView, dataOffset, littleEndian) {
30027                 return dataView.getInt32(dataOffset, littleEndian) /
30028                     dataView.getInt32(dataOffset + 4, littleEndian);
30029             },
30030             size: 8
30031         }
30032     },
30033     
30034     footer : {
30035         STANDARD : [
30036             {
30037                 tag : 'div',
30038                 cls : 'btn-group roo-upload-cropbox-rotate-left',
30039                 action : 'rotate-left',
30040                 cn : [
30041                     {
30042                         tag : 'button',
30043                         cls : 'btn btn-default',
30044                         html : '<i class="fa fa-undo"></i>'
30045                     }
30046                 ]
30047             },
30048             {
30049                 tag : 'div',
30050                 cls : 'btn-group roo-upload-cropbox-picture',
30051                 action : 'picture',
30052                 cn : [
30053                     {
30054                         tag : 'button',
30055                         cls : 'btn btn-default',
30056                         html : '<i class="fa fa-picture-o"></i>'
30057                     }
30058                 ]
30059             },
30060             {
30061                 tag : 'div',
30062                 cls : 'btn-group roo-upload-cropbox-rotate-right',
30063                 action : 'rotate-right',
30064                 cn : [
30065                     {
30066                         tag : 'button',
30067                         cls : 'btn btn-default',
30068                         html : '<i class="fa fa-repeat"></i>'
30069                     }
30070                 ]
30071             }
30072         ],
30073         DOCUMENT : [
30074             {
30075                 tag : 'div',
30076                 cls : 'btn-group roo-upload-cropbox-rotate-left',
30077                 action : 'rotate-left',
30078                 cn : [
30079                     {
30080                         tag : 'button',
30081                         cls : 'btn btn-default',
30082                         html : '<i class="fa fa-undo"></i>'
30083                     }
30084                 ]
30085             },
30086             {
30087                 tag : 'div',
30088                 cls : 'btn-group roo-upload-cropbox-download',
30089                 action : 'download',
30090                 cn : [
30091                     {
30092                         tag : 'button',
30093                         cls : 'btn btn-default',
30094                         html : '<i class="fa fa-download"></i>'
30095                     }
30096                 ]
30097             },
30098             {
30099                 tag : 'div',
30100                 cls : 'btn-group roo-upload-cropbox-crop',
30101                 action : 'crop',
30102                 cn : [
30103                     {
30104                         tag : 'button',
30105                         cls : 'btn btn-default',
30106                         html : '<i class="fa fa-crop"></i>'
30107                     }
30108                 ]
30109             },
30110             {
30111                 tag : 'div',
30112                 cls : 'btn-group roo-upload-cropbox-trash',
30113                 action : 'trash',
30114                 cn : [
30115                     {
30116                         tag : 'button',
30117                         cls : 'btn btn-default',
30118                         html : '<i class="fa fa-trash"></i>'
30119                     }
30120                 ]
30121             },
30122             {
30123                 tag : 'div',
30124                 cls : 'btn-group roo-upload-cropbox-rotate-right',
30125                 action : 'rotate-right',
30126                 cn : [
30127                     {
30128                         tag : 'button',
30129                         cls : 'btn btn-default',
30130                         html : '<i class="fa fa-repeat"></i>'
30131                     }
30132                 ]
30133             }
30134         ],
30135         ROTATOR : [
30136             {
30137                 tag : 'div',
30138                 cls : 'btn-group roo-upload-cropbox-rotate-left',
30139                 action : 'rotate-left',
30140                 cn : [
30141                     {
30142                         tag : 'button',
30143                         cls : 'btn btn-default',
30144                         html : '<i class="fa fa-undo"></i>'
30145                     }
30146                 ]
30147             },
30148             {
30149                 tag : 'div',
30150                 cls : 'btn-group roo-upload-cropbox-rotate-right',
30151                 action : 'rotate-right',
30152                 cn : [
30153                     {
30154                         tag : 'button',
30155                         cls : 'btn btn-default',
30156                         html : '<i class="fa fa-repeat"></i>'
30157                     }
30158                 ]
30159             }
30160         ]
30161     }
30162 });
30163
30164 /*
30165 * Licence: LGPL
30166 */
30167
30168 /**
30169  * @class Roo.bootstrap.DocumentManager
30170  * @extends Roo.bootstrap.Component
30171  * Bootstrap DocumentManager class
30172  * @cfg {String} paramName default 'imageUpload'
30173  * @cfg {String} toolTipName default 'filename'
30174  * @cfg {String} method default POST
30175  * @cfg {String} url action url
30176  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
30177  * @cfg {Boolean} multiple multiple upload default true
30178  * @cfg {Number} thumbSize default 300
30179  * @cfg {String} fieldLabel
30180  * @cfg {Number} labelWidth default 4
30181  * @cfg {String} labelAlign (left|top) default left
30182  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
30183 * @cfg {Number} labellg set the width of label (1-12)
30184  * @cfg {Number} labelmd set the width of label (1-12)
30185  * @cfg {Number} labelsm set the width of label (1-12)
30186  * @cfg {Number} labelxs set the width of label (1-12)
30187  * 
30188  * @constructor
30189  * Create a new DocumentManager
30190  * @param {Object} config The config object
30191  */
30192
30193 Roo.bootstrap.DocumentManager = function(config){
30194     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
30195     
30196     this.files = [];
30197     this.delegates = [];
30198     
30199     this.addEvents({
30200         /**
30201          * @event initial
30202          * Fire when initial the DocumentManager
30203          * @param {Roo.bootstrap.DocumentManager} this
30204          */
30205         "initial" : true,
30206         /**
30207          * @event inspect
30208          * inspect selected file
30209          * @param {Roo.bootstrap.DocumentManager} this
30210          * @param {File} file
30211          */
30212         "inspect" : true,
30213         /**
30214          * @event exception
30215          * Fire when xhr load exception
30216          * @param {Roo.bootstrap.DocumentManager} this
30217          * @param {XMLHttpRequest} xhr
30218          */
30219         "exception" : true,
30220         /**
30221          * @event afterupload
30222          * Fire when xhr load exception
30223          * @param {Roo.bootstrap.DocumentManager} this
30224          * @param {XMLHttpRequest} xhr
30225          */
30226         "afterupload" : true,
30227         /**
30228          * @event prepare
30229          * prepare the form data
30230          * @param {Roo.bootstrap.DocumentManager} this
30231          * @param {Object} formData
30232          */
30233         "prepare" : true,
30234         /**
30235          * @event remove
30236          * Fire when remove the file
30237          * @param {Roo.bootstrap.DocumentManager} this
30238          * @param {Object} file
30239          */
30240         "remove" : true,
30241         /**
30242          * @event refresh
30243          * Fire after refresh the file
30244          * @param {Roo.bootstrap.DocumentManager} this
30245          */
30246         "refresh" : true,
30247         /**
30248          * @event click
30249          * Fire after click the image
30250          * @param {Roo.bootstrap.DocumentManager} this
30251          * @param {Object} file
30252          */
30253         "click" : true,
30254         /**
30255          * @event edit
30256          * Fire when upload a image and editable set to true
30257          * @param {Roo.bootstrap.DocumentManager} this
30258          * @param {Object} file
30259          */
30260         "edit" : true,
30261         /**
30262          * @event beforeselectfile
30263          * Fire before select file
30264          * @param {Roo.bootstrap.DocumentManager} this
30265          */
30266         "beforeselectfile" : true,
30267         /**
30268          * @event process
30269          * Fire before process file
30270          * @param {Roo.bootstrap.DocumentManager} this
30271          * @param {Object} file
30272          */
30273         "process" : true,
30274         /**
30275          * @event previewrendered
30276          * Fire when preview rendered
30277          * @param {Roo.bootstrap.DocumentManager} this
30278          * @param {Object} file
30279          */
30280         "previewrendered" : true,
30281         /**
30282          */
30283         "previewResize" : true
30284         
30285     });
30286 };
30287
30288 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
30289     
30290     boxes : 0,
30291     inputName : '',
30292     thumbSize : 300,
30293     multiple : true,
30294     files : false,
30295     method : 'POST',
30296     url : '',
30297     paramName : 'imageUpload',
30298     toolTipName : 'filename',
30299     fieldLabel : '',
30300     labelWidth : 4,
30301     labelAlign : 'left',
30302     editable : true,
30303     delegates : false,
30304     xhr : false, 
30305     
30306     labellg : 0,
30307     labelmd : 0,
30308     labelsm : 0,
30309     labelxs : 0,
30310     
30311     getAutoCreate : function()
30312     {   
30313         var managerWidget = {
30314             tag : 'div',
30315             cls : 'roo-document-manager',
30316             cn : [
30317                 {
30318                     tag : 'input',
30319                     cls : 'roo-document-manager-selector',
30320                     type : 'file'
30321                 },
30322                 {
30323                     tag : 'div',
30324                     cls : 'roo-document-manager-uploader',
30325                     cn : [
30326                         {
30327                             tag : 'div',
30328                             cls : 'roo-document-manager-upload-btn',
30329                             html : '<i class="fa fa-plus"></i>'
30330                         }
30331                     ]
30332                     
30333                 }
30334             ]
30335         };
30336         
30337         var content = [
30338             {
30339                 tag : 'div',
30340                 cls : 'column col-md-12',
30341                 cn : managerWidget
30342             }
30343         ];
30344         
30345         if(this.fieldLabel.length){
30346             
30347             content = [
30348                 {
30349                     tag : 'div',
30350                     cls : 'column col-md-12',
30351                     html : this.fieldLabel
30352                 },
30353                 {
30354                     tag : 'div',
30355                     cls : 'column col-md-12',
30356                     cn : managerWidget
30357                 }
30358             ];
30359
30360             if(this.labelAlign == 'left'){
30361                 content = [
30362                     {
30363                         tag : 'div',
30364                         cls : 'column',
30365                         html : this.fieldLabel
30366                     },
30367                     {
30368                         tag : 'div',
30369                         cls : 'column',
30370                         cn : managerWidget
30371                     }
30372                 ];
30373                 
30374                 if(this.labelWidth > 12){
30375                     content[0].style = "width: " + this.labelWidth + 'px';
30376                 }
30377
30378                 if(this.labelWidth < 13 && this.labelmd == 0){
30379                     this.labelmd = this.labelWidth;
30380                 }
30381
30382                 if(this.labellg > 0){
30383                     content[0].cls += ' col-lg-' + this.labellg;
30384                     content[1].cls += ' col-lg-' + (12 - this.labellg);
30385                 }
30386
30387                 if(this.labelmd > 0){
30388                     content[0].cls += ' col-md-' + this.labelmd;
30389                     content[1].cls += ' col-md-' + (12 - this.labelmd);
30390                 }
30391
30392                 if(this.labelsm > 0){
30393                     content[0].cls += ' col-sm-' + this.labelsm;
30394                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
30395                 }
30396
30397                 if(this.labelxs > 0){
30398                     content[0].cls += ' col-xs-' + this.labelxs;
30399                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
30400                 }
30401                 
30402             }
30403         }
30404         
30405         var cfg = {
30406             tag : 'div',
30407             cls : 'row clearfix',
30408             cn : content
30409         };
30410         
30411         return cfg;
30412         
30413     },
30414     
30415     initEvents : function()
30416     {
30417         this.managerEl = this.el.select('.roo-document-manager', true).first();
30418         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30419         
30420         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
30421         this.selectorEl.hide();
30422         
30423         if(this.multiple){
30424             this.selectorEl.attr('multiple', 'multiple');
30425         }
30426         
30427         this.selectorEl.on('change', this.onFileSelected, this);
30428         
30429         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
30430         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30431         
30432         this.uploader.on('click', this.onUploaderClick, this);
30433         
30434         this.renderProgressDialog();
30435         
30436         var _this = this;
30437         
30438         window.addEventListener("resize", function() { _this.refresh(); } );
30439         
30440         this.fireEvent('initial', this);
30441     },
30442     
30443     renderProgressDialog : function()
30444     {
30445         var _this = this;
30446         
30447         this.progressDialog = new Roo.bootstrap.Modal({
30448             cls : 'roo-document-manager-progress-dialog',
30449             allow_close : false,
30450             animate : false,
30451             title : '',
30452             buttons : [
30453                 {
30454                     name  :'cancel',
30455                     weight : 'danger',
30456                     html : 'Cancel'
30457                 }
30458             ], 
30459             listeners : { 
30460                 btnclick : function() {
30461                     _this.uploadCancel();
30462                     this.hide();
30463                 }
30464             }
30465         });
30466          
30467         this.progressDialog.render(Roo.get(document.body));
30468          
30469         this.progress = new Roo.bootstrap.Progress({
30470             cls : 'roo-document-manager-progress',
30471             active : true,
30472             striped : true
30473         });
30474         
30475         this.progress.render(this.progressDialog.getChildContainer());
30476         
30477         this.progressBar = new Roo.bootstrap.ProgressBar({
30478             cls : 'roo-document-manager-progress-bar',
30479             aria_valuenow : 0,
30480             aria_valuemin : 0,
30481             aria_valuemax : 12,
30482             panel : 'success'
30483         });
30484         
30485         this.progressBar.render(this.progress.getChildContainer());
30486     },
30487     
30488     onUploaderClick : function(e)
30489     {
30490         e.preventDefault();
30491      
30492         if(this.fireEvent('beforeselectfile', this) != false){
30493             this.selectorEl.dom.click();
30494         }
30495         
30496     },
30497     
30498     onFileSelected : function(e)
30499     {
30500         e.preventDefault();
30501         
30502         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30503             return;
30504         }
30505         
30506         Roo.each(this.selectorEl.dom.files, function(file){
30507             if(this.fireEvent('inspect', this, file) != false){
30508                 this.files.push(file);
30509             }
30510         }, this);
30511         
30512         this.queue();
30513         
30514     },
30515     
30516     queue : function()
30517     {
30518         this.selectorEl.dom.value = '';
30519         
30520         if(!this.files || !this.files.length){
30521             return;
30522         }
30523         
30524         if(this.boxes > 0 && this.files.length > this.boxes){
30525             this.files = this.files.slice(0, this.boxes);
30526         }
30527         
30528         this.uploader.show();
30529         
30530         if(this.boxes > 0 && this.files.length > this.boxes - 1){
30531             this.uploader.hide();
30532         }
30533         
30534         var _this = this;
30535         
30536         var files = [];
30537         
30538         var docs = [];
30539         
30540         Roo.each(this.files, function(file){
30541             
30542             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
30543                 var f = this.renderPreview(file);
30544                 files.push(f);
30545                 return;
30546             }
30547             
30548             if(file.type.indexOf('image') != -1){
30549                 this.delegates.push(
30550                     (function(){
30551                         _this.process(file);
30552                     }).createDelegate(this)
30553                 );
30554         
30555                 return;
30556             }
30557             
30558             docs.push(
30559                 (function(){
30560                     _this.process(file);
30561                 }).createDelegate(this)
30562             );
30563             
30564         }, this);
30565         
30566         this.files = files;
30567         
30568         this.delegates = this.delegates.concat(docs);
30569         
30570         if(!this.delegates.length){
30571             this.refresh();
30572             return;
30573         }
30574         
30575         this.progressBar.aria_valuemax = this.delegates.length;
30576         
30577         this.arrange();
30578         
30579         return;
30580     },
30581     
30582     arrange : function()
30583     {
30584         if(!this.delegates.length){
30585             this.progressDialog.hide();
30586             this.refresh();
30587             return;
30588         }
30589         
30590         var delegate = this.delegates.shift();
30591         
30592         this.progressDialog.show();
30593         
30594         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
30595         
30596         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
30597         
30598         delegate();
30599     },
30600     
30601     refresh : function()
30602     {
30603         this.uploader.show();
30604         
30605         if(this.boxes > 0 && this.files.length > this.boxes - 1){
30606             this.uploader.hide();
30607         }
30608         
30609         Roo.isTouch ? this.closable(false) : this.closable(true);
30610         
30611         this.fireEvent('refresh', this);
30612     },
30613     
30614     onRemove : function(e, el, o)
30615     {
30616         e.preventDefault();
30617         
30618         this.fireEvent('remove', this, o);
30619         
30620     },
30621     
30622     remove : function(o)
30623     {
30624         var files = [];
30625         
30626         Roo.each(this.files, function(file){
30627             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
30628                 files.push(file);
30629                 return;
30630             }
30631
30632             o.target.remove();
30633
30634         }, this);
30635         
30636         this.files = files;
30637         
30638         this.refresh();
30639     },
30640     
30641     clear : function()
30642     {
30643         Roo.each(this.files, function(file){
30644             if(!file.target){
30645                 return;
30646             }
30647             
30648             file.target.remove();
30649
30650         }, this);
30651         
30652         this.files = [];
30653         
30654         this.refresh();
30655     },
30656     
30657     onClick : function(e, el, o)
30658     {
30659         e.preventDefault();
30660         
30661         this.fireEvent('click', this, o);
30662         
30663     },
30664     
30665     closable : function(closable)
30666     {
30667         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
30668             
30669             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30670             
30671             if(closable){
30672                 el.show();
30673                 return;
30674             }
30675             
30676             el.hide();
30677             
30678         }, this);
30679     },
30680     
30681     xhrOnLoad : function(xhr)
30682     {
30683         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
30684             el.remove();
30685         }, this);
30686         
30687         if (xhr.readyState !== 4) {
30688             this.arrange();
30689             this.fireEvent('exception', this, xhr);
30690             return;
30691         }
30692
30693         var response = Roo.decode(xhr.responseText);
30694         
30695         if(!response.success){
30696             this.arrange();
30697             this.fireEvent('exception', this, xhr);
30698             return;
30699         }
30700         
30701         var file = this.renderPreview(response.data);
30702         
30703         this.files.push(file);
30704         
30705         this.arrange();
30706         
30707         this.fireEvent('afterupload', this, xhr);
30708         
30709     },
30710     
30711     xhrOnError : function(xhr)
30712     {
30713         Roo.log('xhr on error');
30714         
30715         var response = Roo.decode(xhr.responseText);
30716           
30717         Roo.log(response);
30718         
30719         this.arrange();
30720     },
30721     
30722     process : function(file)
30723     {
30724         if(this.fireEvent('process', this, file) !== false){
30725             if(this.editable && file.type.indexOf('image') != -1){
30726                 this.fireEvent('edit', this, file);
30727                 return;
30728             }
30729
30730             this.uploadStart(file, false);
30731
30732             return;
30733         }
30734         
30735     },
30736     
30737     uploadStart : function(file, crop)
30738     {
30739         this.xhr = new XMLHttpRequest();
30740         
30741         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
30742             this.arrange();
30743             return;
30744         }
30745         
30746         file.xhr = this.xhr;
30747             
30748         this.managerEl.createChild({
30749             tag : 'div',
30750             cls : 'roo-document-manager-loading',
30751             cn : [
30752                 {
30753                     tag : 'div',
30754                     tooltip : file.name,
30755                     cls : 'roo-document-manager-thumb',
30756                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
30757                 }
30758             ]
30759
30760         });
30761
30762         this.xhr.open(this.method, this.url, true);
30763         
30764         var headers = {
30765             "Accept": "application/json",
30766             "Cache-Control": "no-cache",
30767             "X-Requested-With": "XMLHttpRequest"
30768         };
30769         
30770         for (var headerName in headers) {
30771             var headerValue = headers[headerName];
30772             if (headerValue) {
30773                 this.xhr.setRequestHeader(headerName, headerValue);
30774             }
30775         }
30776         
30777         var _this = this;
30778         
30779         this.xhr.onload = function()
30780         {
30781             _this.xhrOnLoad(_this.xhr);
30782         }
30783         
30784         this.xhr.onerror = function()
30785         {
30786             _this.xhrOnError(_this.xhr);
30787         }
30788         
30789         var formData = new FormData();
30790
30791         formData.append('returnHTML', 'NO');
30792         
30793         if(crop){
30794             formData.append('crop', crop);
30795         }
30796         
30797         formData.append(this.paramName, file, file.name);
30798         
30799         var options = {
30800             file : file, 
30801             manually : false
30802         };
30803         
30804         if(this.fireEvent('prepare', this, formData, options) != false){
30805             
30806             if(options.manually){
30807                 return;
30808             }
30809             
30810             this.xhr.send(formData);
30811             return;
30812         };
30813         
30814         this.uploadCancel();
30815     },
30816     
30817     uploadCancel : function()
30818     {
30819         if (this.xhr) {
30820             this.xhr.abort();
30821         }
30822         
30823         this.delegates = [];
30824         
30825         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
30826             el.remove();
30827         }, this);
30828         
30829         this.arrange();
30830     },
30831     
30832     renderPreview : function(file)
30833     {
30834         if(typeof(file.target) != 'undefined' && file.target){
30835             return file;
30836         }
30837         
30838         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
30839         
30840         var previewEl = this.managerEl.createChild({
30841             tag : 'div',
30842             cls : 'roo-document-manager-preview',
30843             cn : [
30844                 {
30845                     tag : 'div',
30846                     tooltip : file[this.toolTipName],
30847                     cls : 'roo-document-manager-thumb',
30848                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
30849                 },
30850                 {
30851                     tag : 'button',
30852                     cls : 'close',
30853                     html : '<i class="fa fa-times-circle"></i>'
30854                 }
30855             ]
30856         });
30857
30858         var close = previewEl.select('button.close', true).first();
30859
30860         close.on('click', this.onRemove, this, file);
30861
30862         file.target = previewEl;
30863
30864         var image = previewEl.select('img', true).first();
30865         
30866         var _this = this;
30867         
30868         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
30869         
30870         image.on('click', this.onClick, this, file);
30871         
30872         this.fireEvent('previewrendered', this, file);
30873         
30874         return file;
30875         
30876     },
30877     
30878     onPreviewLoad : function(file, image)
30879     {
30880         if(typeof(file.target) == 'undefined' || !file.target){
30881             return;
30882         }
30883         
30884         var width = image.dom.naturalWidth || image.dom.width;
30885         var height = image.dom.naturalHeight || image.dom.height;
30886         
30887         if(!this.previewResize) {
30888             return;
30889         }
30890         
30891         if(width > height){
30892             file.target.addClass('wide');
30893             return;
30894         }
30895         
30896         file.target.addClass('tall');
30897         return;
30898         
30899     },
30900     
30901     uploadFromSource : function(file, crop)
30902     {
30903         this.xhr = new XMLHttpRequest();
30904         
30905         this.managerEl.createChild({
30906             tag : 'div',
30907             cls : 'roo-document-manager-loading',
30908             cn : [
30909                 {
30910                     tag : 'div',
30911                     tooltip : file.name,
30912                     cls : 'roo-document-manager-thumb',
30913                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
30914                 }
30915             ]
30916
30917         });
30918
30919         this.xhr.open(this.method, this.url, true);
30920         
30921         var headers = {
30922             "Accept": "application/json",
30923             "Cache-Control": "no-cache",
30924             "X-Requested-With": "XMLHttpRequest"
30925         };
30926         
30927         for (var headerName in headers) {
30928             var headerValue = headers[headerName];
30929             if (headerValue) {
30930                 this.xhr.setRequestHeader(headerName, headerValue);
30931             }
30932         }
30933         
30934         var _this = this;
30935         
30936         this.xhr.onload = function()
30937         {
30938             _this.xhrOnLoad(_this.xhr);
30939         }
30940         
30941         this.xhr.onerror = function()
30942         {
30943             _this.xhrOnError(_this.xhr);
30944         }
30945         
30946         var formData = new FormData();
30947
30948         formData.append('returnHTML', 'NO');
30949         
30950         formData.append('crop', crop);
30951         
30952         if(typeof(file.filename) != 'undefined'){
30953             formData.append('filename', file.filename);
30954         }
30955         
30956         if(typeof(file.mimetype) != 'undefined'){
30957             formData.append('mimetype', file.mimetype);
30958         }
30959         
30960         Roo.log(formData);
30961         
30962         if(this.fireEvent('prepare', this, formData) != false){
30963             this.xhr.send(formData);
30964         };
30965     }
30966 });
30967
30968 /*
30969 * Licence: LGPL
30970 */
30971
30972 /**
30973  * @class Roo.bootstrap.DocumentViewer
30974  * @extends Roo.bootstrap.Component
30975  * Bootstrap DocumentViewer class
30976  * @cfg {Boolean} showDownload (true|false) show download button (default true)
30977  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
30978  * 
30979  * @constructor
30980  * Create a new DocumentViewer
30981  * @param {Object} config The config object
30982  */
30983
30984 Roo.bootstrap.DocumentViewer = function(config){
30985     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
30986     
30987     this.addEvents({
30988         /**
30989          * @event initial
30990          * Fire after initEvent
30991          * @param {Roo.bootstrap.DocumentViewer} this
30992          */
30993         "initial" : true,
30994         /**
30995          * @event click
30996          * Fire after click
30997          * @param {Roo.bootstrap.DocumentViewer} this
30998          */
30999         "click" : true,
31000         /**
31001          * @event download
31002          * Fire after download button
31003          * @param {Roo.bootstrap.DocumentViewer} this
31004          */
31005         "download" : true,
31006         /**
31007          * @event trash
31008          * Fire after trash button
31009          * @param {Roo.bootstrap.DocumentViewer} this
31010          */
31011         "trash" : true
31012         
31013     });
31014 };
31015
31016 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
31017     
31018     showDownload : true,
31019     
31020     showTrash : true,
31021     
31022     getAutoCreate : function()
31023     {
31024         var cfg = {
31025             tag : 'div',
31026             cls : 'roo-document-viewer',
31027             cn : [
31028                 {
31029                     tag : 'div',
31030                     cls : 'roo-document-viewer-body',
31031                     cn : [
31032                         {
31033                             tag : 'div',
31034                             cls : 'roo-document-viewer-thumb',
31035                             cn : [
31036                                 {
31037                                     tag : 'img',
31038                                     cls : 'roo-document-viewer-image'
31039                                 }
31040                             ]
31041                         }
31042                     ]
31043                 },
31044                 {
31045                     tag : 'div',
31046                     cls : 'roo-document-viewer-footer',
31047                     cn : {
31048                         tag : 'div',
31049                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
31050                         cn : [
31051                             {
31052                                 tag : 'div',
31053                                 cls : 'btn-group roo-document-viewer-download',
31054                                 cn : [
31055                                     {
31056                                         tag : 'button',
31057                                         cls : 'btn btn-default',
31058                                         html : '<i class="fa fa-download"></i>'
31059                                     }
31060                                 ]
31061                             },
31062                             {
31063                                 tag : 'div',
31064                                 cls : 'btn-group roo-document-viewer-trash',
31065                                 cn : [
31066                                     {
31067                                         tag : 'button',
31068                                         cls : 'btn btn-default',
31069                                         html : '<i class="fa fa-trash"></i>'
31070                                     }
31071                                 ]
31072                             }
31073                         ]
31074                     }
31075                 }
31076             ]
31077         };
31078         
31079         return cfg;
31080     },
31081     
31082     initEvents : function()
31083     {
31084         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
31085         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
31086         
31087         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
31088         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
31089         
31090         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
31091         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
31092         
31093         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
31094         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
31095         
31096         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
31097         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
31098         
31099         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
31100         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
31101         
31102         this.bodyEl.on('click', this.onClick, this);
31103         this.downloadBtn.on('click', this.onDownload, this);
31104         this.trashBtn.on('click', this.onTrash, this);
31105         
31106         this.downloadBtn.hide();
31107         this.trashBtn.hide();
31108         
31109         if(this.showDownload){
31110             this.downloadBtn.show();
31111         }
31112         
31113         if(this.showTrash){
31114             this.trashBtn.show();
31115         }
31116         
31117         if(!this.showDownload && !this.showTrash) {
31118             this.footerEl.hide();
31119         }
31120         
31121     },
31122     
31123     initial : function()
31124     {
31125         this.fireEvent('initial', this);
31126         
31127     },
31128     
31129     onClick : function(e)
31130     {
31131         e.preventDefault();
31132         
31133         this.fireEvent('click', this);
31134     },
31135     
31136     onDownload : function(e)
31137     {
31138         e.preventDefault();
31139         
31140         this.fireEvent('download', this);
31141     },
31142     
31143     onTrash : function(e)
31144     {
31145         e.preventDefault();
31146         
31147         this.fireEvent('trash', this);
31148     }
31149     
31150 });
31151 /*
31152  * - LGPL
31153  *
31154  * nav progress bar
31155  * 
31156  */
31157
31158 /**
31159  * @class Roo.bootstrap.NavProgressBar
31160  * @extends Roo.bootstrap.Component
31161  * Bootstrap NavProgressBar class
31162  * 
31163  * @constructor
31164  * Create a new nav progress bar
31165  * @param {Object} config The config object
31166  */
31167
31168 Roo.bootstrap.NavProgressBar = function(config){
31169     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
31170
31171     this.bullets = this.bullets || [];
31172    
31173 //    Roo.bootstrap.NavProgressBar.register(this);
31174      this.addEvents({
31175         /**
31176              * @event changed
31177              * Fires when the active item changes
31178              * @param {Roo.bootstrap.NavProgressBar} this
31179              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
31180              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
31181          */
31182         'changed': true
31183      });
31184     
31185 };
31186
31187 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
31188     
31189     bullets : [],
31190     barItems : [],
31191     
31192     getAutoCreate : function()
31193     {
31194         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
31195         
31196         cfg = {
31197             tag : 'div',
31198             cls : 'roo-navigation-bar-group',
31199             cn : [
31200                 {
31201                     tag : 'div',
31202                     cls : 'roo-navigation-top-bar'
31203                 },
31204                 {
31205                     tag : 'div',
31206                     cls : 'roo-navigation-bullets-bar',
31207                     cn : [
31208                         {
31209                             tag : 'ul',
31210                             cls : 'roo-navigation-bar'
31211                         }
31212                     ]
31213                 },
31214                 
31215                 {
31216                     tag : 'div',
31217                     cls : 'roo-navigation-bottom-bar'
31218                 }
31219             ]
31220             
31221         };
31222         
31223         return cfg;
31224         
31225     },
31226     
31227     initEvents: function() 
31228     {
31229         
31230     },
31231     
31232     onRender : function(ct, position) 
31233     {
31234         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
31235         
31236         if(this.bullets.length){
31237             Roo.each(this.bullets, function(b){
31238                this.addItem(b);
31239             }, this);
31240         }
31241         
31242         this.format();
31243         
31244     },
31245     
31246     addItem : function(cfg)
31247     {
31248         var item = new Roo.bootstrap.NavProgressItem(cfg);
31249         
31250         item.parentId = this.id;
31251         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
31252         
31253         if(cfg.html){
31254             var top = new Roo.bootstrap.Element({
31255                 tag : 'div',
31256                 cls : 'roo-navigation-bar-text'
31257             });
31258             
31259             var bottom = new Roo.bootstrap.Element({
31260                 tag : 'div',
31261                 cls : 'roo-navigation-bar-text'
31262             });
31263             
31264             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
31265             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
31266             
31267             var topText = new Roo.bootstrap.Element({
31268                 tag : 'span',
31269                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
31270             });
31271             
31272             var bottomText = new Roo.bootstrap.Element({
31273                 tag : 'span',
31274                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
31275             });
31276             
31277             topText.onRender(top.el, null);
31278             bottomText.onRender(bottom.el, null);
31279             
31280             item.topEl = top;
31281             item.bottomEl = bottom;
31282         }
31283         
31284         this.barItems.push(item);
31285         
31286         return item;
31287     },
31288     
31289     getActive : function()
31290     {
31291         var active = false;
31292         
31293         Roo.each(this.barItems, function(v){
31294             
31295             if (!v.isActive()) {
31296                 return;
31297             }
31298             
31299             active = v;
31300             return false;
31301             
31302         });
31303         
31304         return active;
31305     },
31306     
31307     setActiveItem : function(item)
31308     {
31309         var prev = false;
31310         
31311         Roo.each(this.barItems, function(v){
31312             if (v.rid == item.rid) {
31313                 return ;
31314             }
31315             
31316             if (v.isActive()) {
31317                 v.setActive(false);
31318                 prev = v;
31319             }
31320         });
31321
31322         item.setActive(true);
31323         
31324         this.fireEvent('changed', this, item, prev);
31325     },
31326     
31327     getBarItem: function(rid)
31328     {
31329         var ret = false;
31330         
31331         Roo.each(this.barItems, function(e) {
31332             if (e.rid != rid) {
31333                 return;
31334             }
31335             
31336             ret =  e;
31337             return false;
31338         });
31339         
31340         return ret;
31341     },
31342     
31343     indexOfItem : function(item)
31344     {
31345         var index = false;
31346         
31347         Roo.each(this.barItems, function(v, i){
31348             
31349             if (v.rid != item.rid) {
31350                 return;
31351             }
31352             
31353             index = i;
31354             return false
31355         });
31356         
31357         return index;
31358     },
31359     
31360     setActiveNext : function()
31361     {
31362         var i = this.indexOfItem(this.getActive());
31363         
31364         if (i > this.barItems.length) {
31365             return;
31366         }
31367         
31368         this.setActiveItem(this.barItems[i+1]);
31369     },
31370     
31371     setActivePrev : function()
31372     {
31373         var i = this.indexOfItem(this.getActive());
31374         
31375         if (i  < 1) {
31376             return;
31377         }
31378         
31379         this.setActiveItem(this.barItems[i-1]);
31380     },
31381     
31382     format : function()
31383     {
31384         if(!this.barItems.length){
31385             return;
31386         }
31387      
31388         var width = 100 / this.barItems.length;
31389         
31390         Roo.each(this.barItems, function(i){
31391             i.el.setStyle('width', width + '%');
31392             i.topEl.el.setStyle('width', width + '%');
31393             i.bottomEl.el.setStyle('width', width + '%');
31394         }, this);
31395         
31396     }
31397     
31398 });
31399 /*
31400  * - LGPL
31401  *
31402  * Nav Progress Item
31403  * 
31404  */
31405
31406 /**
31407  * @class Roo.bootstrap.NavProgressItem
31408  * @extends Roo.bootstrap.Component
31409  * Bootstrap NavProgressItem class
31410  * @cfg {String} rid the reference id
31411  * @cfg {Boolean} active (true|false) Is item active default false
31412  * @cfg {Boolean} disabled (true|false) Is item active default false
31413  * @cfg {String} html
31414  * @cfg {String} position (top|bottom) text position default bottom
31415  * @cfg {String} icon show icon instead of number
31416  * 
31417  * @constructor
31418  * Create a new NavProgressItem
31419  * @param {Object} config The config object
31420  */
31421 Roo.bootstrap.NavProgressItem = function(config){
31422     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
31423     this.addEvents({
31424         // raw events
31425         /**
31426          * @event click
31427          * The raw click event for the entire grid.
31428          * @param {Roo.bootstrap.NavProgressItem} this
31429          * @param {Roo.EventObject} e
31430          */
31431         "click" : true
31432     });
31433    
31434 };
31435
31436 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
31437     
31438     rid : '',
31439     active : false,
31440     disabled : false,
31441     html : '',
31442     position : 'bottom',
31443     icon : false,
31444     
31445     getAutoCreate : function()
31446     {
31447         var iconCls = 'roo-navigation-bar-item-icon';
31448         
31449         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
31450         
31451         var cfg = {
31452             tag: 'li',
31453             cls: 'roo-navigation-bar-item',
31454             cn : [
31455                 {
31456                     tag : 'i',
31457                     cls : iconCls
31458                 }
31459             ]
31460         };
31461         
31462         if(this.active){
31463             cfg.cls += ' active';
31464         }
31465         if(this.disabled){
31466             cfg.cls += ' disabled';
31467         }
31468         
31469         return cfg;
31470     },
31471     
31472     disable : function()
31473     {
31474         this.setDisabled(true);
31475     },
31476     
31477     enable : function()
31478     {
31479         this.setDisabled(false);
31480     },
31481     
31482     initEvents: function() 
31483     {
31484         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
31485         
31486         this.iconEl.on('click', this.onClick, this);
31487     },
31488     
31489     onClick : function(e)
31490     {
31491         e.preventDefault();
31492         
31493         if(this.disabled){
31494             return;
31495         }
31496         
31497         if(this.fireEvent('click', this, e) === false){
31498             return;
31499         };
31500         
31501         this.parent().setActiveItem(this);
31502     },
31503     
31504     isActive: function () 
31505     {
31506         return this.active;
31507     },
31508     
31509     setActive : function(state)
31510     {
31511         if(this.active == state){
31512             return;
31513         }
31514         
31515         this.active = state;
31516         
31517         if (state) {
31518             this.el.addClass('active');
31519             return;
31520         }
31521         
31522         this.el.removeClass('active');
31523         
31524         return;
31525     },
31526     
31527     setDisabled : function(state)
31528     {
31529         if(this.disabled == state){
31530             return;
31531         }
31532         
31533         this.disabled = state;
31534         
31535         if (state) {
31536             this.el.addClass('disabled');
31537             return;
31538         }
31539         
31540         this.el.removeClass('disabled');
31541     },
31542     
31543     tooltipEl : function()
31544     {
31545         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
31546     }
31547 });
31548  
31549
31550  /*
31551  * - LGPL
31552  *
31553  * FieldLabel
31554  * 
31555  */
31556
31557 /**
31558  * @class Roo.bootstrap.FieldLabel
31559  * @extends Roo.bootstrap.Component
31560  * Bootstrap FieldLabel class
31561  * @cfg {String} html contents of the element
31562  * @cfg {String} tag tag of the element default label
31563  * @cfg {String} cls class of the element
31564  * @cfg {String} target label target 
31565  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
31566  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
31567  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
31568  * @cfg {String} iconTooltip default "This field is required"
31569  * @cfg {String} indicatorpos (left|right) default left
31570  * 
31571  * @constructor
31572  * Create a new FieldLabel
31573  * @param {Object} config The config object
31574  */
31575
31576 Roo.bootstrap.FieldLabel = function(config){
31577     Roo.bootstrap.Element.superclass.constructor.call(this, config);
31578     
31579     this.addEvents({
31580             /**
31581              * @event invalid
31582              * Fires after the field has been marked as invalid.
31583              * @param {Roo.form.FieldLabel} this
31584              * @param {String} msg The validation message
31585              */
31586             invalid : true,
31587             /**
31588              * @event valid
31589              * Fires after the field has been validated with no errors.
31590              * @param {Roo.form.FieldLabel} this
31591              */
31592             valid : true
31593         });
31594 };
31595
31596 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
31597     
31598     tag: 'label',
31599     cls: '',
31600     html: '',
31601     target: '',
31602     allowBlank : true,
31603     invalidClass : 'has-warning',
31604     validClass : 'has-success',
31605     iconTooltip : 'This field is required',
31606     indicatorpos : 'left',
31607     
31608     getAutoCreate : function(){
31609         
31610         var cls = "";
31611         if (!this.allowBlank) {
31612             cls  = "visible";
31613         }
31614         
31615         var cfg = {
31616             tag : this.tag,
31617             cls : 'roo-bootstrap-field-label ' + this.cls,
31618             for : this.target,
31619             cn : [
31620                 {
31621                     tag : 'i',
31622                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
31623                     tooltip : this.iconTooltip
31624                 },
31625                 {
31626                     tag : 'span',
31627                     html : this.html
31628                 }
31629             ] 
31630         };
31631         
31632         if(this.indicatorpos == 'right'){
31633             var cfg = {
31634                 tag : this.tag,
31635                 cls : 'roo-bootstrap-field-label ' + this.cls,
31636                 for : this.target,
31637                 cn : [
31638                     {
31639                         tag : 'span',
31640                         html : this.html
31641                     },
31642                     {
31643                         tag : 'i',
31644                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
31645                         tooltip : this.iconTooltip
31646                     }
31647                 ] 
31648             };
31649         }
31650         
31651         return cfg;
31652     },
31653     
31654     initEvents: function() 
31655     {
31656         Roo.bootstrap.Element.superclass.initEvents.call(this);
31657         
31658         this.indicator = this.indicatorEl();
31659         
31660         if(this.indicator){
31661             this.indicator.removeClass('visible');
31662             this.indicator.addClass('invisible');
31663         }
31664         
31665         Roo.bootstrap.FieldLabel.register(this);
31666     },
31667     
31668     indicatorEl : function()
31669     {
31670         var indicator = this.el.select('i.roo-required-indicator',true).first();
31671         
31672         if(!indicator){
31673             return false;
31674         }
31675         
31676         return indicator;
31677         
31678     },
31679     
31680     /**
31681      * Mark this field as valid
31682      */
31683     markValid : function()
31684     {
31685         if(this.indicator){
31686             this.indicator.removeClass('visible');
31687             this.indicator.addClass('invisible');
31688         }
31689         if (Roo.bootstrap.version == 3) {
31690             this.el.removeClass(this.invalidClass);
31691             this.el.addClass(this.validClass);
31692         } else {
31693             this.el.removeClass('is-invalid');
31694             this.el.addClass('is-valid');
31695         }
31696         
31697         
31698         this.fireEvent('valid', this);
31699     },
31700     
31701     /**
31702      * Mark this field as invalid
31703      * @param {String} msg The validation message
31704      */
31705     markInvalid : function(msg)
31706     {
31707         if(this.indicator){
31708             this.indicator.removeClass('invisible');
31709             this.indicator.addClass('visible');
31710         }
31711           if (Roo.bootstrap.version == 3) {
31712             this.el.removeClass(this.validClass);
31713             this.el.addClass(this.invalidClass);
31714         } else {
31715             this.el.removeClass('is-valid');
31716             this.el.addClass('is-invalid');
31717         }
31718         
31719         
31720         this.fireEvent('invalid', this, msg);
31721     }
31722     
31723    
31724 });
31725
31726 Roo.apply(Roo.bootstrap.FieldLabel, {
31727     
31728     groups: {},
31729     
31730      /**
31731     * register a FieldLabel Group
31732     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
31733     */
31734     register : function(label)
31735     {
31736         if(this.groups.hasOwnProperty(label.target)){
31737             return;
31738         }
31739      
31740         this.groups[label.target] = label;
31741         
31742     },
31743     /**
31744     * fetch a FieldLabel Group based on the target
31745     * @param {string} target
31746     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
31747     */
31748     get: function(target) {
31749         if (typeof(this.groups[target]) == 'undefined') {
31750             return false;
31751         }
31752         
31753         return this.groups[target] ;
31754     }
31755 });
31756
31757  
31758
31759  /*
31760  * - LGPL
31761  *
31762  * page DateSplitField.
31763  * 
31764  */
31765
31766
31767 /**
31768  * @class Roo.bootstrap.DateSplitField
31769  * @extends Roo.bootstrap.Component
31770  * Bootstrap DateSplitField class
31771  * @cfg {string} fieldLabel - the label associated
31772  * @cfg {Number} labelWidth set the width of label (0-12)
31773  * @cfg {String} labelAlign (top|left)
31774  * @cfg {Boolean} dayAllowBlank (true|false) default false
31775  * @cfg {Boolean} monthAllowBlank (true|false) default false
31776  * @cfg {Boolean} yearAllowBlank (true|false) default false
31777  * @cfg {string} dayPlaceholder 
31778  * @cfg {string} monthPlaceholder
31779  * @cfg {string} yearPlaceholder
31780  * @cfg {string} dayFormat default 'd'
31781  * @cfg {string} monthFormat default 'm'
31782  * @cfg {string} yearFormat default 'Y'
31783  * @cfg {Number} labellg set the width of label (1-12)
31784  * @cfg {Number} labelmd set the width of label (1-12)
31785  * @cfg {Number} labelsm set the width of label (1-12)
31786  * @cfg {Number} labelxs set the width of label (1-12)
31787
31788  *     
31789  * @constructor
31790  * Create a new DateSplitField
31791  * @param {Object} config The config object
31792  */
31793
31794 Roo.bootstrap.DateSplitField = function(config){
31795     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
31796     
31797     this.addEvents({
31798         // raw events
31799          /**
31800          * @event years
31801          * getting the data of years
31802          * @param {Roo.bootstrap.DateSplitField} this
31803          * @param {Object} years
31804          */
31805         "years" : true,
31806         /**
31807          * @event days
31808          * getting the data of days
31809          * @param {Roo.bootstrap.DateSplitField} this
31810          * @param {Object} days
31811          */
31812         "days" : true,
31813         /**
31814          * @event invalid
31815          * Fires after the field has been marked as invalid.
31816          * @param {Roo.form.Field} this
31817          * @param {String} msg The validation message
31818          */
31819         invalid : true,
31820        /**
31821          * @event valid
31822          * Fires after the field has been validated with no errors.
31823          * @param {Roo.form.Field} this
31824          */
31825         valid : true
31826     });
31827 };
31828
31829 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
31830     
31831     fieldLabel : '',
31832     labelAlign : 'top',
31833     labelWidth : 3,
31834     dayAllowBlank : false,
31835     monthAllowBlank : false,
31836     yearAllowBlank : false,
31837     dayPlaceholder : '',
31838     monthPlaceholder : '',
31839     yearPlaceholder : '',
31840     dayFormat : 'd',
31841     monthFormat : 'm',
31842     yearFormat : 'Y',
31843     isFormField : true,
31844     labellg : 0,
31845     labelmd : 0,
31846     labelsm : 0,
31847     labelxs : 0,
31848     
31849     getAutoCreate : function()
31850     {
31851         var cfg = {
31852             tag : 'div',
31853             cls : 'row roo-date-split-field-group',
31854             cn : [
31855                 {
31856                     tag : 'input',
31857                     type : 'hidden',
31858                     cls : 'form-hidden-field roo-date-split-field-group-value',
31859                     name : this.name
31860                 }
31861             ]
31862         };
31863         
31864         var labelCls = 'col-md-12';
31865         var contentCls = 'col-md-4';
31866         
31867         if(this.fieldLabel){
31868             
31869             var label = {
31870                 tag : 'div',
31871                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
31872                 cn : [
31873                     {
31874                         tag : 'label',
31875                         html : this.fieldLabel
31876                     }
31877                 ]
31878             };
31879             
31880             if(this.labelAlign == 'left'){
31881             
31882                 if(this.labelWidth > 12){
31883                     label.style = "width: " + this.labelWidth + 'px';
31884                 }
31885
31886                 if(this.labelWidth < 13 && this.labelmd == 0){
31887                     this.labelmd = this.labelWidth;
31888                 }
31889
31890                 if(this.labellg > 0){
31891                     labelCls = ' col-lg-' + this.labellg;
31892                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
31893                 }
31894
31895                 if(this.labelmd > 0){
31896                     labelCls = ' col-md-' + this.labelmd;
31897                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
31898                 }
31899
31900                 if(this.labelsm > 0){
31901                     labelCls = ' col-sm-' + this.labelsm;
31902                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
31903                 }
31904
31905                 if(this.labelxs > 0){
31906                     labelCls = ' col-xs-' + this.labelxs;
31907                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
31908                 }
31909             }
31910             
31911             label.cls += ' ' + labelCls;
31912             
31913             cfg.cn.push(label);
31914         }
31915         
31916         Roo.each(['day', 'month', 'year'], function(t){
31917             cfg.cn.push({
31918                 tag : 'div',
31919                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
31920             });
31921         }, this);
31922         
31923         return cfg;
31924     },
31925     
31926     inputEl: function ()
31927     {
31928         return this.el.select('.roo-date-split-field-group-value', true).first();
31929     },
31930     
31931     onRender : function(ct, position) 
31932     {
31933         var _this = this;
31934         
31935         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
31936         
31937         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
31938         
31939         this.dayField = new Roo.bootstrap.ComboBox({
31940             allowBlank : this.dayAllowBlank,
31941             alwaysQuery : true,
31942             displayField : 'value',
31943             editable : false,
31944             fieldLabel : '',
31945             forceSelection : true,
31946             mode : 'local',
31947             placeholder : this.dayPlaceholder,
31948             selectOnFocus : true,
31949             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
31950             triggerAction : 'all',
31951             typeAhead : true,
31952             valueField : 'value',
31953             store : new Roo.data.SimpleStore({
31954                 data : (function() {    
31955                     var days = [];
31956                     _this.fireEvent('days', _this, days);
31957                     return days;
31958                 })(),
31959                 fields : [ 'value' ]
31960             }),
31961             listeners : {
31962                 select : function (_self, record, index)
31963                 {
31964                     _this.setValue(_this.getValue());
31965                 }
31966             }
31967         });
31968
31969         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
31970         
31971         this.monthField = new Roo.bootstrap.MonthField({
31972             after : '<i class=\"fa fa-calendar\"></i>',
31973             allowBlank : this.monthAllowBlank,
31974             placeholder : this.monthPlaceholder,
31975             readOnly : true,
31976             listeners : {
31977                 render : function (_self)
31978                 {
31979                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
31980                         e.preventDefault();
31981                         _self.focus();
31982                     });
31983                 },
31984                 select : function (_self, oldvalue, newvalue)
31985                 {
31986                     _this.setValue(_this.getValue());
31987                 }
31988             }
31989         });
31990         
31991         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
31992         
31993         this.yearField = new Roo.bootstrap.ComboBox({
31994             allowBlank : this.yearAllowBlank,
31995             alwaysQuery : true,
31996             displayField : 'value',
31997             editable : false,
31998             fieldLabel : '',
31999             forceSelection : true,
32000             mode : 'local',
32001             placeholder : this.yearPlaceholder,
32002             selectOnFocus : true,
32003             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32004             triggerAction : 'all',
32005             typeAhead : true,
32006             valueField : 'value',
32007             store : new Roo.data.SimpleStore({
32008                 data : (function() {
32009                     var years = [];
32010                     _this.fireEvent('years', _this, years);
32011                     return years;
32012                 })(),
32013                 fields : [ 'value' ]
32014             }),
32015             listeners : {
32016                 select : function (_self, record, index)
32017                 {
32018                     _this.setValue(_this.getValue());
32019                 }
32020             }
32021         });
32022
32023         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
32024     },
32025     
32026     setValue : function(v, format)
32027     {
32028         this.inputEl.dom.value = v;
32029         
32030         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
32031         
32032         var d = Date.parseDate(v, f);
32033         
32034         if(!d){
32035             this.validate();
32036             return;
32037         }
32038         
32039         this.setDay(d.format(this.dayFormat));
32040         this.setMonth(d.format(this.monthFormat));
32041         this.setYear(d.format(this.yearFormat));
32042         
32043         this.validate();
32044         
32045         return;
32046     },
32047     
32048     setDay : function(v)
32049     {
32050         this.dayField.setValue(v);
32051         this.inputEl.dom.value = this.getValue();
32052         this.validate();
32053         return;
32054     },
32055     
32056     setMonth : function(v)
32057     {
32058         this.monthField.setValue(v, true);
32059         this.inputEl.dom.value = this.getValue();
32060         this.validate();
32061         return;
32062     },
32063     
32064     setYear : function(v)
32065     {
32066         this.yearField.setValue(v);
32067         this.inputEl.dom.value = this.getValue();
32068         this.validate();
32069         return;
32070     },
32071     
32072     getDay : function()
32073     {
32074         return this.dayField.getValue();
32075     },
32076     
32077     getMonth : function()
32078     {
32079         return this.monthField.getValue();
32080     },
32081     
32082     getYear : function()
32083     {
32084         return this.yearField.getValue();
32085     },
32086     
32087     getValue : function()
32088     {
32089         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
32090         
32091         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
32092         
32093         return date;
32094     },
32095     
32096     reset : function()
32097     {
32098         this.setDay('');
32099         this.setMonth('');
32100         this.setYear('');
32101         this.inputEl.dom.value = '';
32102         this.validate();
32103         return;
32104     },
32105     
32106     validate : function()
32107     {
32108         var d = this.dayField.validate();
32109         var m = this.monthField.validate();
32110         var y = this.yearField.validate();
32111         
32112         var valid = true;
32113         
32114         if(
32115                 (!this.dayAllowBlank && !d) ||
32116                 (!this.monthAllowBlank && !m) ||
32117                 (!this.yearAllowBlank && !y)
32118         ){
32119             valid = false;
32120         }
32121         
32122         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
32123             return valid;
32124         }
32125         
32126         if(valid){
32127             this.markValid();
32128             return valid;
32129         }
32130         
32131         this.markInvalid();
32132         
32133         return valid;
32134     },
32135     
32136     markValid : function()
32137     {
32138         
32139         var label = this.el.select('label', true).first();
32140         var icon = this.el.select('i.fa-star', true).first();
32141
32142         if(label && icon){
32143             icon.remove();
32144         }
32145         
32146         this.fireEvent('valid', this);
32147     },
32148     
32149      /**
32150      * Mark this field as invalid
32151      * @param {String} msg The validation message
32152      */
32153     markInvalid : function(msg)
32154     {
32155         
32156         var label = this.el.select('label', true).first();
32157         var icon = this.el.select('i.fa-star', true).first();
32158
32159         if(label && !icon){
32160             this.el.select('.roo-date-split-field-label', true).createChild({
32161                 tag : 'i',
32162                 cls : 'text-danger fa fa-lg fa-star',
32163                 tooltip : 'This field is required',
32164                 style : 'margin-right:5px;'
32165             }, label, true);
32166         }
32167         
32168         this.fireEvent('invalid', this, msg);
32169     },
32170     
32171     clearInvalid : function()
32172     {
32173         var label = this.el.select('label', true).first();
32174         var icon = this.el.select('i.fa-star', true).first();
32175
32176         if(label && icon){
32177             icon.remove();
32178         }
32179         
32180         this.fireEvent('valid', this);
32181     },
32182     
32183     getName: function()
32184     {
32185         return this.name;
32186     }
32187     
32188 });
32189
32190  /**
32191  *
32192  * This is based on 
32193  * http://masonry.desandro.com
32194  *
32195  * The idea is to render all the bricks based on vertical width...
32196  *
32197  * The original code extends 'outlayer' - we might need to use that....
32198  * 
32199  */
32200
32201
32202 /**
32203  * @class Roo.bootstrap.LayoutMasonry
32204  * @extends Roo.bootstrap.Component
32205  * Bootstrap Layout Masonry class
32206  * 
32207  * @constructor
32208  * Create a new Element
32209  * @param {Object} config The config object
32210  */
32211
32212 Roo.bootstrap.LayoutMasonry = function(config){
32213     
32214     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
32215     
32216     this.bricks = [];
32217     
32218     Roo.bootstrap.LayoutMasonry.register(this);
32219     
32220     this.addEvents({
32221         // raw events
32222         /**
32223          * @event layout
32224          * Fire after layout the items
32225          * @param {Roo.bootstrap.LayoutMasonry} this
32226          * @param {Roo.EventObject} e
32227          */
32228         "layout" : true
32229     });
32230     
32231 };
32232
32233 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
32234     
32235     /**
32236      * @cfg {Boolean} isLayoutInstant = no animation?
32237      */   
32238     isLayoutInstant : false, // needed?
32239    
32240     /**
32241      * @cfg {Number} boxWidth  width of the columns
32242      */   
32243     boxWidth : 450,
32244     
32245       /**
32246      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
32247      */   
32248     boxHeight : 0,
32249     
32250     /**
32251      * @cfg {Number} padWidth padding below box..
32252      */   
32253     padWidth : 10, 
32254     
32255     /**
32256      * @cfg {Number} gutter gutter width..
32257      */   
32258     gutter : 10,
32259     
32260      /**
32261      * @cfg {Number} maxCols maximum number of columns
32262      */   
32263     
32264     maxCols: 0,
32265     
32266     /**
32267      * @cfg {Boolean} isAutoInitial defalut true
32268      */   
32269     isAutoInitial : true, 
32270     
32271     containerWidth: 0,
32272     
32273     /**
32274      * @cfg {Boolean} isHorizontal defalut false
32275      */   
32276     isHorizontal : false, 
32277
32278     currentSize : null,
32279     
32280     tag: 'div',
32281     
32282     cls: '',
32283     
32284     bricks: null, //CompositeElement
32285     
32286     cols : 1,
32287     
32288     _isLayoutInited : false,
32289     
32290 //    isAlternative : false, // only use for vertical layout...
32291     
32292     /**
32293      * @cfg {Number} alternativePadWidth padding below box..
32294      */   
32295     alternativePadWidth : 50,
32296     
32297     selectedBrick : [],
32298     
32299     getAutoCreate : function(){
32300         
32301         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
32302         
32303         var cfg = {
32304             tag: this.tag,
32305             cls: 'blog-masonary-wrapper ' + this.cls,
32306             cn : {
32307                 cls : 'mas-boxes masonary'
32308             }
32309         };
32310         
32311         return cfg;
32312     },
32313     
32314     getChildContainer: function( )
32315     {
32316         if (this.boxesEl) {
32317             return this.boxesEl;
32318         }
32319         
32320         this.boxesEl = this.el.select('.mas-boxes').first();
32321         
32322         return this.boxesEl;
32323     },
32324     
32325     
32326     initEvents : function()
32327     {
32328         var _this = this;
32329         
32330         if(this.isAutoInitial){
32331             Roo.log('hook children rendered');
32332             this.on('childrenrendered', function() {
32333                 Roo.log('children rendered');
32334                 _this.initial();
32335             } ,this);
32336         }
32337     },
32338     
32339     initial : function()
32340     {
32341         this.selectedBrick = [];
32342         
32343         this.currentSize = this.el.getBox(true);
32344         
32345         Roo.EventManager.onWindowResize(this.resize, this); 
32346
32347         if(!this.isAutoInitial){
32348             this.layout();
32349             return;
32350         }
32351         
32352         this.layout();
32353         
32354         return;
32355         //this.layout.defer(500,this);
32356         
32357     },
32358     
32359     resize : function()
32360     {
32361         var cs = this.el.getBox(true);
32362         
32363         if (
32364                 this.currentSize.width == cs.width && 
32365                 this.currentSize.x == cs.x && 
32366                 this.currentSize.height == cs.height && 
32367                 this.currentSize.y == cs.y 
32368         ) {
32369             Roo.log("no change in with or X or Y");
32370             return;
32371         }
32372         
32373         this.currentSize = cs;
32374         
32375         this.layout();
32376         
32377     },
32378     
32379     layout : function()
32380     {   
32381         this._resetLayout();
32382         
32383         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32384         
32385         this.layoutItems( isInstant );
32386       
32387         this._isLayoutInited = true;
32388         
32389         this.fireEvent('layout', this);
32390         
32391     },
32392     
32393     _resetLayout : function()
32394     {
32395         if(this.isHorizontal){
32396             this.horizontalMeasureColumns();
32397             return;
32398         }
32399         
32400         this.verticalMeasureColumns();
32401         
32402     },
32403     
32404     verticalMeasureColumns : function()
32405     {
32406         this.getContainerWidth();
32407         
32408 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
32409 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
32410 //            return;
32411 //        }
32412         
32413         var boxWidth = this.boxWidth + this.padWidth;
32414         
32415         if(this.containerWidth < this.boxWidth){
32416             boxWidth = this.containerWidth
32417         }
32418         
32419         var containerWidth = this.containerWidth;
32420         
32421         var cols = Math.floor(containerWidth / boxWidth);
32422         
32423         this.cols = Math.max( cols, 1 );
32424         
32425         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32426         
32427         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
32428         
32429         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
32430         
32431         this.colWidth = boxWidth + avail - this.padWidth;
32432         
32433         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
32434         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
32435     },
32436     
32437     horizontalMeasureColumns : function()
32438     {
32439         this.getContainerWidth();
32440         
32441         var boxWidth = this.boxWidth;
32442         
32443         if(this.containerWidth < boxWidth){
32444             boxWidth = this.containerWidth;
32445         }
32446         
32447         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
32448         
32449         this.el.setHeight(boxWidth);
32450         
32451     },
32452     
32453     getContainerWidth : function()
32454     {
32455         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32456     },
32457     
32458     layoutItems : function( isInstant )
32459     {
32460         Roo.log(this.bricks);
32461         
32462         var items = Roo.apply([], this.bricks);
32463         
32464         if(this.isHorizontal){
32465             this._horizontalLayoutItems( items , isInstant );
32466             return;
32467         }
32468         
32469 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
32470 //            this._verticalAlternativeLayoutItems( items , isInstant );
32471 //            return;
32472 //        }
32473         
32474         this._verticalLayoutItems( items , isInstant );
32475         
32476     },
32477     
32478     _verticalLayoutItems : function ( items , isInstant)
32479     {
32480         if ( !items || !items.length ) {
32481             return;
32482         }
32483         
32484         var standard = [
32485             ['xs', 'xs', 'xs', 'tall'],
32486             ['xs', 'xs', 'tall'],
32487             ['xs', 'xs', 'sm'],
32488             ['xs', 'xs', 'xs'],
32489             ['xs', 'tall'],
32490             ['xs', 'sm'],
32491             ['xs', 'xs'],
32492             ['xs'],
32493             
32494             ['sm', 'xs', 'xs'],
32495             ['sm', 'xs'],
32496             ['sm'],
32497             
32498             ['tall', 'xs', 'xs', 'xs'],
32499             ['tall', 'xs', 'xs'],
32500             ['tall', 'xs'],
32501             ['tall']
32502             
32503         ];
32504         
32505         var queue = [];
32506         
32507         var boxes = [];
32508         
32509         var box = [];
32510         
32511         Roo.each(items, function(item, k){
32512             
32513             switch (item.size) {
32514                 // these layouts take up a full box,
32515                 case 'md' :
32516                 case 'md-left' :
32517                 case 'md-right' :
32518                 case 'wide' :
32519                     
32520                     if(box.length){
32521                         boxes.push(box);
32522                         box = [];
32523                     }
32524                     
32525                     boxes.push([item]);
32526                     
32527                     break;
32528                     
32529                 case 'xs' :
32530                 case 'sm' :
32531                 case 'tall' :
32532                     
32533                     box.push(item);
32534                     
32535                     break;
32536                 default :
32537                     break;
32538                     
32539             }
32540             
32541         }, this);
32542         
32543         if(box.length){
32544             boxes.push(box);
32545             box = [];
32546         }
32547         
32548         var filterPattern = function(box, length)
32549         {
32550             if(!box.length){
32551                 return;
32552             }
32553             
32554             var match = false;
32555             
32556             var pattern = box.slice(0, length);
32557             
32558             var format = [];
32559             
32560             Roo.each(pattern, function(i){
32561                 format.push(i.size);
32562             }, this);
32563             
32564             Roo.each(standard, function(s){
32565                 
32566                 if(String(s) != String(format)){
32567                     return;
32568                 }
32569                 
32570                 match = true;
32571                 return false;
32572                 
32573             }, this);
32574             
32575             if(!match && length == 1){
32576                 return;
32577             }
32578             
32579             if(!match){
32580                 filterPattern(box, length - 1);
32581                 return;
32582             }
32583                 
32584             queue.push(pattern);
32585
32586             box = box.slice(length, box.length);
32587
32588             filterPattern(box, 4);
32589
32590             return;
32591             
32592         }
32593         
32594         Roo.each(boxes, function(box, k){
32595             
32596             if(!box.length){
32597                 return;
32598             }
32599             
32600             if(box.length == 1){
32601                 queue.push(box);
32602                 return;
32603             }
32604             
32605             filterPattern(box, 4);
32606             
32607         }, this);
32608         
32609         this._processVerticalLayoutQueue( queue, isInstant );
32610         
32611     },
32612     
32613 //    _verticalAlternativeLayoutItems : function( items , isInstant )
32614 //    {
32615 //        if ( !items || !items.length ) {
32616 //            return;
32617 //        }
32618 //
32619 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
32620 //        
32621 //    },
32622     
32623     _horizontalLayoutItems : function ( items , isInstant)
32624     {
32625         if ( !items || !items.length || items.length < 3) {
32626             return;
32627         }
32628         
32629         items.reverse();
32630         
32631         var eItems = items.slice(0, 3);
32632         
32633         items = items.slice(3, items.length);
32634         
32635         var standard = [
32636             ['xs', 'xs', 'xs', 'wide'],
32637             ['xs', 'xs', 'wide'],
32638             ['xs', 'xs', 'sm'],
32639             ['xs', 'xs', 'xs'],
32640             ['xs', 'wide'],
32641             ['xs', 'sm'],
32642             ['xs', 'xs'],
32643             ['xs'],
32644             
32645             ['sm', 'xs', 'xs'],
32646             ['sm', 'xs'],
32647             ['sm'],
32648             
32649             ['wide', 'xs', 'xs', 'xs'],
32650             ['wide', 'xs', 'xs'],
32651             ['wide', 'xs'],
32652             ['wide'],
32653             
32654             ['wide-thin']
32655         ];
32656         
32657         var queue = [];
32658         
32659         var boxes = [];
32660         
32661         var box = [];
32662         
32663         Roo.each(items, function(item, k){
32664             
32665             switch (item.size) {
32666                 case 'md' :
32667                 case 'md-left' :
32668                 case 'md-right' :
32669                 case 'tall' :
32670                     
32671                     if(box.length){
32672                         boxes.push(box);
32673                         box = [];
32674                     }
32675                     
32676                     boxes.push([item]);
32677                     
32678                     break;
32679                     
32680                 case 'xs' :
32681                 case 'sm' :
32682                 case 'wide' :
32683                 case 'wide-thin' :
32684                     
32685                     box.push(item);
32686                     
32687                     break;
32688                 default :
32689                     break;
32690                     
32691             }
32692             
32693         }, this);
32694         
32695         if(box.length){
32696             boxes.push(box);
32697             box = [];
32698         }
32699         
32700         var filterPattern = function(box, length)
32701         {
32702             if(!box.length){
32703                 return;
32704             }
32705             
32706             var match = false;
32707             
32708             var pattern = box.slice(0, length);
32709             
32710             var format = [];
32711             
32712             Roo.each(pattern, function(i){
32713                 format.push(i.size);
32714             }, this);
32715             
32716             Roo.each(standard, function(s){
32717                 
32718                 if(String(s) != String(format)){
32719                     return;
32720                 }
32721                 
32722                 match = true;
32723                 return false;
32724                 
32725             }, this);
32726             
32727             if(!match && length == 1){
32728                 return;
32729             }
32730             
32731             if(!match){
32732                 filterPattern(box, length - 1);
32733                 return;
32734             }
32735                 
32736             queue.push(pattern);
32737
32738             box = box.slice(length, box.length);
32739
32740             filterPattern(box, 4);
32741
32742             return;
32743             
32744         }
32745         
32746         Roo.each(boxes, function(box, k){
32747             
32748             if(!box.length){
32749                 return;
32750             }
32751             
32752             if(box.length == 1){
32753                 queue.push(box);
32754                 return;
32755             }
32756             
32757             filterPattern(box, 4);
32758             
32759         }, this);
32760         
32761         
32762         var prune = [];
32763         
32764         var pos = this.el.getBox(true);
32765         
32766         var minX = pos.x;
32767         
32768         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
32769         
32770         var hit_end = false;
32771         
32772         Roo.each(queue, function(box){
32773             
32774             if(hit_end){
32775                 
32776                 Roo.each(box, function(b){
32777                 
32778                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
32779                     b.el.hide();
32780
32781                 }, this);
32782
32783                 return;
32784             }
32785             
32786             var mx = 0;
32787             
32788             Roo.each(box, function(b){
32789                 
32790                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
32791                 b.el.show();
32792
32793                 mx = Math.max(mx, b.x);
32794                 
32795             }, this);
32796             
32797             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
32798             
32799             if(maxX < minX){
32800                 
32801                 Roo.each(box, function(b){
32802                 
32803                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
32804                     b.el.hide();
32805                     
32806                 }, this);
32807                 
32808                 hit_end = true;
32809                 
32810                 return;
32811             }
32812             
32813             prune.push(box);
32814             
32815         }, this);
32816         
32817         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
32818     },
32819     
32820     /** Sets position of item in DOM
32821     * @param {Element} item
32822     * @param {Number} x - horizontal position
32823     * @param {Number} y - vertical position
32824     * @param {Boolean} isInstant - disables transitions
32825     */
32826     _processVerticalLayoutQueue : function( queue, isInstant )
32827     {
32828         var pos = this.el.getBox(true);
32829         var x = pos.x;
32830         var y = pos.y;
32831         var maxY = [];
32832         
32833         for (var i = 0; i < this.cols; i++){
32834             maxY[i] = pos.y;
32835         }
32836         
32837         Roo.each(queue, function(box, k){
32838             
32839             var col = k % this.cols;
32840             
32841             Roo.each(box, function(b,kk){
32842                 
32843                 b.el.position('absolute');
32844                 
32845                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32846                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32847                 
32848                 if(b.size == 'md-left' || b.size == 'md-right'){
32849                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
32850                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
32851                 }
32852                 
32853                 b.el.setWidth(width);
32854                 b.el.setHeight(height);
32855                 // iframe?
32856                 b.el.select('iframe',true).setSize(width,height);
32857                 
32858             }, this);
32859             
32860             for (var i = 0; i < this.cols; i++){
32861                 
32862                 if(maxY[i] < maxY[col]){
32863                     col = i;
32864                     continue;
32865                 }
32866                 
32867                 col = Math.min(col, i);
32868                 
32869             }
32870             
32871             x = pos.x + col * (this.colWidth + this.padWidth);
32872             
32873             y = maxY[col];
32874             
32875             var positions = [];
32876             
32877             switch (box.length){
32878                 case 1 :
32879                     positions = this.getVerticalOneBoxColPositions(x, y, box);
32880                     break;
32881                 case 2 :
32882                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
32883                     break;
32884                 case 3 :
32885                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
32886                     break;
32887                 case 4 :
32888                     positions = this.getVerticalFourBoxColPositions(x, y, box);
32889                     break;
32890                 default :
32891                     break;
32892             }
32893             
32894             Roo.each(box, function(b,kk){
32895                 
32896                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
32897                 
32898                 var sz = b.el.getSize();
32899                 
32900                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
32901                 
32902             }, this);
32903             
32904         }, this);
32905         
32906         var mY = 0;
32907         
32908         for (var i = 0; i < this.cols; i++){
32909             mY = Math.max(mY, maxY[i]);
32910         }
32911         
32912         this.el.setHeight(mY - pos.y);
32913         
32914     },
32915     
32916 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
32917 //    {
32918 //        var pos = this.el.getBox(true);
32919 //        var x = pos.x;
32920 //        var y = pos.y;
32921 //        var maxX = pos.right;
32922 //        
32923 //        var maxHeight = 0;
32924 //        
32925 //        Roo.each(items, function(item, k){
32926 //            
32927 //            var c = k % 2;
32928 //            
32929 //            item.el.position('absolute');
32930 //                
32931 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
32932 //
32933 //            item.el.setWidth(width);
32934 //
32935 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
32936 //
32937 //            item.el.setHeight(height);
32938 //            
32939 //            if(c == 0){
32940 //                item.el.setXY([x, y], isInstant ? false : true);
32941 //            } else {
32942 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
32943 //            }
32944 //            
32945 //            y = y + height + this.alternativePadWidth;
32946 //            
32947 //            maxHeight = maxHeight + height + this.alternativePadWidth;
32948 //            
32949 //        }, this);
32950 //        
32951 //        this.el.setHeight(maxHeight);
32952 //        
32953 //    },
32954     
32955     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
32956     {
32957         var pos = this.el.getBox(true);
32958         
32959         var minX = pos.x;
32960         var minY = pos.y;
32961         
32962         var maxX = pos.right;
32963         
32964         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
32965         
32966         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
32967         
32968         Roo.each(queue, function(box, k){
32969             
32970             Roo.each(box, function(b, kk){
32971                 
32972                 b.el.position('absolute');
32973                 
32974                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32975                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32976                 
32977                 if(b.size == 'md-left' || b.size == 'md-right'){
32978                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
32979                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
32980                 }
32981                 
32982                 b.el.setWidth(width);
32983                 b.el.setHeight(height);
32984                 
32985             }, this);
32986             
32987             if(!box.length){
32988                 return;
32989             }
32990             
32991             var positions = [];
32992             
32993             switch (box.length){
32994                 case 1 :
32995                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
32996                     break;
32997                 case 2 :
32998                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
32999                     break;
33000                 case 3 :
33001                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
33002                     break;
33003                 case 4 :
33004                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
33005                     break;
33006                 default :
33007                     break;
33008             }
33009             
33010             Roo.each(box, function(b,kk){
33011                 
33012                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33013                 
33014                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
33015                 
33016             }, this);
33017             
33018         }, this);
33019         
33020     },
33021     
33022     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
33023     {
33024         Roo.each(eItems, function(b,k){
33025             
33026             b.size = (k == 0) ? 'sm' : 'xs';
33027             b.x = (k == 0) ? 2 : 1;
33028             b.y = (k == 0) ? 2 : 1;
33029             
33030             b.el.position('absolute');
33031             
33032             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33033                 
33034             b.el.setWidth(width);
33035             
33036             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33037             
33038             b.el.setHeight(height);
33039             
33040         }, this);
33041
33042         var positions = [];
33043         
33044         positions.push({
33045             x : maxX - this.unitWidth * 2 - this.gutter,
33046             y : minY
33047         });
33048         
33049         positions.push({
33050             x : maxX - this.unitWidth,
33051             y : minY + (this.unitWidth + this.gutter) * 2
33052         });
33053         
33054         positions.push({
33055             x : maxX - this.unitWidth * 3 - this.gutter * 2,
33056             y : minY
33057         });
33058         
33059         Roo.each(eItems, function(b,k){
33060             
33061             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
33062
33063         }, this);
33064         
33065     },
33066     
33067     getVerticalOneBoxColPositions : function(x, y, box)
33068     {
33069         var pos = [];
33070         
33071         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
33072         
33073         if(box[0].size == 'md-left'){
33074             rand = 0;
33075         }
33076         
33077         if(box[0].size == 'md-right'){
33078             rand = 1;
33079         }
33080         
33081         pos.push({
33082             x : x + (this.unitWidth + this.gutter) * rand,
33083             y : y
33084         });
33085         
33086         return pos;
33087     },
33088     
33089     getVerticalTwoBoxColPositions : function(x, y, box)
33090     {
33091         var pos = [];
33092         
33093         if(box[0].size == 'xs'){
33094             
33095             pos.push({
33096                 x : x,
33097                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
33098             });
33099
33100             pos.push({
33101                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
33102                 y : y
33103             });
33104             
33105             return pos;
33106             
33107         }
33108         
33109         pos.push({
33110             x : x,
33111             y : y
33112         });
33113
33114         pos.push({
33115             x : x + (this.unitWidth + this.gutter) * 2,
33116             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
33117         });
33118         
33119         return pos;
33120         
33121     },
33122     
33123     getVerticalThreeBoxColPositions : function(x, y, box)
33124     {
33125         var pos = [];
33126         
33127         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
33128             
33129             pos.push({
33130                 x : x,
33131                 y : y
33132             });
33133
33134             pos.push({
33135                 x : x + (this.unitWidth + this.gutter) * 1,
33136                 y : y
33137             });
33138             
33139             pos.push({
33140                 x : x + (this.unitWidth + this.gutter) * 2,
33141                 y : y
33142             });
33143             
33144             return pos;
33145             
33146         }
33147         
33148         if(box[0].size == 'xs' && box[1].size == 'xs'){
33149             
33150             pos.push({
33151                 x : x,
33152                 y : y
33153             });
33154
33155             pos.push({
33156                 x : x,
33157                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
33158             });
33159             
33160             pos.push({
33161                 x : x + (this.unitWidth + this.gutter) * 1,
33162                 y : y
33163             });
33164             
33165             return pos;
33166             
33167         }
33168         
33169         pos.push({
33170             x : x,
33171             y : y
33172         });
33173
33174         pos.push({
33175             x : x + (this.unitWidth + this.gutter) * 2,
33176             y : y
33177         });
33178
33179         pos.push({
33180             x : x + (this.unitWidth + this.gutter) * 2,
33181             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
33182         });
33183             
33184         return pos;
33185         
33186     },
33187     
33188     getVerticalFourBoxColPositions : function(x, y, box)
33189     {
33190         var pos = [];
33191         
33192         if(box[0].size == 'xs'){
33193             
33194             pos.push({
33195                 x : x,
33196                 y : y
33197             });
33198
33199             pos.push({
33200                 x : x,
33201                 y : y + (this.unitHeight + this.gutter) * 1
33202             });
33203             
33204             pos.push({
33205                 x : x,
33206                 y : y + (this.unitHeight + this.gutter) * 2
33207             });
33208             
33209             pos.push({
33210                 x : x + (this.unitWidth + this.gutter) * 1,
33211                 y : y
33212             });
33213             
33214             return pos;
33215             
33216         }
33217         
33218         pos.push({
33219             x : x,
33220             y : y
33221         });
33222
33223         pos.push({
33224             x : x + (this.unitWidth + this.gutter) * 2,
33225             y : y
33226         });
33227
33228         pos.push({
33229             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
33230             y : y + (this.unitHeight + this.gutter) * 1
33231         });
33232
33233         pos.push({
33234             x : x + (this.unitWidth + this.gutter) * 2,
33235             y : y + (this.unitWidth + this.gutter) * 2
33236         });
33237
33238         return pos;
33239         
33240     },
33241     
33242     getHorizontalOneBoxColPositions : function(maxX, minY, box)
33243     {
33244         var pos = [];
33245         
33246         if(box[0].size == 'md-left'){
33247             pos.push({
33248                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
33249                 y : minY
33250             });
33251             
33252             return pos;
33253         }
33254         
33255         if(box[0].size == 'md-right'){
33256             pos.push({
33257                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
33258                 y : minY + (this.unitWidth + this.gutter) * 1
33259             });
33260             
33261             return pos;
33262         }
33263         
33264         var rand = Math.floor(Math.random() * (4 - box[0].y));
33265         
33266         pos.push({
33267             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33268             y : minY + (this.unitWidth + this.gutter) * rand
33269         });
33270         
33271         return pos;
33272         
33273     },
33274     
33275     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
33276     {
33277         var pos = [];
33278         
33279         if(box[0].size == 'xs'){
33280             
33281             pos.push({
33282                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33283                 y : minY
33284             });
33285
33286             pos.push({
33287                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33288                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
33289             });
33290             
33291             return pos;
33292             
33293         }
33294         
33295         pos.push({
33296             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33297             y : minY
33298         });
33299
33300         pos.push({
33301             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33302             y : minY + (this.unitWidth + this.gutter) * 2
33303         });
33304         
33305         return pos;
33306         
33307     },
33308     
33309     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
33310     {
33311         var pos = [];
33312         
33313         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
33314             
33315             pos.push({
33316                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33317                 y : minY
33318             });
33319
33320             pos.push({
33321                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33322                 y : minY + (this.unitWidth + this.gutter) * 1
33323             });
33324             
33325             pos.push({
33326                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33327                 y : minY + (this.unitWidth + this.gutter) * 2
33328             });
33329             
33330             return pos;
33331             
33332         }
33333         
33334         if(box[0].size == 'xs' && box[1].size == 'xs'){
33335             
33336             pos.push({
33337                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33338                 y : minY
33339             });
33340
33341             pos.push({
33342                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33343                 y : minY
33344             });
33345             
33346             pos.push({
33347                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33348                 y : minY + (this.unitWidth + this.gutter) * 1
33349             });
33350             
33351             return pos;
33352             
33353         }
33354         
33355         pos.push({
33356             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33357             y : minY
33358         });
33359
33360         pos.push({
33361             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33362             y : minY + (this.unitWidth + this.gutter) * 2
33363         });
33364
33365         pos.push({
33366             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33367             y : minY + (this.unitWidth + this.gutter) * 2
33368         });
33369             
33370         return pos;
33371         
33372     },
33373     
33374     getHorizontalFourBoxColPositions : function(maxX, minY, box)
33375     {
33376         var pos = [];
33377         
33378         if(box[0].size == 'xs'){
33379             
33380             pos.push({
33381                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33382                 y : minY
33383             });
33384
33385             pos.push({
33386                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33387                 y : minY
33388             });
33389             
33390             pos.push({
33391                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33392                 y : minY
33393             });
33394             
33395             pos.push({
33396                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
33397                 y : minY + (this.unitWidth + this.gutter) * 1
33398             });
33399             
33400             return pos;
33401             
33402         }
33403         
33404         pos.push({
33405             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33406             y : minY
33407         });
33408         
33409         pos.push({
33410             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33411             y : minY + (this.unitWidth + this.gutter) * 2
33412         });
33413         
33414         pos.push({
33415             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33416             y : minY + (this.unitWidth + this.gutter) * 2
33417         });
33418         
33419         pos.push({
33420             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1) - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
33421             y : minY + (this.unitWidth + this.gutter) * 2
33422         });
33423
33424         return pos;
33425         
33426     },
33427     
33428     /**
33429     * remove a Masonry Brick
33430     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
33431     */
33432     removeBrick : function(brick_id)
33433     {
33434         if (!brick_id) {
33435             return;
33436         }
33437         
33438         for (var i = 0; i<this.bricks.length; i++) {
33439             if (this.bricks[i].id == brick_id) {
33440                 this.bricks.splice(i,1);
33441                 this.el.dom.removeChild(Roo.get(brick_id).dom);
33442                 this.initial();
33443             }
33444         }
33445     },
33446     
33447     /**
33448     * adds a Masonry Brick
33449     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33450     */
33451     addBrick : function(cfg)
33452     {
33453         var cn = new Roo.bootstrap.MasonryBrick(cfg);
33454         //this.register(cn);
33455         cn.parentId = this.id;
33456         cn.render(this.el);
33457         return cn;
33458     },
33459     
33460     /**
33461     * register a Masonry Brick
33462     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33463     */
33464     
33465     register : function(brick)
33466     {
33467         this.bricks.push(brick);
33468         brick.masonryId = this.id;
33469     },
33470     
33471     /**
33472     * clear all the Masonry Brick
33473     */
33474     clearAll : function()
33475     {
33476         this.bricks = [];
33477         //this.getChildContainer().dom.innerHTML = "";
33478         this.el.dom.innerHTML = '';
33479     },
33480     
33481     getSelected : function()
33482     {
33483         if (!this.selectedBrick) {
33484             return false;
33485         }
33486         
33487         return this.selectedBrick;
33488     }
33489 });
33490
33491 Roo.apply(Roo.bootstrap.LayoutMasonry, {
33492     
33493     groups: {},
33494      /**
33495     * register a Masonry Layout
33496     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
33497     */
33498     
33499     register : function(layout)
33500     {
33501         this.groups[layout.id] = layout;
33502     },
33503     /**
33504     * fetch a  Masonry Layout based on the masonry layout ID
33505     * @param {string} the masonry layout to add
33506     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
33507     */
33508     
33509     get: function(layout_id) {
33510         if (typeof(this.groups[layout_id]) == 'undefined') {
33511             return false;
33512         }
33513         return this.groups[layout_id] ;
33514     }
33515     
33516     
33517     
33518 });
33519
33520  
33521
33522  /**
33523  *
33524  * This is based on 
33525  * http://masonry.desandro.com
33526  *
33527  * The idea is to render all the bricks based on vertical width...
33528  *
33529  * The original code extends 'outlayer' - we might need to use that....
33530  * 
33531  */
33532
33533
33534 /**
33535  * @class Roo.bootstrap.LayoutMasonryAuto
33536  * @extends Roo.bootstrap.Component
33537  * Bootstrap Layout Masonry class
33538  * 
33539  * @constructor
33540  * Create a new Element
33541  * @param {Object} config The config object
33542  */
33543
33544 Roo.bootstrap.LayoutMasonryAuto = function(config){
33545     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
33546 };
33547
33548 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
33549     
33550       /**
33551      * @cfg {Boolean} isFitWidth  - resize the width..
33552      */   
33553     isFitWidth : false,  // options..
33554     /**
33555      * @cfg {Boolean} isOriginLeft = left align?
33556      */   
33557     isOriginLeft : true,
33558     /**
33559      * @cfg {Boolean} isOriginTop = top align?
33560      */   
33561     isOriginTop : false,
33562     /**
33563      * @cfg {Boolean} isLayoutInstant = no animation?
33564      */   
33565     isLayoutInstant : false, // needed?
33566     /**
33567      * @cfg {Boolean} isResizingContainer = not sure if this is used..
33568      */   
33569     isResizingContainer : true,
33570     /**
33571      * @cfg {Number} columnWidth  width of the columns 
33572      */   
33573     
33574     columnWidth : 0,
33575     
33576     /**
33577      * @cfg {Number} maxCols maximum number of columns
33578      */   
33579     
33580     maxCols: 0,
33581     /**
33582      * @cfg {Number} padHeight padding below box..
33583      */   
33584     
33585     padHeight : 10, 
33586     
33587     /**
33588      * @cfg {Boolean} isAutoInitial defalut true
33589      */   
33590     
33591     isAutoInitial : true, 
33592     
33593     // private?
33594     gutter : 0,
33595     
33596     containerWidth: 0,
33597     initialColumnWidth : 0,
33598     currentSize : null,
33599     
33600     colYs : null, // array.
33601     maxY : 0,
33602     padWidth: 10,
33603     
33604     
33605     tag: 'div',
33606     cls: '',
33607     bricks: null, //CompositeElement
33608     cols : 0, // array?
33609     // element : null, // wrapped now this.el
33610     _isLayoutInited : null, 
33611     
33612     
33613     getAutoCreate : function(){
33614         
33615         var cfg = {
33616             tag: this.tag,
33617             cls: 'blog-masonary-wrapper ' + this.cls,
33618             cn : {
33619                 cls : 'mas-boxes masonary'
33620             }
33621         };
33622         
33623         return cfg;
33624     },
33625     
33626     getChildContainer: function( )
33627     {
33628         if (this.boxesEl) {
33629             return this.boxesEl;
33630         }
33631         
33632         this.boxesEl = this.el.select('.mas-boxes').first();
33633         
33634         return this.boxesEl;
33635     },
33636     
33637     
33638     initEvents : function()
33639     {
33640         var _this = this;
33641         
33642         if(this.isAutoInitial){
33643             Roo.log('hook children rendered');
33644             this.on('childrenrendered', function() {
33645                 Roo.log('children rendered');
33646                 _this.initial();
33647             } ,this);
33648         }
33649         
33650     },
33651     
33652     initial : function()
33653     {
33654         this.reloadItems();
33655
33656         this.currentSize = this.el.getBox(true);
33657
33658         /// was window resize... - let's see if this works..
33659         Roo.EventManager.onWindowResize(this.resize, this); 
33660
33661         if(!this.isAutoInitial){
33662             this.layout();
33663             return;
33664         }
33665         
33666         this.layout.defer(500,this);
33667     },
33668     
33669     reloadItems: function()
33670     {
33671         this.bricks = this.el.select('.masonry-brick', true);
33672         
33673         this.bricks.each(function(b) {
33674             //Roo.log(b.getSize());
33675             if (!b.attr('originalwidth')) {
33676                 b.attr('originalwidth',  b.getSize().width);
33677             }
33678             
33679         });
33680         
33681         Roo.log(this.bricks.elements.length);
33682     },
33683     
33684     resize : function()
33685     {
33686         Roo.log('resize');
33687         var cs = this.el.getBox(true);
33688         
33689         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
33690             Roo.log("no change in with or X");
33691             return;
33692         }
33693         this.currentSize = cs;
33694         this.layout();
33695     },
33696     
33697     layout : function()
33698     {
33699          Roo.log('layout');
33700         this._resetLayout();
33701         //this._manageStamps();
33702       
33703         // don't animate first layout
33704         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33705         this.layoutItems( isInstant );
33706       
33707         // flag for initalized
33708         this._isLayoutInited = true;
33709     },
33710     
33711     layoutItems : function( isInstant )
33712     {
33713         //var items = this._getItemsForLayout( this.items );
33714         // original code supports filtering layout items.. we just ignore it..
33715         
33716         this._layoutItems( this.bricks , isInstant );
33717       
33718         this._postLayout();
33719     },
33720     _layoutItems : function ( items , isInstant)
33721     {
33722        //this.fireEvent( 'layout', this, items );
33723     
33724
33725         if ( !items || !items.elements.length ) {
33726           // no items, emit event with empty array
33727             return;
33728         }
33729
33730         var queue = [];
33731         items.each(function(item) {
33732             Roo.log("layout item");
33733             Roo.log(item);
33734             // get x/y object from method
33735             var position = this._getItemLayoutPosition( item );
33736             // enqueue
33737             position.item = item;
33738             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
33739             queue.push( position );
33740         }, this);
33741       
33742         this._processLayoutQueue( queue );
33743     },
33744     /** Sets position of item in DOM
33745     * @param {Element} item
33746     * @param {Number} x - horizontal position
33747     * @param {Number} y - vertical position
33748     * @param {Boolean} isInstant - disables transitions
33749     */
33750     _processLayoutQueue : function( queue )
33751     {
33752         for ( var i=0, len = queue.length; i < len; i++ ) {
33753             var obj = queue[i];
33754             obj.item.position('absolute');
33755             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
33756         }
33757     },
33758       
33759     
33760     /**
33761     * Any logic you want to do after each layout,
33762     * i.e. size the container
33763     */
33764     _postLayout : function()
33765     {
33766         this.resizeContainer();
33767     },
33768     
33769     resizeContainer : function()
33770     {
33771         if ( !this.isResizingContainer ) {
33772             return;
33773         }
33774         var size = this._getContainerSize();
33775         if ( size ) {
33776             this.el.setSize(size.width,size.height);
33777             this.boxesEl.setSize(size.width,size.height);
33778         }
33779     },
33780     
33781     
33782     
33783     _resetLayout : function()
33784     {
33785         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
33786         this.colWidth = this.el.getWidth();
33787         //this.gutter = this.el.getWidth(); 
33788         
33789         this.measureColumns();
33790
33791         // reset column Y
33792         var i = this.cols;
33793         this.colYs = [];
33794         while (i--) {
33795             this.colYs.push( 0 );
33796         }
33797     
33798         this.maxY = 0;
33799     },
33800
33801     measureColumns : function()
33802     {
33803         this.getContainerWidth();
33804       // if columnWidth is 0, default to outerWidth of first item
33805         if ( !this.columnWidth ) {
33806             var firstItem = this.bricks.first();
33807             Roo.log(firstItem);
33808             this.columnWidth  = this.containerWidth;
33809             if (firstItem && firstItem.attr('originalwidth') ) {
33810                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
33811             }
33812             // columnWidth fall back to item of first element
33813             Roo.log("set column width?");
33814                         this.initialColumnWidth = this.columnWidth  ;
33815
33816             // if first elem has no width, default to size of container
33817             
33818         }
33819         
33820         
33821         if (this.initialColumnWidth) {
33822             this.columnWidth = this.initialColumnWidth;
33823         }
33824         
33825         
33826             
33827         // column width is fixed at the top - however if container width get's smaller we should
33828         // reduce it...
33829         
33830         // this bit calcs how man columns..
33831             
33832         var columnWidth = this.columnWidth += this.gutter;
33833       
33834         // calculate columns
33835         var containerWidth = this.containerWidth + this.gutter;
33836         
33837         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
33838         // fix rounding errors, typically with gutters
33839         var excess = columnWidth - containerWidth % columnWidth;
33840         
33841         
33842         // if overshoot is less than a pixel, round up, otherwise floor it
33843         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
33844         cols = Math[ mathMethod ]( cols );
33845         this.cols = Math.max( cols, 1 );
33846         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33847         
33848          // padding positioning..
33849         var totalColWidth = this.cols * this.columnWidth;
33850         var padavail = this.containerWidth - totalColWidth;
33851         // so for 2 columns - we need 3 'pads'
33852         
33853         var padNeeded = (1+this.cols) * this.padWidth;
33854         
33855         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
33856         
33857         this.columnWidth += padExtra
33858         //this.padWidth = Math.floor(padavail /  ( this.cols));
33859         
33860         // adjust colum width so that padding is fixed??
33861         
33862         // we have 3 columns ... total = width * 3
33863         // we have X left over... that should be used by 
33864         
33865         //if (this.expandC) {
33866             
33867         //}
33868         
33869         
33870         
33871     },
33872     
33873     getContainerWidth : function()
33874     {
33875        /* // container is parent if fit width
33876         var container = this.isFitWidth ? this.element.parentNode : this.element;
33877         // check that this.size and size are there
33878         // IE8 triggers resize on body size change, so they might not be
33879         
33880         var size = getSize( container );  //FIXME
33881         this.containerWidth = size && size.innerWidth; //FIXME
33882         */
33883          
33884         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33885         
33886     },
33887     
33888     _getItemLayoutPosition : function( item )  // what is item?
33889     {
33890         // we resize the item to our columnWidth..
33891       
33892         item.setWidth(this.columnWidth);
33893         item.autoBoxAdjust  = false;
33894         
33895         var sz = item.getSize();
33896  
33897         // how many columns does this brick span
33898         var remainder = this.containerWidth % this.columnWidth;
33899         
33900         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
33901         // round if off by 1 pixel, otherwise use ceil
33902         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
33903         colSpan = Math.min( colSpan, this.cols );
33904         
33905         // normally this should be '1' as we dont' currently allow multi width columns..
33906         
33907         var colGroup = this._getColGroup( colSpan );
33908         // get the minimum Y value from the columns
33909         var minimumY = Math.min.apply( Math, colGroup );
33910         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
33911         
33912         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
33913          
33914         // position the brick
33915         var position = {
33916             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
33917             y: this.currentSize.y + minimumY + this.padHeight
33918         };
33919         
33920         Roo.log(position);
33921         // apply setHeight to necessary columns
33922         var setHeight = minimumY + sz.height + this.padHeight;
33923         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
33924         
33925         var setSpan = this.cols + 1 - colGroup.length;
33926         for ( var i = 0; i < setSpan; i++ ) {
33927           this.colYs[ shortColIndex + i ] = setHeight ;
33928         }
33929       
33930         return position;
33931     },
33932     
33933     /**
33934      * @param {Number} colSpan - number of columns the element spans
33935      * @returns {Array} colGroup
33936      */
33937     _getColGroup : function( colSpan )
33938     {
33939         if ( colSpan < 2 ) {
33940           // if brick spans only one column, use all the column Ys
33941           return this.colYs;
33942         }
33943       
33944         var colGroup = [];
33945         // how many different places could this brick fit horizontally
33946         var groupCount = this.cols + 1 - colSpan;
33947         // for each group potential horizontal position
33948         for ( var i = 0; i < groupCount; i++ ) {
33949           // make an array of colY values for that one group
33950           var groupColYs = this.colYs.slice( i, i + colSpan );
33951           // and get the max value of the array
33952           colGroup[i] = Math.max.apply( Math, groupColYs );
33953         }
33954         return colGroup;
33955     },
33956     /*
33957     _manageStamp : function( stamp )
33958     {
33959         var stampSize =  stamp.getSize();
33960         var offset = stamp.getBox();
33961         // get the columns that this stamp affects
33962         var firstX = this.isOriginLeft ? offset.x : offset.right;
33963         var lastX = firstX + stampSize.width;
33964         var firstCol = Math.floor( firstX / this.columnWidth );
33965         firstCol = Math.max( 0, firstCol );
33966         
33967         var lastCol = Math.floor( lastX / this.columnWidth );
33968         // lastCol should not go over if multiple of columnWidth #425
33969         lastCol -= lastX % this.columnWidth ? 0 : 1;
33970         lastCol = Math.min( this.cols - 1, lastCol );
33971         
33972         // set colYs to bottom of the stamp
33973         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
33974             stampSize.height;
33975             
33976         for ( var i = firstCol; i <= lastCol; i++ ) {
33977           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
33978         }
33979     },
33980     */
33981     
33982     _getContainerSize : function()
33983     {
33984         this.maxY = Math.max.apply( Math, this.colYs );
33985         var size = {
33986             height: this.maxY
33987         };
33988       
33989         if ( this.isFitWidth ) {
33990             size.width = this._getContainerFitWidth();
33991         }
33992       
33993         return size;
33994     },
33995     
33996     _getContainerFitWidth : function()
33997     {
33998         var unusedCols = 0;
33999         // count unused columns
34000         var i = this.cols;
34001         while ( --i ) {
34002           if ( this.colYs[i] !== 0 ) {
34003             break;
34004           }
34005           unusedCols++;
34006         }
34007         // fit container to columns that have been used
34008         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
34009     },
34010     
34011     needsResizeLayout : function()
34012     {
34013         var previousWidth = this.containerWidth;
34014         this.getContainerWidth();
34015         return previousWidth !== this.containerWidth;
34016     }
34017  
34018 });
34019
34020  
34021
34022  /*
34023  * - LGPL
34024  *
34025  * element
34026  * 
34027  */
34028
34029 /**
34030  * @class Roo.bootstrap.MasonryBrick
34031  * @extends Roo.bootstrap.Component
34032  * Bootstrap MasonryBrick class
34033  * 
34034  * @constructor
34035  * Create a new MasonryBrick
34036  * @param {Object} config The config object
34037  */
34038
34039 Roo.bootstrap.MasonryBrick = function(config){
34040     
34041     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
34042     
34043     Roo.bootstrap.MasonryBrick.register(this);
34044     
34045     this.addEvents({
34046         // raw events
34047         /**
34048          * @event click
34049          * When a MasonryBrick is clcik
34050          * @param {Roo.bootstrap.MasonryBrick} this
34051          * @param {Roo.EventObject} e
34052          */
34053         "click" : true
34054     });
34055 };
34056
34057 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
34058     
34059     /**
34060      * @cfg {String} title
34061      */   
34062     title : '',
34063     /**
34064      * @cfg {String} html
34065      */   
34066     html : '',
34067     /**
34068      * @cfg {String} bgimage
34069      */   
34070     bgimage : '',
34071     /**
34072      * @cfg {String} videourl
34073      */   
34074     videourl : '',
34075     /**
34076      * @cfg {String} cls
34077      */   
34078     cls : '',
34079     /**
34080      * @cfg {String} href
34081      */   
34082     href : '',
34083     /**
34084      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
34085      */   
34086     size : 'xs',
34087     
34088     /**
34089      * @cfg {String} placetitle (center|bottom)
34090      */   
34091     placetitle : '',
34092     
34093     /**
34094      * @cfg {Boolean} isFitContainer defalut true
34095      */   
34096     isFitContainer : true, 
34097     
34098     /**
34099      * @cfg {Boolean} preventDefault defalut false
34100      */   
34101     preventDefault : false, 
34102     
34103     /**
34104      * @cfg {Boolean} inverse defalut false
34105      */   
34106     maskInverse : false, 
34107     
34108     getAutoCreate : function()
34109     {
34110         if(!this.isFitContainer){
34111             return this.getSplitAutoCreate();
34112         }
34113         
34114         var cls = 'masonry-brick masonry-brick-full';
34115         
34116         if(this.href.length){
34117             cls += ' masonry-brick-link';
34118         }
34119         
34120         if(this.bgimage.length){
34121             cls += ' masonry-brick-image';
34122         }
34123         
34124         if(this.maskInverse){
34125             cls += ' mask-inverse';
34126         }
34127         
34128         if(!this.html.length && !this.maskInverse && !this.videourl.length){
34129             cls += ' enable-mask';
34130         }
34131         
34132         if(this.size){
34133             cls += ' masonry-' + this.size + '-brick';
34134         }
34135         
34136         if(this.placetitle.length){
34137             
34138             switch (this.placetitle) {
34139                 case 'center' :
34140                     cls += ' masonry-center-title';
34141                     break;
34142                 case 'bottom' :
34143                     cls += ' masonry-bottom-title';
34144                     break;
34145                 default:
34146                     break;
34147             }
34148             
34149         } else {
34150             if(!this.html.length && !this.bgimage.length){
34151                 cls += ' masonry-center-title';
34152             }
34153
34154             if(!this.html.length && this.bgimage.length){
34155                 cls += ' masonry-bottom-title';
34156             }
34157         }
34158         
34159         if(this.cls){
34160             cls += ' ' + this.cls;
34161         }
34162         
34163         var cfg = {
34164             tag: (this.href.length) ? 'a' : 'div',
34165             cls: cls,
34166             cn: [
34167                 {
34168                     tag: 'div',
34169                     cls: 'masonry-brick-mask'
34170                 },
34171                 {
34172                     tag: 'div',
34173                     cls: 'masonry-brick-paragraph',
34174                     cn: []
34175                 }
34176             ]
34177         };
34178         
34179         if(this.href.length){
34180             cfg.href = this.href;
34181         }
34182         
34183         var cn = cfg.cn[1].cn;
34184         
34185         if(this.title.length){
34186             cn.push({
34187                 tag: 'h4',
34188                 cls: 'masonry-brick-title',
34189                 html: this.title
34190             });
34191         }
34192         
34193         if(this.html.length){
34194             cn.push({
34195                 tag: 'p',
34196                 cls: 'masonry-brick-text',
34197                 html: this.html
34198             });
34199         }
34200         
34201         if (!this.title.length && !this.html.length) {
34202             cfg.cn[1].cls += ' hide';
34203         }
34204         
34205         if(this.bgimage.length){
34206             cfg.cn.push({
34207                 tag: 'img',
34208                 cls: 'masonry-brick-image-view',
34209                 src: this.bgimage
34210             });
34211         }
34212         
34213         if(this.videourl.length){
34214             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
34215             // youtube support only?
34216             cfg.cn.push({
34217                 tag: 'iframe',
34218                 cls: 'masonry-brick-image-view',
34219                 src: vurl,
34220                 frameborder : 0,
34221                 allowfullscreen : true
34222             });
34223         }
34224         
34225         return cfg;
34226         
34227     },
34228     
34229     getSplitAutoCreate : function()
34230     {
34231         var cls = 'masonry-brick masonry-brick-split';
34232         
34233         if(this.href.length){
34234             cls += ' masonry-brick-link';
34235         }
34236         
34237         if(this.bgimage.length){
34238             cls += ' masonry-brick-image';
34239         }
34240         
34241         if(this.size){
34242             cls += ' masonry-' + this.size + '-brick';
34243         }
34244         
34245         switch (this.placetitle) {
34246             case 'center' :
34247                 cls += ' masonry-center-title';
34248                 break;
34249             case 'bottom' :
34250                 cls += ' masonry-bottom-title';
34251                 break;
34252             default:
34253                 if(!this.bgimage.length){
34254                     cls += ' masonry-center-title';
34255                 }
34256
34257                 if(this.bgimage.length){
34258                     cls += ' masonry-bottom-title';
34259                 }
34260                 break;
34261         }
34262         
34263         if(this.cls){
34264             cls += ' ' + this.cls;
34265         }
34266         
34267         var cfg = {
34268             tag: (this.href.length) ? 'a' : 'div',
34269             cls: cls,
34270             cn: [
34271                 {
34272                     tag: 'div',
34273                     cls: 'masonry-brick-split-head',
34274                     cn: [
34275                         {
34276                             tag: 'div',
34277                             cls: 'masonry-brick-paragraph',
34278                             cn: []
34279                         }
34280                     ]
34281                 },
34282                 {
34283                     tag: 'div',
34284                     cls: 'masonry-brick-split-body',
34285                     cn: []
34286                 }
34287             ]
34288         };
34289         
34290         if(this.href.length){
34291             cfg.href = this.href;
34292         }
34293         
34294         if(this.title.length){
34295             cfg.cn[0].cn[0].cn.push({
34296                 tag: 'h4',
34297                 cls: 'masonry-brick-title',
34298                 html: this.title
34299             });
34300         }
34301         
34302         if(this.html.length){
34303             cfg.cn[1].cn.push({
34304                 tag: 'p',
34305                 cls: 'masonry-brick-text',
34306                 html: this.html
34307             });
34308         }
34309
34310         if(this.bgimage.length){
34311             cfg.cn[0].cn.push({
34312                 tag: 'img',
34313                 cls: 'masonry-brick-image-view',
34314                 src: this.bgimage
34315             });
34316         }
34317         
34318         if(this.videourl.length){
34319             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
34320             // youtube support only?
34321             cfg.cn[0].cn.cn.push({
34322                 tag: 'iframe',
34323                 cls: 'masonry-brick-image-view',
34324                 src: vurl,
34325                 frameborder : 0,
34326                 allowfullscreen : true
34327             });
34328         }
34329         
34330         return cfg;
34331     },
34332     
34333     initEvents: function() 
34334     {
34335         switch (this.size) {
34336             case 'xs' :
34337                 this.x = 1;
34338                 this.y = 1;
34339                 break;
34340             case 'sm' :
34341                 this.x = 2;
34342                 this.y = 2;
34343                 break;
34344             case 'md' :
34345             case 'md-left' :
34346             case 'md-right' :
34347                 this.x = 3;
34348                 this.y = 3;
34349                 break;
34350             case 'tall' :
34351                 this.x = 2;
34352                 this.y = 3;
34353                 break;
34354             case 'wide' :
34355                 this.x = 3;
34356                 this.y = 2;
34357                 break;
34358             case 'wide-thin' :
34359                 this.x = 3;
34360                 this.y = 1;
34361                 break;
34362                         
34363             default :
34364                 break;
34365         }
34366         
34367         if(Roo.isTouch){
34368             this.el.on('touchstart', this.onTouchStart, this);
34369             this.el.on('touchmove', this.onTouchMove, this);
34370             this.el.on('touchend', this.onTouchEnd, this);
34371             this.el.on('contextmenu', this.onContextMenu, this);
34372         } else {
34373             this.el.on('mouseenter'  ,this.enter, this);
34374             this.el.on('mouseleave', this.leave, this);
34375             this.el.on('click', this.onClick, this);
34376         }
34377         
34378         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
34379             this.parent().bricks.push(this);   
34380         }
34381         
34382     },
34383     
34384     onClick: function(e, el)
34385     {
34386         var time = this.endTimer - this.startTimer;
34387         // Roo.log(e.preventDefault());
34388         if(Roo.isTouch){
34389             if(time > 1000){
34390                 e.preventDefault();
34391                 return;
34392             }
34393         }
34394         
34395         if(!this.preventDefault){
34396             return;
34397         }
34398         
34399         e.preventDefault();
34400         
34401         if (this.activeClass != '') {
34402             this.selectBrick();
34403         }
34404         
34405         this.fireEvent('click', this, e);
34406     },
34407     
34408     enter: function(e, el)
34409     {
34410         e.preventDefault();
34411         
34412         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
34413             return;
34414         }
34415         
34416         if(this.bgimage.length && this.html.length){
34417             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
34418         }
34419     },
34420     
34421     leave: function(e, el)
34422     {
34423         e.preventDefault();
34424         
34425         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
34426             return;
34427         }
34428         
34429         if(this.bgimage.length && this.html.length){
34430             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
34431         }
34432     },
34433     
34434     onTouchStart: function(e, el)
34435     {
34436 //        e.preventDefault();
34437         
34438         this.touchmoved = false;
34439         
34440         if(!this.isFitContainer){
34441             return;
34442         }
34443         
34444         if(!this.bgimage.length || !this.html.length){
34445             return;
34446         }
34447         
34448         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
34449         
34450         this.timer = new Date().getTime();
34451         
34452     },
34453     
34454     onTouchMove: function(e, el)
34455     {
34456         this.touchmoved = true;
34457     },
34458     
34459     onContextMenu : function(e,el)
34460     {
34461         e.preventDefault();
34462         e.stopPropagation();
34463         return false;
34464     },
34465     
34466     onTouchEnd: function(e, el)
34467     {
34468 //        e.preventDefault();
34469         
34470         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
34471         
34472             this.leave(e,el);
34473             
34474             return;
34475         }
34476         
34477         if(!this.bgimage.length || !this.html.length){
34478             
34479             if(this.href.length){
34480                 window.location.href = this.href;
34481             }
34482             
34483             return;
34484         }
34485         
34486         if(!this.isFitContainer){
34487             return;
34488         }
34489         
34490         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
34491         
34492         window.location.href = this.href;
34493     },
34494     
34495     //selection on single brick only
34496     selectBrick : function() {
34497         
34498         if (!this.parentId) {
34499             return;
34500         }
34501         
34502         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
34503         var index = m.selectedBrick.indexOf(this.id);
34504         
34505         if ( index > -1) {
34506             m.selectedBrick.splice(index,1);
34507             this.el.removeClass(this.activeClass);
34508             return;
34509         }
34510         
34511         for(var i = 0; i < m.selectedBrick.length; i++) {
34512             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
34513             b.el.removeClass(b.activeClass);
34514         }
34515         
34516         m.selectedBrick = [];
34517         
34518         m.selectedBrick.push(this.id);
34519         this.el.addClass(this.activeClass);
34520         return;
34521     },
34522     
34523     isSelected : function(){
34524         return this.el.hasClass(this.activeClass);
34525         
34526     }
34527 });
34528
34529 Roo.apply(Roo.bootstrap.MasonryBrick, {
34530     
34531     //groups: {},
34532     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
34533      /**
34534     * register a Masonry Brick
34535     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34536     */
34537     
34538     register : function(brick)
34539     {
34540         //this.groups[brick.id] = brick;
34541         this.groups.add(brick.id, brick);
34542     },
34543     /**
34544     * fetch a  masonry brick based on the masonry brick ID
34545     * @param {string} the masonry brick to add
34546     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
34547     */
34548     
34549     get: function(brick_id) 
34550     {
34551         // if (typeof(this.groups[brick_id]) == 'undefined') {
34552         //     return false;
34553         // }
34554         // return this.groups[brick_id] ;
34555         
34556         if(this.groups.key(brick_id)) {
34557             return this.groups.key(brick_id);
34558         }
34559         
34560         return false;
34561     }
34562     
34563     
34564     
34565 });
34566
34567  /*
34568  * - LGPL
34569  *
34570  * element
34571  * 
34572  */
34573
34574 /**
34575  * @class Roo.bootstrap.Brick
34576  * @extends Roo.bootstrap.Component
34577  * Bootstrap Brick class
34578  * 
34579  * @constructor
34580  * Create a new Brick
34581  * @param {Object} config The config object
34582  */
34583
34584 Roo.bootstrap.Brick = function(config){
34585     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
34586     
34587     this.addEvents({
34588         // raw events
34589         /**
34590          * @event click
34591          * When a Brick is click
34592          * @param {Roo.bootstrap.Brick} this
34593          * @param {Roo.EventObject} e
34594          */
34595         "click" : true
34596     });
34597 };
34598
34599 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
34600     
34601     /**
34602      * @cfg {String} title
34603      */   
34604     title : '',
34605     /**
34606      * @cfg {String} html
34607      */   
34608     html : '',
34609     /**
34610      * @cfg {String} bgimage
34611      */   
34612     bgimage : '',
34613     /**
34614      * @cfg {String} cls
34615      */   
34616     cls : '',
34617     /**
34618      * @cfg {String} href
34619      */   
34620     href : '',
34621     /**
34622      * @cfg {String} video
34623      */   
34624     video : '',
34625     /**
34626      * @cfg {Boolean} square
34627      */   
34628     square : true,
34629     
34630     getAutoCreate : function()
34631     {
34632         var cls = 'roo-brick';
34633         
34634         if(this.href.length){
34635             cls += ' roo-brick-link';
34636         }
34637         
34638         if(this.bgimage.length){
34639             cls += ' roo-brick-image';
34640         }
34641         
34642         if(!this.html.length && !this.bgimage.length){
34643             cls += ' roo-brick-center-title';
34644         }
34645         
34646         if(!this.html.length && this.bgimage.length){
34647             cls += ' roo-brick-bottom-title';
34648         }
34649         
34650         if(this.cls){
34651             cls += ' ' + this.cls;
34652         }
34653         
34654         var cfg = {
34655             tag: (this.href.length) ? 'a' : 'div',
34656             cls: cls,
34657             cn: [
34658                 {
34659                     tag: 'div',
34660                     cls: 'roo-brick-paragraph',
34661                     cn: []
34662                 }
34663             ]
34664         };
34665         
34666         if(this.href.length){
34667             cfg.href = this.href;
34668         }
34669         
34670         var cn = cfg.cn[0].cn;
34671         
34672         if(this.title.length){
34673             cn.push({
34674                 tag: 'h4',
34675                 cls: 'roo-brick-title',
34676                 html: this.title
34677             });
34678         }
34679         
34680         if(this.html.length){
34681             cn.push({
34682                 tag: 'p',
34683                 cls: 'roo-brick-text',
34684                 html: this.html
34685             });
34686         } else {
34687             cn.cls += ' hide';
34688         }
34689         
34690         if(this.bgimage.length){
34691             cfg.cn.push({
34692                 tag: 'img',
34693                 cls: 'roo-brick-image-view',
34694                 src: this.bgimage
34695             });
34696         }
34697         
34698         return cfg;
34699     },
34700     
34701     initEvents: function() 
34702     {
34703         if(this.title.length || this.html.length){
34704             this.el.on('mouseenter'  ,this.enter, this);
34705             this.el.on('mouseleave', this.leave, this);
34706         }
34707         
34708         Roo.EventManager.onWindowResize(this.resize, this); 
34709         
34710         if(this.bgimage.length){
34711             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
34712             this.imageEl.on('load', this.onImageLoad, this);
34713             return;
34714         }
34715         
34716         this.resize();
34717     },
34718     
34719     onImageLoad : function()
34720     {
34721         this.resize();
34722     },
34723     
34724     resize : function()
34725     {
34726         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
34727         
34728         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
34729         
34730         if(this.bgimage.length){
34731             var image = this.el.select('.roo-brick-image-view', true).first();
34732             
34733             image.setWidth(paragraph.getWidth());
34734             
34735             if(this.square){
34736                 image.setHeight(paragraph.getWidth());
34737             }
34738             
34739             this.el.setHeight(image.getHeight());
34740             paragraph.setHeight(image.getHeight());
34741             
34742         }
34743         
34744     },
34745     
34746     enter: function(e, el)
34747     {
34748         e.preventDefault();
34749         
34750         if(this.bgimage.length){
34751             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
34752             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
34753         }
34754     },
34755     
34756     leave: function(e, el)
34757     {
34758         e.preventDefault();
34759         
34760         if(this.bgimage.length){
34761             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
34762             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
34763         }
34764     }
34765     
34766 });
34767
34768  
34769
34770  /*
34771  * - LGPL
34772  *
34773  * Number field 
34774  */
34775
34776 /**
34777  * @class Roo.bootstrap.NumberField
34778  * @extends Roo.bootstrap.Input
34779  * Bootstrap NumberField class
34780  * 
34781  * 
34782  * 
34783  * 
34784  * @constructor
34785  * Create a new NumberField
34786  * @param {Object} config The config object
34787  */
34788
34789 Roo.bootstrap.NumberField = function(config){
34790     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
34791 };
34792
34793 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
34794     
34795     /**
34796      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
34797      */
34798     allowDecimals : true,
34799     /**
34800      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
34801      */
34802     decimalSeparator : ".",
34803     /**
34804      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
34805      */
34806     decimalPrecision : 2,
34807     /**
34808      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
34809      */
34810     allowNegative : true,
34811     
34812     /**
34813      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
34814      */
34815     allowZero: true,
34816     /**
34817      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
34818      */
34819     minValue : Number.NEGATIVE_INFINITY,
34820     /**
34821      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
34822      */
34823     maxValue : Number.MAX_VALUE,
34824     /**
34825      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
34826      */
34827     minText : "The minimum value for this field is {0}",
34828     /**
34829      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
34830      */
34831     maxText : "The maximum value for this field is {0}",
34832     /**
34833      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
34834      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
34835      */
34836     nanText : "{0} is not a valid number",
34837     /**
34838      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
34839      */
34840     thousandsDelimiter : false,
34841     /**
34842      * @cfg {String} valueAlign alignment of value
34843      */
34844     valueAlign : "left",
34845
34846     getAutoCreate : function()
34847     {
34848         var hiddenInput = {
34849             tag: 'input',
34850             type: 'hidden',
34851             id: Roo.id(),
34852             cls: 'hidden-number-input'
34853         };
34854         
34855         if (this.name) {
34856             hiddenInput.name = this.name;
34857         }
34858         
34859         this.name = '';
34860         
34861         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
34862         
34863         this.name = hiddenInput.name;
34864         
34865         if(cfg.cn.length > 0) {
34866             cfg.cn.push(hiddenInput);
34867         }
34868         
34869         return cfg;
34870     },
34871
34872     // private
34873     initEvents : function()
34874     {   
34875         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
34876         
34877         var allowed = "0123456789";
34878         
34879         if(this.allowDecimals){
34880             allowed += this.decimalSeparator;
34881         }
34882         
34883         if(this.allowNegative){
34884             allowed += "-";
34885         }
34886         
34887         if(this.thousandsDelimiter) {
34888             allowed += ",";
34889         }
34890         
34891         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
34892         
34893         var keyPress = function(e){
34894             
34895             var k = e.getKey();
34896             
34897             var c = e.getCharCode();
34898             
34899             if(
34900                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
34901                     allowed.indexOf(String.fromCharCode(c)) === -1
34902             ){
34903                 e.stopEvent();
34904                 return;
34905             }
34906             
34907             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
34908                 return;
34909             }
34910             
34911             if(allowed.indexOf(String.fromCharCode(c)) === -1){
34912                 e.stopEvent();
34913             }
34914         };
34915         
34916         this.el.on("keypress", keyPress, this);
34917     },
34918     
34919     validateValue : function(value)
34920     {
34921         
34922         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
34923             return false;
34924         }
34925         
34926         var num = this.parseValue(value);
34927         
34928         if(isNaN(num)){
34929             this.markInvalid(String.format(this.nanText, value));
34930             return false;
34931         }
34932         
34933         if(num < this.minValue){
34934             this.markInvalid(String.format(this.minText, this.minValue));
34935             return false;
34936         }
34937         
34938         if(num > this.maxValue){
34939             this.markInvalid(String.format(this.maxText, this.maxValue));
34940             return false;
34941         }
34942         
34943         return true;
34944     },
34945
34946     getValue : function()
34947     {
34948         var v = this.hiddenEl().getValue();
34949         
34950         return this.fixPrecision(this.parseValue(v));
34951     },
34952
34953     parseValue : function(value)
34954     {
34955         if(this.thousandsDelimiter) {
34956             value += "";
34957             r = new RegExp(",", "g");
34958             value = value.replace(r, "");
34959         }
34960         
34961         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
34962         return isNaN(value) ? '' : value;
34963     },
34964
34965     fixPrecision : function(value)
34966     {
34967         if(this.thousandsDelimiter) {
34968             value += "";
34969             r = new RegExp(",", "g");
34970             value = value.replace(r, "");
34971         }
34972         
34973         var nan = isNaN(value);
34974         
34975         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
34976             return nan ? '' : value;
34977         }
34978         return parseFloat(value).toFixed(this.decimalPrecision);
34979     },
34980
34981     setValue : function(v)
34982     {
34983         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
34984         
34985         this.value = v;
34986         
34987         if(this.rendered){
34988             
34989             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
34990             
34991             this.inputEl().dom.value = (v == '') ? '' :
34992                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
34993             
34994             if(!this.allowZero && v === '0') {
34995                 this.hiddenEl().dom.value = '';
34996                 this.inputEl().dom.value = '';
34997             }
34998             
34999             this.validate();
35000         }
35001     },
35002
35003     decimalPrecisionFcn : function(v)
35004     {
35005         return Math.floor(v);
35006     },
35007
35008     beforeBlur : function()
35009     {
35010         var v = this.parseValue(this.getRawValue());
35011         
35012         if(v || v === 0 || v === ''){
35013             this.setValue(v);
35014         }
35015     },
35016     
35017     hiddenEl : function()
35018     {
35019         return this.el.select('input.hidden-number-input',true).first();
35020     }
35021     
35022 });
35023
35024  
35025
35026 /*
35027 * Licence: LGPL
35028 */
35029
35030 /**
35031  * @class Roo.bootstrap.DocumentSlider
35032  * @extends Roo.bootstrap.Component
35033  * Bootstrap DocumentSlider class
35034  * 
35035  * @constructor
35036  * Create a new DocumentViewer
35037  * @param {Object} config The config object
35038  */
35039
35040 Roo.bootstrap.DocumentSlider = function(config){
35041     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
35042     
35043     this.files = [];
35044     
35045     this.addEvents({
35046         /**
35047          * @event initial
35048          * Fire after initEvent
35049          * @param {Roo.bootstrap.DocumentSlider} this
35050          */
35051         "initial" : true,
35052         /**
35053          * @event update
35054          * Fire after update
35055          * @param {Roo.bootstrap.DocumentSlider} this
35056          */
35057         "update" : true,
35058         /**
35059          * @event click
35060          * Fire after click
35061          * @param {Roo.bootstrap.DocumentSlider} this
35062          */
35063         "click" : true
35064     });
35065 };
35066
35067 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
35068     
35069     files : false,
35070     
35071     indicator : 0,
35072     
35073     getAutoCreate : function()
35074     {
35075         var cfg = {
35076             tag : 'div',
35077             cls : 'roo-document-slider',
35078             cn : [
35079                 {
35080                     tag : 'div',
35081                     cls : 'roo-document-slider-header',
35082                     cn : [
35083                         {
35084                             tag : 'div',
35085                             cls : 'roo-document-slider-header-title'
35086                         }
35087                     ]
35088                 },
35089                 {
35090                     tag : 'div',
35091                     cls : 'roo-document-slider-body',
35092                     cn : [
35093                         {
35094                             tag : 'div',
35095                             cls : 'roo-document-slider-prev',
35096                             cn : [
35097                                 {
35098                                     tag : 'i',
35099                                     cls : 'fa fa-chevron-left'
35100                                 }
35101                             ]
35102                         },
35103                         {
35104                             tag : 'div',
35105                             cls : 'roo-document-slider-thumb',
35106                             cn : [
35107                                 {
35108                                     tag : 'img',
35109                                     cls : 'roo-document-slider-image'
35110                                 }
35111                             ]
35112                         },
35113                         {
35114                             tag : 'div',
35115                             cls : 'roo-document-slider-next',
35116                             cn : [
35117                                 {
35118                                     tag : 'i',
35119                                     cls : 'fa fa-chevron-right'
35120                                 }
35121                             ]
35122                         }
35123                     ]
35124                 }
35125             ]
35126         };
35127         
35128         return cfg;
35129     },
35130     
35131     initEvents : function()
35132     {
35133         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
35134         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
35135         
35136         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
35137         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
35138         
35139         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
35140         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
35141         
35142         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
35143         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
35144         
35145         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
35146         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
35147         
35148         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
35149         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
35150         
35151         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
35152         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
35153         
35154         this.thumbEl.on('click', this.onClick, this);
35155         
35156         this.prevIndicator.on('click', this.prev, this);
35157         
35158         this.nextIndicator.on('click', this.next, this);
35159         
35160     },
35161     
35162     initial : function()
35163     {
35164         if(this.files.length){
35165             this.indicator = 1;
35166             this.update()
35167         }
35168         
35169         this.fireEvent('initial', this);
35170     },
35171     
35172     update : function()
35173     {
35174         this.imageEl.attr('src', this.files[this.indicator - 1]);
35175         
35176         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
35177         
35178         this.prevIndicator.show();
35179         
35180         if(this.indicator == 1){
35181             this.prevIndicator.hide();
35182         }
35183         
35184         this.nextIndicator.show();
35185         
35186         if(this.indicator == this.files.length){
35187             this.nextIndicator.hide();
35188         }
35189         
35190         this.thumbEl.scrollTo('top');
35191         
35192         this.fireEvent('update', this);
35193     },
35194     
35195     onClick : function(e)
35196     {
35197         e.preventDefault();
35198         
35199         this.fireEvent('click', this);
35200     },
35201     
35202     prev : function(e)
35203     {
35204         e.preventDefault();
35205         
35206         this.indicator = Math.max(1, this.indicator - 1);
35207         
35208         this.update();
35209     },
35210     
35211     next : function(e)
35212     {
35213         e.preventDefault();
35214         
35215         this.indicator = Math.min(this.files.length, this.indicator + 1);
35216         
35217         this.update();
35218     }
35219 });
35220 /*
35221  * - LGPL
35222  *
35223  * RadioSet
35224  *
35225  *
35226  */
35227
35228 /**
35229  * @class Roo.bootstrap.RadioSet
35230  * @extends Roo.bootstrap.Input
35231  * Bootstrap RadioSet class
35232  * @cfg {String} indicatorpos (left|right) default left
35233  * @cfg {Boolean} inline (true|false) inline the element (default true)
35234  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
35235  * @constructor
35236  * Create a new RadioSet
35237  * @param {Object} config The config object
35238  */
35239
35240 Roo.bootstrap.RadioSet = function(config){
35241     
35242     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
35243     
35244     this.radioes = [];
35245     
35246     Roo.bootstrap.RadioSet.register(this);
35247     
35248     this.addEvents({
35249         /**
35250         * @event check
35251         * Fires when the element is checked or unchecked.
35252         * @param {Roo.bootstrap.RadioSet} this This radio
35253         * @param {Roo.bootstrap.Radio} item The checked item
35254         */
35255        check : true,
35256        /**
35257         * @event click
35258         * Fires when the element is click.
35259         * @param {Roo.bootstrap.RadioSet} this This radio set
35260         * @param {Roo.bootstrap.Radio} item The checked item
35261         * @param {Roo.EventObject} e The event object
35262         */
35263        click : true
35264     });
35265     
35266 };
35267
35268 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
35269
35270     radioes : false,
35271     
35272     inline : true,
35273     
35274     weight : '',
35275     
35276     indicatorpos : 'left',
35277     
35278     getAutoCreate : function()
35279     {
35280         var label = {
35281             tag : 'label',
35282             cls : 'roo-radio-set-label',
35283             cn : [
35284                 {
35285                     tag : 'span',
35286                     html : this.fieldLabel
35287                 }
35288             ]
35289         };
35290         if (Roo.bootstrap.version == 3) {
35291             
35292             
35293             if(this.indicatorpos == 'left'){
35294                 label.cn.unshift({
35295                     tag : 'i',
35296                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
35297                     tooltip : 'This field is required'
35298                 });
35299             } else {
35300                 label.cn.push({
35301                     tag : 'i',
35302                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
35303                     tooltip : 'This field is required'
35304                 });
35305             }
35306         }
35307         var items = {
35308             tag : 'div',
35309             cls : 'roo-radio-set-items'
35310         };
35311         
35312         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
35313         
35314         if (align === 'left' && this.fieldLabel.length) {
35315             
35316             items = {
35317                 cls : "roo-radio-set-right", 
35318                 cn: [
35319                     items
35320                 ]
35321             };
35322             
35323             if(this.labelWidth > 12){
35324                 label.style = "width: " + this.labelWidth + 'px';
35325             }
35326             
35327             if(this.labelWidth < 13 && this.labelmd == 0){
35328                 this.labelmd = this.labelWidth;
35329             }
35330             
35331             if(this.labellg > 0){
35332                 label.cls += ' col-lg-' + this.labellg;
35333                 items.cls += ' col-lg-' + (12 - this.labellg);
35334             }
35335             
35336             if(this.labelmd > 0){
35337                 label.cls += ' col-md-' + this.labelmd;
35338                 items.cls += ' col-md-' + (12 - this.labelmd);
35339             }
35340             
35341             if(this.labelsm > 0){
35342                 label.cls += ' col-sm-' + this.labelsm;
35343                 items.cls += ' col-sm-' + (12 - this.labelsm);
35344             }
35345             
35346             if(this.labelxs > 0){
35347                 label.cls += ' col-xs-' + this.labelxs;
35348                 items.cls += ' col-xs-' + (12 - this.labelxs);
35349             }
35350         }
35351         
35352         var cfg = {
35353             tag : 'div',
35354             cls : 'roo-radio-set',
35355             cn : [
35356                 {
35357                     tag : 'input',
35358                     cls : 'roo-radio-set-input',
35359                     type : 'hidden',
35360                     name : this.name,
35361                     value : this.value ? this.value :  ''
35362                 },
35363                 label,
35364                 items
35365             ]
35366         };
35367         
35368         if(this.weight.length){
35369             cfg.cls += ' roo-radio-' + this.weight;
35370         }
35371         
35372         if(this.inline) {
35373             cfg.cls += ' roo-radio-set-inline';
35374         }
35375         
35376         var settings=this;
35377         ['xs','sm','md','lg'].map(function(size){
35378             if (settings[size]) {
35379                 cfg.cls += ' col-' + size + '-' + settings[size];
35380             }
35381         });
35382         
35383         return cfg;
35384         
35385     },
35386
35387     initEvents : function()
35388     {
35389         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
35390         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
35391         
35392         if(!this.fieldLabel.length){
35393             this.labelEl.hide();
35394         }
35395         
35396         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
35397         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
35398         
35399         this.indicator = this.indicatorEl();
35400         
35401         if(this.indicator){
35402             this.indicator.addClass('invisible');
35403         }
35404         
35405         this.originalValue = this.getValue();
35406         
35407     },
35408     
35409     inputEl: function ()
35410     {
35411         return this.el.select('.roo-radio-set-input', true).first();
35412     },
35413     
35414     getChildContainer : function()
35415     {
35416         return this.itemsEl;
35417     },
35418     
35419     register : function(item)
35420     {
35421         this.radioes.push(item);
35422         
35423     },
35424     
35425     validate : function()
35426     {   
35427         if(this.getVisibilityEl().hasClass('hidden')){
35428             return true;
35429         }
35430         
35431         var valid = false;
35432         
35433         Roo.each(this.radioes, function(i){
35434             if(!i.checked){
35435                 return;
35436             }
35437             
35438             valid = true;
35439             return false;
35440         });
35441         
35442         if(this.allowBlank) {
35443             return true;
35444         }
35445         
35446         if(this.disabled || valid){
35447             this.markValid();
35448             return true;
35449         }
35450         
35451         this.markInvalid();
35452         return false;
35453         
35454     },
35455     
35456     markValid : function()
35457     {
35458         if(this.labelEl.isVisible(true) && this.indicatorEl()){
35459             this.indicatorEl().removeClass('visible');
35460             this.indicatorEl().addClass('invisible');
35461         }
35462         
35463         
35464         if (Roo.bootstrap.version == 3) {
35465             this.el.removeClass([this.invalidClass, this.validClass]);
35466             this.el.addClass(this.validClass);
35467         } else {
35468             this.el.removeClass(['is-invalid','is-valid']);
35469             this.el.addClass(['is-valid']);
35470         }
35471         this.fireEvent('valid', this);
35472     },
35473     
35474     markInvalid : function(msg)
35475     {
35476         if(this.allowBlank || this.disabled){
35477             return;
35478         }
35479         
35480         if(this.labelEl.isVisible(true) && this.indicatorEl()){
35481             this.indicatorEl().removeClass('invisible');
35482             this.indicatorEl().addClass('visible');
35483         }
35484         if (Roo.bootstrap.version == 3) {
35485             this.el.removeClass([this.invalidClass, this.validClass]);
35486             this.el.addClass(this.invalidClass);
35487         } else {
35488             this.el.removeClass(['is-invalid','is-valid']);
35489             this.el.addClass(['is-invalid']);
35490         }
35491         
35492         this.fireEvent('invalid', this, msg);
35493         
35494     },
35495     
35496     setValue : function(v, suppressEvent)
35497     {   
35498         if(this.value === v){
35499             return;
35500         }
35501         
35502         this.value = v;
35503         
35504         if(this.rendered){
35505             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
35506         }
35507         
35508         Roo.each(this.radioes, function(i){
35509             i.checked = false;
35510             i.el.removeClass('checked');
35511         });
35512         
35513         Roo.each(this.radioes, function(i){
35514             
35515             if(i.value === v || i.value.toString() === v.toString()){
35516                 i.checked = true;
35517                 i.el.addClass('checked');
35518                 
35519                 if(suppressEvent !== true){
35520                     this.fireEvent('check', this, i);
35521                 }
35522                 
35523                 return false;
35524             }
35525             
35526         }, this);
35527         
35528         this.validate();
35529     },
35530     
35531     clearInvalid : function(){
35532         
35533         if(!this.el || this.preventMark){
35534             return;
35535         }
35536         
35537         this.el.removeClass([this.invalidClass]);
35538         
35539         this.fireEvent('valid', this);
35540     }
35541     
35542 });
35543
35544 Roo.apply(Roo.bootstrap.RadioSet, {
35545     
35546     groups: {},
35547     
35548     register : function(set)
35549     {
35550         this.groups[set.name] = set;
35551     },
35552     
35553     get: function(name) 
35554     {
35555         if (typeof(this.groups[name]) == 'undefined') {
35556             return false;
35557         }
35558         
35559         return this.groups[name] ;
35560     }
35561     
35562 });
35563 /*
35564  * Based on:
35565  * Ext JS Library 1.1.1
35566  * Copyright(c) 2006-2007, Ext JS, LLC.
35567  *
35568  * Originally Released Under LGPL - original licence link has changed is not relivant.
35569  *
35570  * Fork - LGPL
35571  * <script type="text/javascript">
35572  */
35573
35574
35575 /**
35576  * @class Roo.bootstrap.SplitBar
35577  * @extends Roo.util.Observable
35578  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
35579  * <br><br>
35580  * Usage:
35581  * <pre><code>
35582 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
35583                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
35584 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
35585 split.minSize = 100;
35586 split.maxSize = 600;
35587 split.animate = true;
35588 split.on('moved', splitterMoved);
35589 </code></pre>
35590  * @constructor
35591  * Create a new SplitBar
35592  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
35593  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
35594  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
35595  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
35596                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
35597                         position of the SplitBar).
35598  */
35599 Roo.bootstrap.SplitBar = function(cfg){
35600     
35601     /** @private */
35602     
35603     //{
35604     //  dragElement : elm
35605     //  resizingElement: el,
35606         // optional..
35607     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
35608     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
35609         // existingProxy ???
35610     //}
35611     
35612     this.el = Roo.get(cfg.dragElement, true);
35613     this.el.dom.unselectable = "on";
35614     /** @private */
35615     this.resizingEl = Roo.get(cfg.resizingElement, true);
35616
35617     /**
35618      * @private
35619      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
35620      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
35621      * @type Number
35622      */
35623     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
35624     
35625     /**
35626      * The minimum size of the resizing element. (Defaults to 0)
35627      * @type Number
35628      */
35629     this.minSize = 0;
35630     
35631     /**
35632      * The maximum size of the resizing element. (Defaults to 2000)
35633      * @type Number
35634      */
35635     this.maxSize = 2000;
35636     
35637     /**
35638      * Whether to animate the transition to the new size
35639      * @type Boolean
35640      */
35641     this.animate = false;
35642     
35643     /**
35644      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
35645      * @type Boolean
35646      */
35647     this.useShim = false;
35648     
35649     /** @private */
35650     this.shim = null;
35651     
35652     if(!cfg.existingProxy){
35653         /** @private */
35654         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
35655     }else{
35656         this.proxy = Roo.get(cfg.existingProxy).dom;
35657     }
35658     /** @private */
35659     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
35660     
35661     /** @private */
35662     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
35663     
35664     /** @private */
35665     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
35666     
35667     /** @private */
35668     this.dragSpecs = {};
35669     
35670     /**
35671      * @private The adapter to use to positon and resize elements
35672      */
35673     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
35674     this.adapter.init(this);
35675     
35676     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35677         /** @private */
35678         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
35679         this.el.addClass("roo-splitbar-h");
35680     }else{
35681         /** @private */
35682         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
35683         this.el.addClass("roo-splitbar-v");
35684     }
35685     
35686     this.addEvents({
35687         /**
35688          * @event resize
35689          * Fires when the splitter is moved (alias for {@link #event-moved})
35690          * @param {Roo.bootstrap.SplitBar} this
35691          * @param {Number} newSize the new width or height
35692          */
35693         "resize" : true,
35694         /**
35695          * @event moved
35696          * Fires when the splitter is moved
35697          * @param {Roo.bootstrap.SplitBar} this
35698          * @param {Number} newSize the new width or height
35699          */
35700         "moved" : true,
35701         /**
35702          * @event beforeresize
35703          * Fires before the splitter is dragged
35704          * @param {Roo.bootstrap.SplitBar} this
35705          */
35706         "beforeresize" : true,
35707
35708         "beforeapply" : true
35709     });
35710
35711     Roo.util.Observable.call(this);
35712 };
35713
35714 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
35715     onStartProxyDrag : function(x, y){
35716         this.fireEvent("beforeresize", this);
35717         if(!this.overlay){
35718             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
35719             o.unselectable();
35720             o.enableDisplayMode("block");
35721             // all splitbars share the same overlay
35722             Roo.bootstrap.SplitBar.prototype.overlay = o;
35723         }
35724         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
35725         this.overlay.show();
35726         Roo.get(this.proxy).setDisplayed("block");
35727         var size = this.adapter.getElementSize(this);
35728         this.activeMinSize = this.getMinimumSize();;
35729         this.activeMaxSize = this.getMaximumSize();;
35730         var c1 = size - this.activeMinSize;
35731         var c2 = Math.max(this.activeMaxSize - size, 0);
35732         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35733             this.dd.resetConstraints();
35734             this.dd.setXConstraint(
35735                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
35736                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
35737             );
35738             this.dd.setYConstraint(0, 0);
35739         }else{
35740             this.dd.resetConstraints();
35741             this.dd.setXConstraint(0, 0);
35742             this.dd.setYConstraint(
35743                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
35744                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
35745             );
35746          }
35747         this.dragSpecs.startSize = size;
35748         this.dragSpecs.startPoint = [x, y];
35749         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
35750     },
35751     
35752     /** 
35753      * @private Called after the drag operation by the DDProxy
35754      */
35755     onEndProxyDrag : function(e){
35756         Roo.get(this.proxy).setDisplayed(false);
35757         var endPoint = Roo.lib.Event.getXY(e);
35758         if(this.overlay){
35759             this.overlay.hide();
35760         }
35761         var newSize;
35762         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35763             newSize = this.dragSpecs.startSize + 
35764                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
35765                     endPoint[0] - this.dragSpecs.startPoint[0] :
35766                     this.dragSpecs.startPoint[0] - endPoint[0]
35767                 );
35768         }else{
35769             newSize = this.dragSpecs.startSize + 
35770                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
35771                     endPoint[1] - this.dragSpecs.startPoint[1] :
35772                     this.dragSpecs.startPoint[1] - endPoint[1]
35773                 );
35774         }
35775         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
35776         if(newSize != this.dragSpecs.startSize){
35777             if(this.fireEvent('beforeapply', this, newSize) !== false){
35778                 this.adapter.setElementSize(this, newSize);
35779                 this.fireEvent("moved", this, newSize);
35780                 this.fireEvent("resize", this, newSize);
35781             }
35782         }
35783     },
35784     
35785     /**
35786      * Get the adapter this SplitBar uses
35787      * @return The adapter object
35788      */
35789     getAdapter : function(){
35790         return this.adapter;
35791     },
35792     
35793     /**
35794      * Set the adapter this SplitBar uses
35795      * @param {Object} adapter A SplitBar adapter object
35796      */
35797     setAdapter : function(adapter){
35798         this.adapter = adapter;
35799         this.adapter.init(this);
35800     },
35801     
35802     /**
35803      * Gets the minimum size for the resizing element
35804      * @return {Number} The minimum size
35805      */
35806     getMinimumSize : function(){
35807         return this.minSize;
35808     },
35809     
35810     /**
35811      * Sets the minimum size for the resizing element
35812      * @param {Number} minSize The minimum size
35813      */
35814     setMinimumSize : function(minSize){
35815         this.minSize = minSize;
35816     },
35817     
35818     /**
35819      * Gets the maximum size for the resizing element
35820      * @return {Number} The maximum size
35821      */
35822     getMaximumSize : function(){
35823         return this.maxSize;
35824     },
35825     
35826     /**
35827      * Sets the maximum size for the resizing element
35828      * @param {Number} maxSize The maximum size
35829      */
35830     setMaximumSize : function(maxSize){
35831         this.maxSize = maxSize;
35832     },
35833     
35834     /**
35835      * Sets the initialize size for the resizing element
35836      * @param {Number} size The initial size
35837      */
35838     setCurrentSize : function(size){
35839         var oldAnimate = this.animate;
35840         this.animate = false;
35841         this.adapter.setElementSize(this, size);
35842         this.animate = oldAnimate;
35843     },
35844     
35845     /**
35846      * Destroy this splitbar. 
35847      * @param {Boolean} removeEl True to remove the element
35848      */
35849     destroy : function(removeEl){
35850         if(this.shim){
35851             this.shim.remove();
35852         }
35853         this.dd.unreg();
35854         this.proxy.parentNode.removeChild(this.proxy);
35855         if(removeEl){
35856             this.el.remove();
35857         }
35858     }
35859 });
35860
35861 /**
35862  * @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.
35863  */
35864 Roo.bootstrap.SplitBar.createProxy = function(dir){
35865     var proxy = new Roo.Element(document.createElement("div"));
35866     proxy.unselectable();
35867     var cls = 'roo-splitbar-proxy';
35868     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
35869     document.body.appendChild(proxy.dom);
35870     return proxy.dom;
35871 };
35872
35873 /** 
35874  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
35875  * Default Adapter. It assumes the splitter and resizing element are not positioned
35876  * elements and only gets/sets the width of the element. Generally used for table based layouts.
35877  */
35878 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
35879 };
35880
35881 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
35882     // do nothing for now
35883     init : function(s){
35884     
35885     },
35886     /**
35887      * Called before drag operations to get the current size of the resizing element. 
35888      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
35889      */
35890      getElementSize : function(s){
35891         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35892             return s.resizingEl.getWidth();
35893         }else{
35894             return s.resizingEl.getHeight();
35895         }
35896     },
35897     
35898     /**
35899      * Called after drag operations to set the size of the resizing element.
35900      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
35901      * @param {Number} newSize The new size to set
35902      * @param {Function} onComplete A function to be invoked when resizing is complete
35903      */
35904     setElementSize : function(s, newSize, onComplete){
35905         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35906             if(!s.animate){
35907                 s.resizingEl.setWidth(newSize);
35908                 if(onComplete){
35909                     onComplete(s, newSize);
35910                 }
35911             }else{
35912                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
35913             }
35914         }else{
35915             
35916             if(!s.animate){
35917                 s.resizingEl.setHeight(newSize);
35918                 if(onComplete){
35919                     onComplete(s, newSize);
35920                 }
35921             }else{
35922                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
35923             }
35924         }
35925     }
35926 };
35927
35928 /** 
35929  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
35930  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
35931  * Adapter that  moves the splitter element to align with the resized sizing element. 
35932  * Used with an absolute positioned SplitBar.
35933  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
35934  * document.body, make sure you assign an id to the body element.
35935  */
35936 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
35937     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
35938     this.container = Roo.get(container);
35939 };
35940
35941 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
35942     init : function(s){
35943         this.basic.init(s);
35944     },
35945     
35946     getElementSize : function(s){
35947         return this.basic.getElementSize(s);
35948     },
35949     
35950     setElementSize : function(s, newSize, onComplete){
35951         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
35952     },
35953     
35954     moveSplitter : function(s){
35955         var yes = Roo.bootstrap.SplitBar;
35956         switch(s.placement){
35957             case yes.LEFT:
35958                 s.el.setX(s.resizingEl.getRight());
35959                 break;
35960             case yes.RIGHT:
35961                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
35962                 break;
35963             case yes.TOP:
35964                 s.el.setY(s.resizingEl.getBottom());
35965                 break;
35966             case yes.BOTTOM:
35967                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
35968                 break;
35969         }
35970     }
35971 };
35972
35973 /**
35974  * Orientation constant - Create a vertical SplitBar
35975  * @static
35976  * @type Number
35977  */
35978 Roo.bootstrap.SplitBar.VERTICAL = 1;
35979
35980 /**
35981  * Orientation constant - Create a horizontal SplitBar
35982  * @static
35983  * @type Number
35984  */
35985 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
35986
35987 /**
35988  * Placement constant - The resizing element is to the left of the splitter element
35989  * @static
35990  * @type Number
35991  */
35992 Roo.bootstrap.SplitBar.LEFT = 1;
35993
35994 /**
35995  * Placement constant - The resizing element is to the right of the splitter element
35996  * @static
35997  * @type Number
35998  */
35999 Roo.bootstrap.SplitBar.RIGHT = 2;
36000
36001 /**
36002  * Placement constant - The resizing element is positioned above the splitter element
36003  * @static
36004  * @type Number
36005  */
36006 Roo.bootstrap.SplitBar.TOP = 3;
36007
36008 /**
36009  * Placement constant - The resizing element is positioned under splitter element
36010  * @static
36011  * @type Number
36012  */
36013 Roo.bootstrap.SplitBar.BOTTOM = 4;
36014 Roo.namespace("Roo.bootstrap.layout");/*
36015  * Based on:
36016  * Ext JS Library 1.1.1
36017  * Copyright(c) 2006-2007, Ext JS, LLC.
36018  *
36019  * Originally Released Under LGPL - original licence link has changed is not relivant.
36020  *
36021  * Fork - LGPL
36022  * <script type="text/javascript">
36023  */
36024
36025 /**
36026  * @class Roo.bootstrap.layout.Manager
36027  * @extends Roo.bootstrap.Component
36028  * Base class for layout managers.
36029  */
36030 Roo.bootstrap.layout.Manager = function(config)
36031 {
36032     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
36033
36034
36035
36036
36037
36038     /** false to disable window resize monitoring @type Boolean */
36039     this.monitorWindowResize = true;
36040     this.regions = {};
36041     this.addEvents({
36042         /**
36043          * @event layout
36044          * Fires when a layout is performed.
36045          * @param {Roo.LayoutManager} this
36046          */
36047         "layout" : true,
36048         /**
36049          * @event regionresized
36050          * Fires when the user resizes a region.
36051          * @param {Roo.LayoutRegion} region The resized region
36052          * @param {Number} newSize The new size (width for east/west, height for north/south)
36053          */
36054         "regionresized" : true,
36055         /**
36056          * @event regioncollapsed
36057          * Fires when a region is collapsed.
36058          * @param {Roo.LayoutRegion} region The collapsed region
36059          */
36060         "regioncollapsed" : true,
36061         /**
36062          * @event regionexpanded
36063          * Fires when a region is expanded.
36064          * @param {Roo.LayoutRegion} region The expanded region
36065          */
36066         "regionexpanded" : true
36067     });
36068     this.updating = false;
36069
36070     if (config.el) {
36071         this.el = Roo.get(config.el);
36072         this.initEvents();
36073     }
36074
36075 };
36076
36077 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
36078
36079
36080     regions : null,
36081
36082     monitorWindowResize : true,
36083
36084
36085     updating : false,
36086
36087
36088     onRender : function(ct, position)
36089     {
36090         if(!this.el){
36091             this.el = Roo.get(ct);
36092             this.initEvents();
36093         }
36094         //this.fireEvent('render',this);
36095     },
36096
36097
36098     initEvents: function()
36099     {
36100
36101
36102         // ie scrollbar fix
36103         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
36104             document.body.scroll = "no";
36105         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
36106             this.el.position('relative');
36107         }
36108         this.id = this.el.id;
36109         this.el.addClass("roo-layout-container");
36110         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
36111         if(this.el.dom != document.body ) {
36112             this.el.on('resize', this.layout,this);
36113             this.el.on('show', this.layout,this);
36114         }
36115
36116     },
36117
36118     /**
36119      * Returns true if this layout is currently being updated
36120      * @return {Boolean}
36121      */
36122     isUpdating : function(){
36123         return this.updating;
36124     },
36125
36126     /**
36127      * Suspend the LayoutManager from doing auto-layouts while
36128      * making multiple add or remove calls
36129      */
36130     beginUpdate : function(){
36131         this.updating = true;
36132     },
36133
36134     /**
36135      * Restore auto-layouts and optionally disable the manager from performing a layout
36136      * @param {Boolean} noLayout true to disable a layout update
36137      */
36138     endUpdate : function(noLayout){
36139         this.updating = false;
36140         if(!noLayout){
36141             this.layout();
36142         }
36143     },
36144
36145     layout: function(){
36146         // abstract...
36147     },
36148
36149     onRegionResized : function(region, newSize){
36150         this.fireEvent("regionresized", region, newSize);
36151         this.layout();
36152     },
36153
36154     onRegionCollapsed : function(region){
36155         this.fireEvent("regioncollapsed", region);
36156     },
36157
36158     onRegionExpanded : function(region){
36159         this.fireEvent("regionexpanded", region);
36160     },
36161
36162     /**
36163      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
36164      * performs box-model adjustments.
36165      * @return {Object} The size as an object {width: (the width), height: (the height)}
36166      */
36167     getViewSize : function()
36168     {
36169         var size;
36170         if(this.el.dom != document.body){
36171             size = this.el.getSize();
36172         }else{
36173             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
36174         }
36175         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
36176         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
36177         return size;
36178     },
36179
36180     /**
36181      * Returns the Element this layout is bound to.
36182      * @return {Roo.Element}
36183      */
36184     getEl : function(){
36185         return this.el;
36186     },
36187
36188     /**
36189      * Returns the specified region.
36190      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
36191      * @return {Roo.LayoutRegion}
36192      */
36193     getRegion : function(target){
36194         return this.regions[target.toLowerCase()];
36195     },
36196
36197     onWindowResize : function(){
36198         if(this.monitorWindowResize){
36199             this.layout();
36200         }
36201     }
36202 });
36203 /*
36204  * Based on:
36205  * Ext JS Library 1.1.1
36206  * Copyright(c) 2006-2007, Ext JS, LLC.
36207  *
36208  * Originally Released Under LGPL - original licence link has changed is not relivant.
36209  *
36210  * Fork - LGPL
36211  * <script type="text/javascript">
36212  */
36213 /**
36214  * @class Roo.bootstrap.layout.Border
36215  * @extends Roo.bootstrap.layout.Manager
36216  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
36217  * please see: examples/bootstrap/nested.html<br><br>
36218  
36219 <b>The container the layout is rendered into can be either the body element or any other element.
36220 If it is not the body element, the container needs to either be an absolute positioned element,
36221 or you will need to add "position:relative" to the css of the container.  You will also need to specify
36222 the container size if it is not the body element.</b>
36223
36224 * @constructor
36225 * Create a new Border
36226 * @param {Object} config Configuration options
36227  */
36228 Roo.bootstrap.layout.Border = function(config){
36229     config = config || {};
36230     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
36231     
36232     
36233     
36234     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
36235         if(config[region]){
36236             config[region].region = region;
36237             this.addRegion(config[region]);
36238         }
36239     },this);
36240     
36241 };
36242
36243 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
36244
36245 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
36246     
36247     parent : false, // this might point to a 'nest' or a ???
36248     
36249     /**
36250      * Creates and adds a new region if it doesn't already exist.
36251      * @param {String} target The target region key (north, south, east, west or center).
36252      * @param {Object} config The regions config object
36253      * @return {BorderLayoutRegion} The new region
36254      */
36255     addRegion : function(config)
36256     {
36257         if(!this.regions[config.region]){
36258             var r = this.factory(config);
36259             this.bindRegion(r);
36260         }
36261         return this.regions[config.region];
36262     },
36263
36264     // private (kinda)
36265     bindRegion : function(r){
36266         this.regions[r.config.region] = r;
36267         
36268         r.on("visibilitychange",    this.layout, this);
36269         r.on("paneladded",          this.layout, this);
36270         r.on("panelremoved",        this.layout, this);
36271         r.on("invalidated",         this.layout, this);
36272         r.on("resized",             this.onRegionResized, this);
36273         r.on("collapsed",           this.onRegionCollapsed, this);
36274         r.on("expanded",            this.onRegionExpanded, this);
36275     },
36276
36277     /**
36278      * Performs a layout update.
36279      */
36280     layout : function()
36281     {
36282         if(this.updating) {
36283             return;
36284         }
36285         
36286         // render all the rebions if they have not been done alreayd?
36287         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
36288             if(this.regions[region] && !this.regions[region].bodyEl){
36289                 this.regions[region].onRender(this.el)
36290             }
36291         },this);
36292         
36293         var size = this.getViewSize();
36294         var w = size.width;
36295         var h = size.height;
36296         var centerW = w;
36297         var centerH = h;
36298         var centerY = 0;
36299         var centerX = 0;
36300         //var x = 0, y = 0;
36301
36302         var rs = this.regions;
36303         var north = rs["north"];
36304         var south = rs["south"]; 
36305         var west = rs["west"];
36306         var east = rs["east"];
36307         var center = rs["center"];
36308         //if(this.hideOnLayout){ // not supported anymore
36309             //c.el.setStyle("display", "none");
36310         //}
36311         if(north && north.isVisible()){
36312             var b = north.getBox();
36313             var m = north.getMargins();
36314             b.width = w - (m.left+m.right);
36315             b.x = m.left;
36316             b.y = m.top;
36317             centerY = b.height + b.y + m.bottom;
36318             centerH -= centerY;
36319             north.updateBox(this.safeBox(b));
36320         }
36321         if(south && south.isVisible()){
36322             var b = south.getBox();
36323             var m = south.getMargins();
36324             b.width = w - (m.left+m.right);
36325             b.x = m.left;
36326             var totalHeight = (b.height + m.top + m.bottom);
36327             b.y = h - totalHeight + m.top;
36328             centerH -= totalHeight;
36329             south.updateBox(this.safeBox(b));
36330         }
36331         if(west && west.isVisible()){
36332             var b = west.getBox();
36333             var m = west.getMargins();
36334             b.height = centerH - (m.top+m.bottom);
36335             b.x = m.left;
36336             b.y = centerY + m.top;
36337             var totalWidth = (b.width + m.left + m.right);
36338             centerX += totalWidth;
36339             centerW -= totalWidth;
36340             west.updateBox(this.safeBox(b));
36341         }
36342         if(east && east.isVisible()){
36343             var b = east.getBox();
36344             var m = east.getMargins();
36345             b.height = centerH - (m.top+m.bottom);
36346             var totalWidth = (b.width + m.left + m.right);
36347             b.x = w - totalWidth + m.left;
36348             b.y = centerY + m.top;
36349             centerW -= totalWidth;
36350             east.updateBox(this.safeBox(b));
36351         }
36352         if(center){
36353             var m = center.getMargins();
36354             var centerBox = {
36355                 x: centerX + m.left,
36356                 y: centerY + m.top,
36357                 width: centerW - (m.left+m.right),
36358                 height: centerH - (m.top+m.bottom)
36359             };
36360             //if(this.hideOnLayout){
36361                 //center.el.setStyle("display", "block");
36362             //}
36363             center.updateBox(this.safeBox(centerBox));
36364         }
36365         this.el.repaint();
36366         this.fireEvent("layout", this);
36367     },
36368
36369     // private
36370     safeBox : function(box){
36371         box.width = Math.max(0, box.width);
36372         box.height = Math.max(0, box.height);
36373         return box;
36374     },
36375
36376     /**
36377      * Adds a ContentPanel (or subclass) to this layout.
36378      * @param {String} target The target region key (north, south, east, west or center).
36379      * @param {Roo.ContentPanel} panel The panel to add
36380      * @return {Roo.ContentPanel} The added panel
36381      */
36382     add : function(target, panel){
36383          
36384         target = target.toLowerCase();
36385         return this.regions[target].add(panel);
36386     },
36387
36388     /**
36389      * Remove a ContentPanel (or subclass) to this layout.
36390      * @param {String} target The target region key (north, south, east, west or center).
36391      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
36392      * @return {Roo.ContentPanel} The removed panel
36393      */
36394     remove : function(target, panel){
36395         target = target.toLowerCase();
36396         return this.regions[target].remove(panel);
36397     },
36398
36399     /**
36400      * Searches all regions for a panel with the specified id
36401      * @param {String} panelId
36402      * @return {Roo.ContentPanel} The panel or null if it wasn't found
36403      */
36404     findPanel : function(panelId){
36405         var rs = this.regions;
36406         for(var target in rs){
36407             if(typeof rs[target] != "function"){
36408                 var p = rs[target].getPanel(panelId);
36409                 if(p){
36410                     return p;
36411                 }
36412             }
36413         }
36414         return null;
36415     },
36416
36417     /**
36418      * Searches all regions for a panel with the specified id and activates (shows) it.
36419      * @param {String/ContentPanel} panelId The panels id or the panel itself
36420      * @return {Roo.ContentPanel} The shown panel or null
36421      */
36422     showPanel : function(panelId) {
36423       var rs = this.regions;
36424       for(var target in rs){
36425          var r = rs[target];
36426          if(typeof r != "function"){
36427             if(r.hasPanel(panelId)){
36428                return r.showPanel(panelId);
36429             }
36430          }
36431       }
36432       return null;
36433    },
36434
36435    /**
36436      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
36437      * @param {Roo.state.Provider} provider (optional) An alternate state provider
36438      */
36439    /*
36440     restoreState : function(provider){
36441         if(!provider){
36442             provider = Roo.state.Manager;
36443         }
36444         var sm = new Roo.LayoutStateManager();
36445         sm.init(this, provider);
36446     },
36447 */
36448  
36449  
36450     /**
36451      * Adds a xtype elements to the layout.
36452      * <pre><code>
36453
36454 layout.addxtype({
36455        xtype : 'ContentPanel',
36456        region: 'west',
36457        items: [ .... ]
36458    }
36459 );
36460
36461 layout.addxtype({
36462         xtype : 'NestedLayoutPanel',
36463         region: 'west',
36464         layout: {
36465            center: { },
36466            west: { }   
36467         },
36468         items : [ ... list of content panels or nested layout panels.. ]
36469    }
36470 );
36471 </code></pre>
36472      * @param {Object} cfg Xtype definition of item to add.
36473      */
36474     addxtype : function(cfg)
36475     {
36476         // basically accepts a pannel...
36477         // can accept a layout region..!?!?
36478         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
36479         
36480         
36481         // theory?  children can only be panels??
36482         
36483         //if (!cfg.xtype.match(/Panel$/)) {
36484         //    return false;
36485         //}
36486         var ret = false;
36487         
36488         if (typeof(cfg.region) == 'undefined') {
36489             Roo.log("Failed to add Panel, region was not set");
36490             Roo.log(cfg);
36491             return false;
36492         }
36493         var region = cfg.region;
36494         delete cfg.region;
36495         
36496           
36497         var xitems = [];
36498         if (cfg.items) {
36499             xitems = cfg.items;
36500             delete cfg.items;
36501         }
36502         var nb = false;
36503         
36504         if ( region == 'center') {
36505             Roo.log("Center: " + cfg.title);
36506         }
36507         
36508         
36509         switch(cfg.xtype) 
36510         {
36511             case 'Content':  // ContentPanel (el, cfg)
36512             case 'Scroll':  // ContentPanel (el, cfg)
36513             case 'View': 
36514                 cfg.autoCreate = cfg.autoCreate || true;
36515                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
36516                 //} else {
36517                 //    var el = this.el.createChild();
36518                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
36519                 //}
36520                 
36521                 this.add(region, ret);
36522                 break;
36523             
36524             /*
36525             case 'TreePanel': // our new panel!
36526                 cfg.el = this.el.createChild();
36527                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
36528                 this.add(region, ret);
36529                 break;
36530             */
36531             
36532             case 'Nest': 
36533                 // create a new Layout (which is  a Border Layout...
36534                 
36535                 var clayout = cfg.layout;
36536                 clayout.el  = this.el.createChild();
36537                 clayout.items   = clayout.items  || [];
36538                 
36539                 delete cfg.layout;
36540                 
36541                 // replace this exitems with the clayout ones..
36542                 xitems = clayout.items;
36543                  
36544                 // force background off if it's in center...
36545                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
36546                     cfg.background = false;
36547                 }
36548                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
36549                 
36550                 
36551                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
36552                 //console.log('adding nested layout panel '  + cfg.toSource());
36553                 this.add(region, ret);
36554                 nb = {}; /// find first...
36555                 break;
36556             
36557             case 'Grid':
36558                 
36559                 // needs grid and region
36560                 
36561                 //var el = this.getRegion(region).el.createChild();
36562                 /*
36563                  *var el = this.el.createChild();
36564                 // create the grid first...
36565                 cfg.grid.container = el;
36566                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
36567                 */
36568                 
36569                 if (region == 'center' && this.active ) {
36570                     cfg.background = false;
36571                 }
36572                 
36573                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
36574                 
36575                 this.add(region, ret);
36576                 /*
36577                 if (cfg.background) {
36578                     // render grid on panel activation (if panel background)
36579                     ret.on('activate', function(gp) {
36580                         if (!gp.grid.rendered) {
36581                     //        gp.grid.render(el);
36582                         }
36583                     });
36584                 } else {
36585                   //  cfg.grid.render(el);
36586                 }
36587                 */
36588                 break;
36589            
36590            
36591             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
36592                 // it was the old xcomponent building that caused this before.
36593                 // espeically if border is the top element in the tree.
36594                 ret = this;
36595                 break; 
36596                 
36597                     
36598                 
36599                 
36600                 
36601             default:
36602                 /*
36603                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
36604                     
36605                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
36606                     this.add(region, ret);
36607                 } else {
36608                 */
36609                     Roo.log(cfg);
36610                     throw "Can not add '" + cfg.xtype + "' to Border";
36611                     return null;
36612              
36613                                 
36614              
36615         }
36616         this.beginUpdate();
36617         // add children..
36618         var region = '';
36619         var abn = {};
36620         Roo.each(xitems, function(i)  {
36621             region = nb && i.region ? i.region : false;
36622             
36623             var add = ret.addxtype(i);
36624            
36625             if (region) {
36626                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
36627                 if (!i.background) {
36628                     abn[region] = nb[region] ;
36629                 }
36630             }
36631             
36632         });
36633         this.endUpdate();
36634
36635         // make the last non-background panel active..
36636         //if (nb) { Roo.log(abn); }
36637         if (nb) {
36638             
36639             for(var r in abn) {
36640                 region = this.getRegion(r);
36641                 if (region) {
36642                     // tried using nb[r], but it does not work..
36643                      
36644                     region.showPanel(abn[r]);
36645                    
36646                 }
36647             }
36648         }
36649         return ret;
36650         
36651     },
36652     
36653     
36654 // private
36655     factory : function(cfg)
36656     {
36657         
36658         var validRegions = Roo.bootstrap.layout.Border.regions;
36659
36660         var target = cfg.region;
36661         cfg.mgr = this;
36662         
36663         var r = Roo.bootstrap.layout;
36664         Roo.log(target);
36665         switch(target){
36666             case "north":
36667                 return new r.North(cfg);
36668             case "south":
36669                 return new r.South(cfg);
36670             case "east":
36671                 return new r.East(cfg);
36672             case "west":
36673                 return new r.West(cfg);
36674             case "center":
36675                 return new r.Center(cfg);
36676         }
36677         throw 'Layout region "'+target+'" not supported.';
36678     }
36679     
36680     
36681 });
36682  /*
36683  * Based on:
36684  * Ext JS Library 1.1.1
36685  * Copyright(c) 2006-2007, Ext JS, LLC.
36686  *
36687  * Originally Released Under LGPL - original licence link has changed is not relivant.
36688  *
36689  * Fork - LGPL
36690  * <script type="text/javascript">
36691  */
36692  
36693 /**
36694  * @class Roo.bootstrap.layout.Basic
36695  * @extends Roo.util.Observable
36696  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
36697  * and does not have a titlebar, tabs or any other features. All it does is size and position 
36698  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
36699  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
36700  * @cfg {string}   region  the region that it inhabits..
36701  * @cfg {bool}   skipConfig skip config?
36702  * 
36703
36704  */
36705 Roo.bootstrap.layout.Basic = function(config){
36706     
36707     this.mgr = config.mgr;
36708     
36709     this.position = config.region;
36710     
36711     var skipConfig = config.skipConfig;
36712     
36713     this.events = {
36714         /**
36715          * @scope Roo.BasicLayoutRegion
36716          */
36717         
36718         /**
36719          * @event beforeremove
36720          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
36721          * @param {Roo.LayoutRegion} this
36722          * @param {Roo.ContentPanel} panel The panel
36723          * @param {Object} e The cancel event object
36724          */
36725         "beforeremove" : true,
36726         /**
36727          * @event invalidated
36728          * Fires when the layout for this region is changed.
36729          * @param {Roo.LayoutRegion} this
36730          */
36731         "invalidated" : true,
36732         /**
36733          * @event visibilitychange
36734          * Fires when this region is shown or hidden 
36735          * @param {Roo.LayoutRegion} this
36736          * @param {Boolean} visibility true or false
36737          */
36738         "visibilitychange" : true,
36739         /**
36740          * @event paneladded
36741          * Fires when a panel is added. 
36742          * @param {Roo.LayoutRegion} this
36743          * @param {Roo.ContentPanel} panel The panel
36744          */
36745         "paneladded" : true,
36746         /**
36747          * @event panelremoved
36748          * Fires when a panel is removed. 
36749          * @param {Roo.LayoutRegion} this
36750          * @param {Roo.ContentPanel} panel The panel
36751          */
36752         "panelremoved" : true,
36753         /**
36754          * @event beforecollapse
36755          * Fires when this region before collapse.
36756          * @param {Roo.LayoutRegion} this
36757          */
36758         "beforecollapse" : true,
36759         /**
36760          * @event collapsed
36761          * Fires when this region is collapsed.
36762          * @param {Roo.LayoutRegion} this
36763          */
36764         "collapsed" : true,
36765         /**
36766          * @event expanded
36767          * Fires when this region is expanded.
36768          * @param {Roo.LayoutRegion} this
36769          */
36770         "expanded" : true,
36771         /**
36772          * @event slideshow
36773          * Fires when this region is slid into view.
36774          * @param {Roo.LayoutRegion} this
36775          */
36776         "slideshow" : true,
36777         /**
36778          * @event slidehide
36779          * Fires when this region slides out of view. 
36780          * @param {Roo.LayoutRegion} this
36781          */
36782         "slidehide" : true,
36783         /**
36784          * @event panelactivated
36785          * Fires when a panel is activated. 
36786          * @param {Roo.LayoutRegion} this
36787          * @param {Roo.ContentPanel} panel The activated panel
36788          */
36789         "panelactivated" : true,
36790         /**
36791          * @event resized
36792          * Fires when the user resizes this region. 
36793          * @param {Roo.LayoutRegion} this
36794          * @param {Number} newSize The new size (width for east/west, height for north/south)
36795          */
36796         "resized" : true
36797     };
36798     /** A collection of panels in this region. @type Roo.util.MixedCollection */
36799     this.panels = new Roo.util.MixedCollection();
36800     this.panels.getKey = this.getPanelId.createDelegate(this);
36801     this.box = null;
36802     this.activePanel = null;
36803     // ensure listeners are added...
36804     
36805     if (config.listeners || config.events) {
36806         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
36807             listeners : config.listeners || {},
36808             events : config.events || {}
36809         });
36810     }
36811     
36812     if(skipConfig !== true){
36813         this.applyConfig(config);
36814     }
36815 };
36816
36817 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
36818 {
36819     getPanelId : function(p){
36820         return p.getId();
36821     },
36822     
36823     applyConfig : function(config){
36824         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36825         this.config = config;
36826         
36827     },
36828     
36829     /**
36830      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
36831      * the width, for horizontal (north, south) the height.
36832      * @param {Number} newSize The new width or height
36833      */
36834     resizeTo : function(newSize){
36835         var el = this.el ? this.el :
36836                  (this.activePanel ? this.activePanel.getEl() : null);
36837         if(el){
36838             switch(this.position){
36839                 case "east":
36840                 case "west":
36841                     el.setWidth(newSize);
36842                     this.fireEvent("resized", this, newSize);
36843                 break;
36844                 case "north":
36845                 case "south":
36846                     el.setHeight(newSize);
36847                     this.fireEvent("resized", this, newSize);
36848                 break;                
36849             }
36850         }
36851     },
36852     
36853     getBox : function(){
36854         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
36855     },
36856     
36857     getMargins : function(){
36858         return this.margins;
36859     },
36860     
36861     updateBox : function(box){
36862         this.box = box;
36863         var el = this.activePanel.getEl();
36864         el.dom.style.left = box.x + "px";
36865         el.dom.style.top = box.y + "px";
36866         this.activePanel.setSize(box.width, box.height);
36867     },
36868     
36869     /**
36870      * Returns the container element for this region.
36871      * @return {Roo.Element}
36872      */
36873     getEl : function(){
36874         return this.activePanel;
36875     },
36876     
36877     /**
36878      * Returns true if this region is currently visible.
36879      * @return {Boolean}
36880      */
36881     isVisible : function(){
36882         return this.activePanel ? true : false;
36883     },
36884     
36885     setActivePanel : function(panel){
36886         panel = this.getPanel(panel);
36887         if(this.activePanel && this.activePanel != panel){
36888             this.activePanel.setActiveState(false);
36889             this.activePanel.getEl().setLeftTop(-10000,-10000);
36890         }
36891         this.activePanel = panel;
36892         panel.setActiveState(true);
36893         if(this.box){
36894             panel.setSize(this.box.width, this.box.height);
36895         }
36896         this.fireEvent("panelactivated", this, panel);
36897         this.fireEvent("invalidated");
36898     },
36899     
36900     /**
36901      * Show the specified panel.
36902      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
36903      * @return {Roo.ContentPanel} The shown panel or null
36904      */
36905     showPanel : function(panel){
36906         panel = this.getPanel(panel);
36907         if(panel){
36908             this.setActivePanel(panel);
36909         }
36910         return panel;
36911     },
36912     
36913     /**
36914      * Get the active panel for this region.
36915      * @return {Roo.ContentPanel} The active panel or null
36916      */
36917     getActivePanel : function(){
36918         return this.activePanel;
36919     },
36920     
36921     /**
36922      * Add the passed ContentPanel(s)
36923      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36924      * @return {Roo.ContentPanel} The panel added (if only one was added)
36925      */
36926     add : function(panel){
36927         if(arguments.length > 1){
36928             for(var i = 0, len = arguments.length; i < len; i++) {
36929                 this.add(arguments[i]);
36930             }
36931             return null;
36932         }
36933         if(this.hasPanel(panel)){
36934             this.showPanel(panel);
36935             return panel;
36936         }
36937         var el = panel.getEl();
36938         if(el.dom.parentNode != this.mgr.el.dom){
36939             this.mgr.el.dom.appendChild(el.dom);
36940         }
36941         if(panel.setRegion){
36942             panel.setRegion(this);
36943         }
36944         this.panels.add(panel);
36945         el.setStyle("position", "absolute");
36946         if(!panel.background){
36947             this.setActivePanel(panel);
36948             if(this.config.initialSize && this.panels.getCount()==1){
36949                 this.resizeTo(this.config.initialSize);
36950             }
36951         }
36952         this.fireEvent("paneladded", this, panel);
36953         return panel;
36954     },
36955     
36956     /**
36957      * Returns true if the panel is in this region.
36958      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36959      * @return {Boolean}
36960      */
36961     hasPanel : function(panel){
36962         if(typeof panel == "object"){ // must be panel obj
36963             panel = panel.getId();
36964         }
36965         return this.getPanel(panel) ? true : false;
36966     },
36967     
36968     /**
36969      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36970      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36971      * @param {Boolean} preservePanel Overrides the config preservePanel option
36972      * @return {Roo.ContentPanel} The panel that was removed
36973      */
36974     remove : function(panel, preservePanel){
36975         panel = this.getPanel(panel);
36976         if(!panel){
36977             return null;
36978         }
36979         var e = {};
36980         this.fireEvent("beforeremove", this, panel, e);
36981         if(e.cancel === true){
36982             return null;
36983         }
36984         var panelId = panel.getId();
36985         this.panels.removeKey(panelId);
36986         return panel;
36987     },
36988     
36989     /**
36990      * Returns the panel specified or null if it's not in this region.
36991      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36992      * @return {Roo.ContentPanel}
36993      */
36994     getPanel : function(id){
36995         if(typeof id == "object"){ // must be panel obj
36996             return id;
36997         }
36998         return this.panels.get(id);
36999     },
37000     
37001     /**
37002      * Returns this regions position (north/south/east/west/center).
37003      * @return {String} 
37004      */
37005     getPosition: function(){
37006         return this.position;    
37007     }
37008 });/*
37009  * Based on:
37010  * Ext JS Library 1.1.1
37011  * Copyright(c) 2006-2007, Ext JS, LLC.
37012  *
37013  * Originally Released Under LGPL - original licence link has changed is not relivant.
37014  *
37015  * Fork - LGPL
37016  * <script type="text/javascript">
37017  */
37018  
37019 /**
37020  * @class Roo.bootstrap.layout.Region
37021  * @extends Roo.bootstrap.layout.Basic
37022  * This class represents a region in a layout manager.
37023  
37024  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
37025  * @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})
37026  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
37027  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
37028  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
37029  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
37030  * @cfg {String}    title           The title for the region (overrides panel titles)
37031  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
37032  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
37033  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
37034  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
37035  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
37036  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
37037  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
37038  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
37039  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
37040  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
37041
37042  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
37043  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
37044  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
37045  * @cfg {Number}    width           For East/West panels
37046  * @cfg {Number}    height          For North/South panels
37047  * @cfg {Boolean}   split           To show the splitter
37048  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
37049  * 
37050  * @cfg {string}   cls             Extra CSS classes to add to region
37051  * 
37052  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37053  * @cfg {string}   region  the region that it inhabits..
37054  *
37055
37056  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
37057  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
37058
37059  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
37060  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
37061  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
37062  */
37063 Roo.bootstrap.layout.Region = function(config)
37064 {
37065     this.applyConfig(config);
37066
37067     var mgr = config.mgr;
37068     var pos = config.region;
37069     config.skipConfig = true;
37070     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
37071     
37072     if (mgr.el) {
37073         this.onRender(mgr.el);   
37074     }
37075      
37076     this.visible = true;
37077     this.collapsed = false;
37078     this.unrendered_panels = [];
37079 };
37080
37081 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
37082
37083     position: '', // set by wrapper (eg. north/south etc..)
37084     unrendered_panels : null,  // unrendered panels.
37085     
37086     tabPosition : false,
37087     
37088     mgr: false, // points to 'Border'
37089     
37090     
37091     createBody : function(){
37092         /** This region's body element 
37093         * @type Roo.Element */
37094         this.bodyEl = this.el.createChild({
37095                 tag: "div",
37096                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
37097         });
37098     },
37099
37100     onRender: function(ctr, pos)
37101     {
37102         var dh = Roo.DomHelper;
37103         /** This region's container element 
37104         * @type Roo.Element */
37105         this.el = dh.append(ctr.dom, {
37106                 tag: "div",
37107                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
37108             }, true);
37109         /** This region's title element 
37110         * @type Roo.Element */
37111     
37112         this.titleEl = dh.append(this.el.dom,  {
37113                 tag: "div",
37114                 unselectable: "on",
37115                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
37116                 children:[
37117                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
37118                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
37119                 ]
37120             }, true);
37121         
37122         this.titleEl.enableDisplayMode();
37123         /** This region's title text element 
37124         * @type HTMLElement */
37125         this.titleTextEl = this.titleEl.dom.firstChild;
37126         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
37127         /*
37128         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
37129         this.closeBtn.enableDisplayMode();
37130         this.closeBtn.on("click", this.closeClicked, this);
37131         this.closeBtn.hide();
37132     */
37133         this.createBody(this.config);
37134         if(this.config.hideWhenEmpty){
37135             this.hide();
37136             this.on("paneladded", this.validateVisibility, this);
37137             this.on("panelremoved", this.validateVisibility, this);
37138         }
37139         if(this.autoScroll){
37140             this.bodyEl.setStyle("overflow", "auto");
37141         }else{
37142             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
37143         }
37144         //if(c.titlebar !== false){
37145             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
37146                 this.titleEl.hide();
37147             }else{
37148                 this.titleEl.show();
37149                 if(this.config.title){
37150                     this.titleTextEl.innerHTML = this.config.title;
37151                 }
37152             }
37153         //}
37154         if(this.config.collapsed){
37155             this.collapse(true);
37156         }
37157         if(this.config.hidden){
37158             this.hide();
37159         }
37160         
37161         if (this.unrendered_panels && this.unrendered_panels.length) {
37162             for (var i =0;i< this.unrendered_panels.length; i++) {
37163                 this.add(this.unrendered_panels[i]);
37164             }
37165             this.unrendered_panels = null;
37166             
37167         }
37168         
37169     },
37170     
37171     applyConfig : function(c)
37172     {
37173         /*
37174          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
37175             var dh = Roo.DomHelper;
37176             if(c.titlebar !== false){
37177                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
37178                 this.collapseBtn.on("click", this.collapse, this);
37179                 this.collapseBtn.enableDisplayMode();
37180                 /*
37181                 if(c.showPin === true || this.showPin){
37182                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
37183                     this.stickBtn.enableDisplayMode();
37184                     this.stickBtn.on("click", this.expand, this);
37185                     this.stickBtn.hide();
37186                 }
37187                 
37188             }
37189             */
37190             /** This region's collapsed element
37191             * @type Roo.Element */
37192             /*
37193              *
37194             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
37195                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
37196             ]}, true);
37197             
37198             if(c.floatable !== false){
37199                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
37200                this.collapsedEl.on("click", this.collapseClick, this);
37201             }
37202
37203             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
37204                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
37205                    id: "message", unselectable: "on", style:{"float":"left"}});
37206                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
37207              }
37208             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
37209             this.expandBtn.on("click", this.expand, this);
37210             
37211         }
37212         
37213         if(this.collapseBtn){
37214             this.collapseBtn.setVisible(c.collapsible == true);
37215         }
37216         
37217         this.cmargins = c.cmargins || this.cmargins ||
37218                          (this.position == "west" || this.position == "east" ?
37219                              {top: 0, left: 2, right:2, bottom: 0} :
37220                              {top: 2, left: 0, right:0, bottom: 2});
37221         */
37222         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37223         
37224         
37225         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
37226         
37227         this.autoScroll = c.autoScroll || false;
37228         
37229         
37230        
37231         
37232         this.duration = c.duration || .30;
37233         this.slideDuration = c.slideDuration || .45;
37234         this.config = c;
37235        
37236     },
37237     /**
37238      * Returns true if this region is currently visible.
37239      * @return {Boolean}
37240      */
37241     isVisible : function(){
37242         return this.visible;
37243     },
37244
37245     /**
37246      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
37247      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
37248      */
37249     //setCollapsedTitle : function(title){
37250     //    title = title || "&#160;";
37251      //   if(this.collapsedTitleTextEl){
37252       //      this.collapsedTitleTextEl.innerHTML = title;
37253        // }
37254     //},
37255
37256     getBox : function(){
37257         var b;
37258       //  if(!this.collapsed){
37259             b = this.el.getBox(false, true);
37260        // }else{
37261           //  b = this.collapsedEl.getBox(false, true);
37262         //}
37263         return b;
37264     },
37265
37266     getMargins : function(){
37267         return this.margins;
37268         //return this.collapsed ? this.cmargins : this.margins;
37269     },
37270 /*
37271     highlight : function(){
37272         this.el.addClass("x-layout-panel-dragover");
37273     },
37274
37275     unhighlight : function(){
37276         this.el.removeClass("x-layout-panel-dragover");
37277     },
37278 */
37279     updateBox : function(box)
37280     {
37281         if (!this.bodyEl) {
37282             return; // not rendered yet..
37283         }
37284         
37285         this.box = box;
37286         if(!this.collapsed){
37287             this.el.dom.style.left = box.x + "px";
37288             this.el.dom.style.top = box.y + "px";
37289             this.updateBody(box.width, box.height);
37290         }else{
37291             this.collapsedEl.dom.style.left = box.x + "px";
37292             this.collapsedEl.dom.style.top = box.y + "px";
37293             this.collapsedEl.setSize(box.width, box.height);
37294         }
37295         if(this.tabs){
37296             this.tabs.autoSizeTabs();
37297         }
37298     },
37299
37300     updateBody : function(w, h)
37301     {
37302         if(w !== null){
37303             this.el.setWidth(w);
37304             w -= this.el.getBorderWidth("rl");
37305             if(this.config.adjustments){
37306                 w += this.config.adjustments[0];
37307             }
37308         }
37309         if(h !== null && h > 0){
37310             this.el.setHeight(h);
37311             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
37312             h -= this.el.getBorderWidth("tb");
37313             if(this.config.adjustments){
37314                 h += this.config.adjustments[1];
37315             }
37316             this.bodyEl.setHeight(h);
37317             if(this.tabs){
37318                 h = this.tabs.syncHeight(h);
37319             }
37320         }
37321         if(this.panelSize){
37322             w = w !== null ? w : this.panelSize.width;
37323             h = h !== null ? h : this.panelSize.height;
37324         }
37325         if(this.activePanel){
37326             var el = this.activePanel.getEl();
37327             w = w !== null ? w : el.getWidth();
37328             h = h !== null ? h : el.getHeight();
37329             this.panelSize = {width: w, height: h};
37330             this.activePanel.setSize(w, h);
37331         }
37332         if(Roo.isIE && this.tabs){
37333             this.tabs.el.repaint();
37334         }
37335     },
37336
37337     /**
37338      * Returns the container element for this region.
37339      * @return {Roo.Element}
37340      */
37341     getEl : function(){
37342         return this.el;
37343     },
37344
37345     /**
37346      * Hides this region.
37347      */
37348     hide : function(){
37349         //if(!this.collapsed){
37350             this.el.dom.style.left = "-2000px";
37351             this.el.hide();
37352         //}else{
37353          //   this.collapsedEl.dom.style.left = "-2000px";
37354          //   this.collapsedEl.hide();
37355        // }
37356         this.visible = false;
37357         this.fireEvent("visibilitychange", this, false);
37358     },
37359
37360     /**
37361      * Shows this region if it was previously hidden.
37362      */
37363     show : function(){
37364         //if(!this.collapsed){
37365             this.el.show();
37366         //}else{
37367         //    this.collapsedEl.show();
37368        // }
37369         this.visible = true;
37370         this.fireEvent("visibilitychange", this, true);
37371     },
37372 /*
37373     closeClicked : function(){
37374         if(this.activePanel){
37375             this.remove(this.activePanel);
37376         }
37377     },
37378
37379     collapseClick : function(e){
37380         if(this.isSlid){
37381            e.stopPropagation();
37382            this.slideIn();
37383         }else{
37384            e.stopPropagation();
37385            this.slideOut();
37386         }
37387     },
37388 */
37389     /**
37390      * Collapses this region.
37391      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
37392      */
37393     /*
37394     collapse : function(skipAnim, skipCheck = false){
37395         if(this.collapsed) {
37396             return;
37397         }
37398         
37399         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
37400             
37401             this.collapsed = true;
37402             if(this.split){
37403                 this.split.el.hide();
37404             }
37405             if(this.config.animate && skipAnim !== true){
37406                 this.fireEvent("invalidated", this);
37407                 this.animateCollapse();
37408             }else{
37409                 this.el.setLocation(-20000,-20000);
37410                 this.el.hide();
37411                 this.collapsedEl.show();
37412                 this.fireEvent("collapsed", this);
37413                 this.fireEvent("invalidated", this);
37414             }
37415         }
37416         
37417     },
37418 */
37419     animateCollapse : function(){
37420         // overridden
37421     },
37422
37423     /**
37424      * Expands this region if it was previously collapsed.
37425      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
37426      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
37427      */
37428     /*
37429     expand : function(e, skipAnim){
37430         if(e) {
37431             e.stopPropagation();
37432         }
37433         if(!this.collapsed || this.el.hasActiveFx()) {
37434             return;
37435         }
37436         if(this.isSlid){
37437             this.afterSlideIn();
37438             skipAnim = true;
37439         }
37440         this.collapsed = false;
37441         if(this.config.animate && skipAnim !== true){
37442             this.animateExpand();
37443         }else{
37444             this.el.show();
37445             if(this.split){
37446                 this.split.el.show();
37447             }
37448             this.collapsedEl.setLocation(-2000,-2000);
37449             this.collapsedEl.hide();
37450             this.fireEvent("invalidated", this);
37451             this.fireEvent("expanded", this);
37452         }
37453     },
37454 */
37455     animateExpand : function(){
37456         // overridden
37457     },
37458
37459     initTabs : function()
37460     {
37461         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
37462         
37463         var ts = new Roo.bootstrap.panel.Tabs({
37464             el: this.bodyEl.dom,
37465             region : this,
37466             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
37467             disableTooltips: this.config.disableTabTips,
37468             toolbar : this.config.toolbar
37469         });
37470         
37471         if(this.config.hideTabs){
37472             ts.stripWrap.setDisplayed(false);
37473         }
37474         this.tabs = ts;
37475         ts.resizeTabs = this.config.resizeTabs === true;
37476         ts.minTabWidth = this.config.minTabWidth || 40;
37477         ts.maxTabWidth = this.config.maxTabWidth || 250;
37478         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
37479         ts.monitorResize = false;
37480         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
37481         ts.bodyEl.addClass('roo-layout-tabs-body');
37482         this.panels.each(this.initPanelAsTab, this);
37483     },
37484
37485     initPanelAsTab : function(panel){
37486         var ti = this.tabs.addTab(
37487             panel.getEl().id,
37488             panel.getTitle(),
37489             null,
37490             this.config.closeOnTab && panel.isClosable(),
37491             panel.tpl
37492         );
37493         if(panel.tabTip !== undefined){
37494             ti.setTooltip(panel.tabTip);
37495         }
37496         ti.on("activate", function(){
37497               this.setActivePanel(panel);
37498         }, this);
37499         
37500         if(this.config.closeOnTab){
37501             ti.on("beforeclose", function(t, e){
37502                 e.cancel = true;
37503                 this.remove(panel);
37504             }, this);
37505         }
37506         
37507         panel.tabItem = ti;
37508         
37509         return ti;
37510     },
37511
37512     updatePanelTitle : function(panel, title)
37513     {
37514         if(this.activePanel == panel){
37515             this.updateTitle(title);
37516         }
37517         if(this.tabs){
37518             var ti = this.tabs.getTab(panel.getEl().id);
37519             ti.setText(title);
37520             if(panel.tabTip !== undefined){
37521                 ti.setTooltip(panel.tabTip);
37522             }
37523         }
37524     },
37525
37526     updateTitle : function(title){
37527         if(this.titleTextEl && !this.config.title){
37528             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
37529         }
37530     },
37531
37532     setActivePanel : function(panel)
37533     {
37534         panel = this.getPanel(panel);
37535         if(this.activePanel && this.activePanel != panel){
37536             if(this.activePanel.setActiveState(false) === false){
37537                 return;
37538             }
37539         }
37540         this.activePanel = panel;
37541         panel.setActiveState(true);
37542         if(this.panelSize){
37543             panel.setSize(this.panelSize.width, this.panelSize.height);
37544         }
37545         if(this.closeBtn){
37546             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
37547         }
37548         this.updateTitle(panel.getTitle());
37549         if(this.tabs){
37550             this.fireEvent("invalidated", this);
37551         }
37552         this.fireEvent("panelactivated", this, panel);
37553     },
37554
37555     /**
37556      * Shows the specified panel.
37557      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
37558      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
37559      */
37560     showPanel : function(panel)
37561     {
37562         panel = this.getPanel(panel);
37563         if(panel){
37564             if(this.tabs){
37565                 var tab = this.tabs.getTab(panel.getEl().id);
37566                 if(tab.isHidden()){
37567                     this.tabs.unhideTab(tab.id);
37568                 }
37569                 tab.activate();
37570             }else{
37571                 this.setActivePanel(panel);
37572             }
37573         }
37574         return panel;
37575     },
37576
37577     /**
37578      * Get the active panel for this region.
37579      * @return {Roo.ContentPanel} The active panel or null
37580      */
37581     getActivePanel : function(){
37582         return this.activePanel;
37583     },
37584
37585     validateVisibility : function(){
37586         if(this.panels.getCount() < 1){
37587             this.updateTitle("&#160;");
37588             this.closeBtn.hide();
37589             this.hide();
37590         }else{
37591             if(!this.isVisible()){
37592                 this.show();
37593             }
37594         }
37595     },
37596
37597     /**
37598      * Adds the passed ContentPanel(s) to this region.
37599      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
37600      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
37601      */
37602     add : function(panel)
37603     {
37604         if(arguments.length > 1){
37605             for(var i = 0, len = arguments.length; i < len; i++) {
37606                 this.add(arguments[i]);
37607             }
37608             return null;
37609         }
37610         
37611         // if we have not been rendered yet, then we can not really do much of this..
37612         if (!this.bodyEl) {
37613             this.unrendered_panels.push(panel);
37614             return panel;
37615         }
37616         
37617         
37618         
37619         
37620         if(this.hasPanel(panel)){
37621             this.showPanel(panel);
37622             return panel;
37623         }
37624         panel.setRegion(this);
37625         this.panels.add(panel);
37626        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
37627             // sinle panel - no tab...?? would it not be better to render it with the tabs,
37628             // and hide them... ???
37629             this.bodyEl.dom.appendChild(panel.getEl().dom);
37630             if(panel.background !== true){
37631                 this.setActivePanel(panel);
37632             }
37633             this.fireEvent("paneladded", this, panel);
37634             return panel;
37635         }
37636         */
37637         if(!this.tabs){
37638             this.initTabs();
37639         }else{
37640             this.initPanelAsTab(panel);
37641         }
37642         
37643         
37644         if(panel.background !== true){
37645             this.tabs.activate(panel.getEl().id);
37646         }
37647         this.fireEvent("paneladded", this, panel);
37648         return panel;
37649     },
37650
37651     /**
37652      * Hides the tab for the specified panel.
37653      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
37654      */
37655     hidePanel : function(panel){
37656         if(this.tabs && (panel = this.getPanel(panel))){
37657             this.tabs.hideTab(panel.getEl().id);
37658         }
37659     },
37660
37661     /**
37662      * Unhides the tab for a previously hidden panel.
37663      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
37664      */
37665     unhidePanel : function(panel){
37666         if(this.tabs && (panel = this.getPanel(panel))){
37667             this.tabs.unhideTab(panel.getEl().id);
37668         }
37669     },
37670
37671     clearPanels : function(){
37672         while(this.panels.getCount() > 0){
37673              this.remove(this.panels.first());
37674         }
37675     },
37676
37677     /**
37678      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
37679      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
37680      * @param {Boolean} preservePanel Overrides the config preservePanel option
37681      * @return {Roo.ContentPanel} The panel that was removed
37682      */
37683     remove : function(panel, preservePanel)
37684     {
37685         panel = this.getPanel(panel);
37686         if(!panel){
37687             return null;
37688         }
37689         var e = {};
37690         this.fireEvent("beforeremove", this, panel, e);
37691         if(e.cancel === true){
37692             return null;
37693         }
37694         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
37695         var panelId = panel.getId();
37696         this.panels.removeKey(panelId);
37697         if(preservePanel){
37698             document.body.appendChild(panel.getEl().dom);
37699         }
37700         if(this.tabs){
37701             this.tabs.removeTab(panel.getEl().id);
37702         }else if (!preservePanel){
37703             this.bodyEl.dom.removeChild(panel.getEl().dom);
37704         }
37705         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
37706             var p = this.panels.first();
37707             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
37708             tempEl.appendChild(p.getEl().dom);
37709             this.bodyEl.update("");
37710             this.bodyEl.dom.appendChild(p.getEl().dom);
37711             tempEl = null;
37712             this.updateTitle(p.getTitle());
37713             this.tabs = null;
37714             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
37715             this.setActivePanel(p);
37716         }
37717         panel.setRegion(null);
37718         if(this.activePanel == panel){
37719             this.activePanel = null;
37720         }
37721         if(this.config.autoDestroy !== false && preservePanel !== true){
37722             try{panel.destroy();}catch(e){}
37723         }
37724         this.fireEvent("panelremoved", this, panel);
37725         return panel;
37726     },
37727
37728     /**
37729      * Returns the TabPanel component used by this region
37730      * @return {Roo.TabPanel}
37731      */
37732     getTabs : function(){
37733         return this.tabs;
37734     },
37735
37736     createTool : function(parentEl, className){
37737         var btn = Roo.DomHelper.append(parentEl, {
37738             tag: "div",
37739             cls: "x-layout-tools-button",
37740             children: [ {
37741                 tag: "div",
37742                 cls: "roo-layout-tools-button-inner " + className,
37743                 html: "&#160;"
37744             }]
37745         }, true);
37746         btn.addClassOnOver("roo-layout-tools-button-over");
37747         return btn;
37748     }
37749 });/*
37750  * Based on:
37751  * Ext JS Library 1.1.1
37752  * Copyright(c) 2006-2007, Ext JS, LLC.
37753  *
37754  * Originally Released Under LGPL - original licence link has changed is not relivant.
37755  *
37756  * Fork - LGPL
37757  * <script type="text/javascript">
37758  */
37759  
37760
37761
37762 /**
37763  * @class Roo.SplitLayoutRegion
37764  * @extends Roo.LayoutRegion
37765  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
37766  */
37767 Roo.bootstrap.layout.Split = function(config){
37768     this.cursor = config.cursor;
37769     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
37770 };
37771
37772 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
37773 {
37774     splitTip : "Drag to resize.",
37775     collapsibleSplitTip : "Drag to resize. Double click to hide.",
37776     useSplitTips : false,
37777
37778     applyConfig : function(config){
37779         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
37780     },
37781     
37782     onRender : function(ctr,pos) {
37783         
37784         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
37785         if(!this.config.split){
37786             return;
37787         }
37788         if(!this.split){
37789             
37790             var splitEl = Roo.DomHelper.append(ctr.dom,  {
37791                             tag: "div",
37792                             id: this.el.id + "-split",
37793                             cls: "roo-layout-split roo-layout-split-"+this.position,
37794                             html: "&#160;"
37795             });
37796             /** The SplitBar for this region 
37797             * @type Roo.SplitBar */
37798             // does not exist yet...
37799             Roo.log([this.position, this.orientation]);
37800             
37801             this.split = new Roo.bootstrap.SplitBar({
37802                 dragElement : splitEl,
37803                 resizingElement: this.el,
37804                 orientation : this.orientation
37805             });
37806             
37807             this.split.on("moved", this.onSplitMove, this);
37808             this.split.useShim = this.config.useShim === true;
37809             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
37810             if(this.useSplitTips){
37811                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
37812             }
37813             //if(config.collapsible){
37814             //    this.split.el.on("dblclick", this.collapse,  this);
37815             //}
37816         }
37817         if(typeof this.config.minSize != "undefined"){
37818             this.split.minSize = this.config.minSize;
37819         }
37820         if(typeof this.config.maxSize != "undefined"){
37821             this.split.maxSize = this.config.maxSize;
37822         }
37823         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
37824             this.hideSplitter();
37825         }
37826         
37827     },
37828
37829     getHMaxSize : function(){
37830          var cmax = this.config.maxSize || 10000;
37831          var center = this.mgr.getRegion("center");
37832          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
37833     },
37834
37835     getVMaxSize : function(){
37836          var cmax = this.config.maxSize || 10000;
37837          var center = this.mgr.getRegion("center");
37838          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
37839     },
37840
37841     onSplitMove : function(split, newSize){
37842         this.fireEvent("resized", this, newSize);
37843     },
37844     
37845     /** 
37846      * Returns the {@link Roo.SplitBar} for this region.
37847      * @return {Roo.SplitBar}
37848      */
37849     getSplitBar : function(){
37850         return this.split;
37851     },
37852     
37853     hide : function(){
37854         this.hideSplitter();
37855         Roo.bootstrap.layout.Split.superclass.hide.call(this);
37856     },
37857
37858     hideSplitter : function(){
37859         if(this.split){
37860             this.split.el.setLocation(-2000,-2000);
37861             this.split.el.hide();
37862         }
37863     },
37864
37865     show : function(){
37866         if(this.split){
37867             this.split.el.show();
37868         }
37869         Roo.bootstrap.layout.Split.superclass.show.call(this);
37870     },
37871     
37872     beforeSlide: function(){
37873         if(Roo.isGecko){// firefox overflow auto bug workaround
37874             this.bodyEl.clip();
37875             if(this.tabs) {
37876                 this.tabs.bodyEl.clip();
37877             }
37878             if(this.activePanel){
37879                 this.activePanel.getEl().clip();
37880                 
37881                 if(this.activePanel.beforeSlide){
37882                     this.activePanel.beforeSlide();
37883                 }
37884             }
37885         }
37886     },
37887     
37888     afterSlide : function(){
37889         if(Roo.isGecko){// firefox overflow auto bug workaround
37890             this.bodyEl.unclip();
37891             if(this.tabs) {
37892                 this.tabs.bodyEl.unclip();
37893             }
37894             if(this.activePanel){
37895                 this.activePanel.getEl().unclip();
37896                 if(this.activePanel.afterSlide){
37897                     this.activePanel.afterSlide();
37898                 }
37899             }
37900         }
37901     },
37902
37903     initAutoHide : function(){
37904         if(this.autoHide !== false){
37905             if(!this.autoHideHd){
37906                 var st = new Roo.util.DelayedTask(this.slideIn, this);
37907                 this.autoHideHd = {
37908                     "mouseout": function(e){
37909                         if(!e.within(this.el, true)){
37910                             st.delay(500);
37911                         }
37912                     },
37913                     "mouseover" : function(e){
37914                         st.cancel();
37915                     },
37916                     scope : this
37917                 };
37918             }
37919             this.el.on(this.autoHideHd);
37920         }
37921     },
37922
37923     clearAutoHide : function(){
37924         if(this.autoHide !== false){
37925             this.el.un("mouseout", this.autoHideHd.mouseout);
37926             this.el.un("mouseover", this.autoHideHd.mouseover);
37927         }
37928     },
37929
37930     clearMonitor : function(){
37931         Roo.get(document).un("click", this.slideInIf, this);
37932     },
37933
37934     // these names are backwards but not changed for compat
37935     slideOut : function(){
37936         if(this.isSlid || this.el.hasActiveFx()){
37937             return;
37938         }
37939         this.isSlid = true;
37940         if(this.collapseBtn){
37941             this.collapseBtn.hide();
37942         }
37943         this.closeBtnState = this.closeBtn.getStyle('display');
37944         this.closeBtn.hide();
37945         if(this.stickBtn){
37946             this.stickBtn.show();
37947         }
37948         this.el.show();
37949         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
37950         this.beforeSlide();
37951         this.el.setStyle("z-index", 10001);
37952         this.el.slideIn(this.getSlideAnchor(), {
37953             callback: function(){
37954                 this.afterSlide();
37955                 this.initAutoHide();
37956                 Roo.get(document).on("click", this.slideInIf, this);
37957                 this.fireEvent("slideshow", this);
37958             },
37959             scope: this,
37960             block: true
37961         });
37962     },
37963
37964     afterSlideIn : function(){
37965         this.clearAutoHide();
37966         this.isSlid = false;
37967         this.clearMonitor();
37968         this.el.setStyle("z-index", "");
37969         if(this.collapseBtn){
37970             this.collapseBtn.show();
37971         }
37972         this.closeBtn.setStyle('display', this.closeBtnState);
37973         if(this.stickBtn){
37974             this.stickBtn.hide();
37975         }
37976         this.fireEvent("slidehide", this);
37977     },
37978
37979     slideIn : function(cb){
37980         if(!this.isSlid || this.el.hasActiveFx()){
37981             Roo.callback(cb);
37982             return;
37983         }
37984         this.isSlid = false;
37985         this.beforeSlide();
37986         this.el.slideOut(this.getSlideAnchor(), {
37987             callback: function(){
37988                 this.el.setLeftTop(-10000, -10000);
37989                 this.afterSlide();
37990                 this.afterSlideIn();
37991                 Roo.callback(cb);
37992             },
37993             scope: this,
37994             block: true
37995         });
37996     },
37997     
37998     slideInIf : function(e){
37999         if(!e.within(this.el)){
38000             this.slideIn();
38001         }
38002     },
38003
38004     animateCollapse : function(){
38005         this.beforeSlide();
38006         this.el.setStyle("z-index", 20000);
38007         var anchor = this.getSlideAnchor();
38008         this.el.slideOut(anchor, {
38009             callback : function(){
38010                 this.el.setStyle("z-index", "");
38011                 this.collapsedEl.slideIn(anchor, {duration:.3});
38012                 this.afterSlide();
38013                 this.el.setLocation(-10000,-10000);
38014                 this.el.hide();
38015                 this.fireEvent("collapsed", this);
38016             },
38017             scope: this,
38018             block: true
38019         });
38020     },
38021
38022     animateExpand : function(){
38023         this.beforeSlide();
38024         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
38025         this.el.setStyle("z-index", 20000);
38026         this.collapsedEl.hide({
38027             duration:.1
38028         });
38029         this.el.slideIn(this.getSlideAnchor(), {
38030             callback : function(){
38031                 this.el.setStyle("z-index", "");
38032                 this.afterSlide();
38033                 if(this.split){
38034                     this.split.el.show();
38035                 }
38036                 this.fireEvent("invalidated", this);
38037                 this.fireEvent("expanded", this);
38038             },
38039             scope: this,
38040             block: true
38041         });
38042     },
38043
38044     anchors : {
38045         "west" : "left",
38046         "east" : "right",
38047         "north" : "top",
38048         "south" : "bottom"
38049     },
38050
38051     sanchors : {
38052         "west" : "l",
38053         "east" : "r",
38054         "north" : "t",
38055         "south" : "b"
38056     },
38057
38058     canchors : {
38059         "west" : "tl-tr",
38060         "east" : "tr-tl",
38061         "north" : "tl-bl",
38062         "south" : "bl-tl"
38063     },
38064
38065     getAnchor : function(){
38066         return this.anchors[this.position];
38067     },
38068
38069     getCollapseAnchor : function(){
38070         return this.canchors[this.position];
38071     },
38072
38073     getSlideAnchor : function(){
38074         return this.sanchors[this.position];
38075     },
38076
38077     getAlignAdj : function(){
38078         var cm = this.cmargins;
38079         switch(this.position){
38080             case "west":
38081                 return [0, 0];
38082             break;
38083             case "east":
38084                 return [0, 0];
38085             break;
38086             case "north":
38087                 return [0, 0];
38088             break;
38089             case "south":
38090                 return [0, 0];
38091             break;
38092         }
38093     },
38094
38095     getExpandAdj : function(){
38096         var c = this.collapsedEl, cm = this.cmargins;
38097         switch(this.position){
38098             case "west":
38099                 return [-(cm.right+c.getWidth()+cm.left), 0];
38100             break;
38101             case "east":
38102                 return [cm.right+c.getWidth()+cm.left, 0];
38103             break;
38104             case "north":
38105                 return [0, -(cm.top+cm.bottom+c.getHeight())];
38106             break;
38107             case "south":
38108                 return [0, cm.top+cm.bottom+c.getHeight()];
38109             break;
38110         }
38111     }
38112 });/*
38113  * Based on:
38114  * Ext JS Library 1.1.1
38115  * Copyright(c) 2006-2007, Ext JS, LLC.
38116  *
38117  * Originally Released Under LGPL - original licence link has changed is not relivant.
38118  *
38119  * Fork - LGPL
38120  * <script type="text/javascript">
38121  */
38122 /*
38123  * These classes are private internal classes
38124  */
38125 Roo.bootstrap.layout.Center = function(config){
38126     config.region = "center";
38127     Roo.bootstrap.layout.Region.call(this, config);
38128     this.visible = true;
38129     this.minWidth = config.minWidth || 20;
38130     this.minHeight = config.minHeight || 20;
38131 };
38132
38133 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
38134     hide : function(){
38135         // center panel can't be hidden
38136     },
38137     
38138     show : function(){
38139         // center panel can't be hidden
38140     },
38141     
38142     getMinWidth: function(){
38143         return this.minWidth;
38144     },
38145     
38146     getMinHeight: function(){
38147         return this.minHeight;
38148     }
38149 });
38150
38151
38152
38153
38154  
38155
38156
38157
38158
38159
38160
38161 Roo.bootstrap.layout.North = function(config)
38162 {
38163     config.region = 'north';
38164     config.cursor = 'n-resize';
38165     
38166     Roo.bootstrap.layout.Split.call(this, config);
38167     
38168     
38169     if(this.split){
38170         this.split.placement = Roo.bootstrap.SplitBar.TOP;
38171         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
38172         this.split.el.addClass("roo-layout-split-v");
38173     }
38174     var size = config.initialSize || config.height;
38175     if(typeof size != "undefined"){
38176         this.el.setHeight(size);
38177     }
38178 };
38179 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
38180 {
38181     orientation: Roo.bootstrap.SplitBar.VERTICAL,
38182     
38183     
38184     
38185     getBox : function(){
38186         if(this.collapsed){
38187             return this.collapsedEl.getBox();
38188         }
38189         var box = this.el.getBox();
38190         if(this.split){
38191             box.height += this.split.el.getHeight();
38192         }
38193         return box;
38194     },
38195     
38196     updateBox : function(box){
38197         if(this.split && !this.collapsed){
38198             box.height -= this.split.el.getHeight();
38199             this.split.el.setLeft(box.x);
38200             this.split.el.setTop(box.y+box.height);
38201             this.split.el.setWidth(box.width);
38202         }
38203         if(this.collapsed){
38204             this.updateBody(box.width, null);
38205         }
38206         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38207     }
38208 });
38209
38210
38211
38212
38213
38214 Roo.bootstrap.layout.South = function(config){
38215     config.region = 'south';
38216     config.cursor = 's-resize';
38217     Roo.bootstrap.layout.Split.call(this, config);
38218     if(this.split){
38219         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
38220         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
38221         this.split.el.addClass("roo-layout-split-v");
38222     }
38223     var size = config.initialSize || config.height;
38224     if(typeof size != "undefined"){
38225         this.el.setHeight(size);
38226     }
38227 };
38228
38229 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
38230     orientation: Roo.bootstrap.SplitBar.VERTICAL,
38231     getBox : function(){
38232         if(this.collapsed){
38233             return this.collapsedEl.getBox();
38234         }
38235         var box = this.el.getBox();
38236         if(this.split){
38237             var sh = this.split.el.getHeight();
38238             box.height += sh;
38239             box.y -= sh;
38240         }
38241         return box;
38242     },
38243     
38244     updateBox : function(box){
38245         if(this.split && !this.collapsed){
38246             var sh = this.split.el.getHeight();
38247             box.height -= sh;
38248             box.y += sh;
38249             this.split.el.setLeft(box.x);
38250             this.split.el.setTop(box.y-sh);
38251             this.split.el.setWidth(box.width);
38252         }
38253         if(this.collapsed){
38254             this.updateBody(box.width, null);
38255         }
38256         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38257     }
38258 });
38259
38260 Roo.bootstrap.layout.East = function(config){
38261     config.region = "east";
38262     config.cursor = "e-resize";
38263     Roo.bootstrap.layout.Split.call(this, config);
38264     if(this.split){
38265         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
38266         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
38267         this.split.el.addClass("roo-layout-split-h");
38268     }
38269     var size = config.initialSize || config.width;
38270     if(typeof size != "undefined"){
38271         this.el.setWidth(size);
38272     }
38273 };
38274 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
38275     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
38276     getBox : function(){
38277         if(this.collapsed){
38278             return this.collapsedEl.getBox();
38279         }
38280         var box = this.el.getBox();
38281         if(this.split){
38282             var sw = this.split.el.getWidth();
38283             box.width += sw;
38284             box.x -= sw;
38285         }
38286         return box;
38287     },
38288
38289     updateBox : function(box){
38290         if(this.split && !this.collapsed){
38291             var sw = this.split.el.getWidth();
38292             box.width -= sw;
38293             this.split.el.setLeft(box.x);
38294             this.split.el.setTop(box.y);
38295             this.split.el.setHeight(box.height);
38296             box.x += sw;
38297         }
38298         if(this.collapsed){
38299             this.updateBody(null, box.height);
38300         }
38301         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38302     }
38303 });
38304
38305 Roo.bootstrap.layout.West = function(config){
38306     config.region = "west";
38307     config.cursor = "w-resize";
38308     
38309     Roo.bootstrap.layout.Split.call(this, config);
38310     if(this.split){
38311         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
38312         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
38313         this.split.el.addClass("roo-layout-split-h");
38314     }
38315     
38316 };
38317 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
38318     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
38319     
38320     onRender: function(ctr, pos)
38321     {
38322         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
38323         var size = this.config.initialSize || this.config.width;
38324         if(typeof size != "undefined"){
38325             this.el.setWidth(size);
38326         }
38327     },
38328     
38329     getBox : function(){
38330         if(this.collapsed){
38331             return this.collapsedEl.getBox();
38332         }
38333         var box = this.el.getBox();
38334         if(this.split){
38335             box.width += this.split.el.getWidth();
38336         }
38337         return box;
38338     },
38339     
38340     updateBox : function(box){
38341         if(this.split && !this.collapsed){
38342             var sw = this.split.el.getWidth();
38343             box.width -= sw;
38344             this.split.el.setLeft(box.x+box.width);
38345             this.split.el.setTop(box.y);
38346             this.split.el.setHeight(box.height);
38347         }
38348         if(this.collapsed){
38349             this.updateBody(null, box.height);
38350         }
38351         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38352     }
38353 });Roo.namespace("Roo.bootstrap.panel");/*
38354  * Based on:
38355  * Ext JS Library 1.1.1
38356  * Copyright(c) 2006-2007, Ext JS, LLC.
38357  *
38358  * Originally Released Under LGPL - original licence link has changed is not relivant.
38359  *
38360  * Fork - LGPL
38361  * <script type="text/javascript">
38362  */
38363 /**
38364  * @class Roo.ContentPanel
38365  * @extends Roo.util.Observable
38366  * A basic ContentPanel element.
38367  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
38368  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
38369  * @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
38370  * @cfg {Boolean}   closable      True if the panel can be closed/removed
38371  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
38372  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
38373  * @cfg {Toolbar}   toolbar       A toolbar for this panel
38374  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
38375  * @cfg {String} title          The title for this panel
38376  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
38377  * @cfg {String} url            Calls {@link #setUrl} with this value
38378  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
38379  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
38380  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
38381  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
38382  * @cfg {Boolean} badges render the badges
38383
38384  * @constructor
38385  * Create a new ContentPanel.
38386  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
38387  * @param {String/Object} config A string to set only the title or a config object
38388  * @param {String} content (optional) Set the HTML content for this panel
38389  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
38390  */
38391 Roo.bootstrap.panel.Content = function( config){
38392     
38393     this.tpl = config.tpl || false;
38394     
38395     var el = config.el;
38396     var content = config.content;
38397
38398     if(config.autoCreate){ // xtype is available if this is called from factory
38399         el = Roo.id();
38400     }
38401     this.el = Roo.get(el);
38402     if(!this.el && config && config.autoCreate){
38403         if(typeof config.autoCreate == "object"){
38404             if(!config.autoCreate.id){
38405                 config.autoCreate.id = config.id||el;
38406             }
38407             this.el = Roo.DomHelper.append(document.body,
38408                         config.autoCreate, true);
38409         }else{
38410             var elcfg =  {   tag: "div",
38411                             cls: "roo-layout-inactive-content",
38412                             id: config.id||el
38413                             };
38414             if (config.html) {
38415                 elcfg.html = config.html;
38416                 
38417             }
38418                         
38419             this.el = Roo.DomHelper.append(document.body, elcfg , true);
38420         }
38421     } 
38422     this.closable = false;
38423     this.loaded = false;
38424     this.active = false;
38425    
38426       
38427     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
38428         
38429         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
38430         
38431         this.wrapEl = this.el; //this.el.wrap();
38432         var ti = [];
38433         if (config.toolbar.items) {
38434             ti = config.toolbar.items ;
38435             delete config.toolbar.items ;
38436         }
38437         
38438         var nitems = [];
38439         this.toolbar.render(this.wrapEl, 'before');
38440         for(var i =0;i < ti.length;i++) {
38441           //  Roo.log(['add child', items[i]]);
38442             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
38443         }
38444         this.toolbar.items = nitems;
38445         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
38446         delete config.toolbar;
38447         
38448     }
38449     /*
38450     // xtype created footer. - not sure if will work as we normally have to render first..
38451     if (this.footer && !this.footer.el && this.footer.xtype) {
38452         if (!this.wrapEl) {
38453             this.wrapEl = this.el.wrap();
38454         }
38455     
38456         this.footer.container = this.wrapEl.createChild();
38457          
38458         this.footer = Roo.factory(this.footer, Roo);
38459         
38460     }
38461     */
38462     
38463      if(typeof config == "string"){
38464         this.title = config;
38465     }else{
38466         Roo.apply(this, config);
38467     }
38468     
38469     if(this.resizeEl){
38470         this.resizeEl = Roo.get(this.resizeEl, true);
38471     }else{
38472         this.resizeEl = this.el;
38473     }
38474     // handle view.xtype
38475     
38476  
38477     
38478     
38479     this.addEvents({
38480         /**
38481          * @event activate
38482          * Fires when this panel is activated. 
38483          * @param {Roo.ContentPanel} this
38484          */
38485         "activate" : true,
38486         /**
38487          * @event deactivate
38488          * Fires when this panel is activated. 
38489          * @param {Roo.ContentPanel} this
38490          */
38491         "deactivate" : true,
38492
38493         /**
38494          * @event resize
38495          * Fires when this panel is resized if fitToFrame is true.
38496          * @param {Roo.ContentPanel} this
38497          * @param {Number} width The width after any component adjustments
38498          * @param {Number} height The height after any component adjustments
38499          */
38500         "resize" : true,
38501         
38502          /**
38503          * @event render
38504          * Fires when this tab is created
38505          * @param {Roo.ContentPanel} this
38506          */
38507         "render" : true
38508         
38509         
38510         
38511     });
38512     
38513
38514     
38515     
38516     if(this.autoScroll){
38517         this.resizeEl.setStyle("overflow", "auto");
38518     } else {
38519         // fix randome scrolling
38520         //this.el.on('scroll', function() {
38521         //    Roo.log('fix random scolling');
38522         //    this.scrollTo('top',0); 
38523         //});
38524     }
38525     content = content || this.content;
38526     if(content){
38527         this.setContent(content);
38528     }
38529     if(config && config.url){
38530         this.setUrl(this.url, this.params, this.loadOnce);
38531     }
38532     
38533     
38534     
38535     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
38536     
38537     if (this.view && typeof(this.view.xtype) != 'undefined') {
38538         this.view.el = this.el.appendChild(document.createElement("div"));
38539         this.view = Roo.factory(this.view); 
38540         this.view.render  &&  this.view.render(false, '');  
38541     }
38542     
38543     
38544     this.fireEvent('render', this);
38545 };
38546
38547 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
38548     
38549     tabTip : '',
38550     
38551     setRegion : function(region){
38552         this.region = region;
38553         this.setActiveClass(region && !this.background);
38554     },
38555     
38556     
38557     setActiveClass: function(state)
38558     {
38559         if(state){
38560            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
38561            this.el.setStyle('position','relative');
38562         }else{
38563            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
38564            this.el.setStyle('position', 'absolute');
38565         } 
38566     },
38567     
38568     /**
38569      * Returns the toolbar for this Panel if one was configured. 
38570      * @return {Roo.Toolbar} 
38571      */
38572     getToolbar : function(){
38573         return this.toolbar;
38574     },
38575     
38576     setActiveState : function(active)
38577     {
38578         this.active = active;
38579         this.setActiveClass(active);
38580         if(!active){
38581             if(this.fireEvent("deactivate", this) === false){
38582                 return false;
38583             }
38584             return true;
38585         }
38586         this.fireEvent("activate", this);
38587         return true;
38588     },
38589     /**
38590      * Updates this panel's element
38591      * @param {String} content The new content
38592      * @param {Boolean} loadScripts (optional) true to look for and process scripts
38593     */
38594     setContent : function(content, loadScripts){
38595         this.el.update(content, loadScripts);
38596     },
38597
38598     ignoreResize : function(w, h){
38599         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
38600             return true;
38601         }else{
38602             this.lastSize = {width: w, height: h};
38603             return false;
38604         }
38605     },
38606     /**
38607      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
38608      * @return {Roo.UpdateManager} The UpdateManager
38609      */
38610     getUpdateManager : function(){
38611         return this.el.getUpdateManager();
38612     },
38613      /**
38614      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
38615      * @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:
38616 <pre><code>
38617 panel.load({
38618     url: "your-url.php",
38619     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
38620     callback: yourFunction,
38621     scope: yourObject, //(optional scope)
38622     discardUrl: false,
38623     nocache: false,
38624     text: "Loading...",
38625     timeout: 30,
38626     scripts: false
38627 });
38628 </code></pre>
38629      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
38630      * 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.
38631      * @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}
38632      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
38633      * @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.
38634      * @return {Roo.ContentPanel} this
38635      */
38636     load : function(){
38637         var um = this.el.getUpdateManager();
38638         um.update.apply(um, arguments);
38639         return this;
38640     },
38641
38642
38643     /**
38644      * 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.
38645      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
38646      * @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)
38647      * @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)
38648      * @return {Roo.UpdateManager} The UpdateManager
38649      */
38650     setUrl : function(url, params, loadOnce){
38651         if(this.refreshDelegate){
38652             this.removeListener("activate", this.refreshDelegate);
38653         }
38654         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38655         this.on("activate", this.refreshDelegate);
38656         return this.el.getUpdateManager();
38657     },
38658     
38659     _handleRefresh : function(url, params, loadOnce){
38660         if(!loadOnce || !this.loaded){
38661             var updater = this.el.getUpdateManager();
38662             updater.update(url, params, this._setLoaded.createDelegate(this));
38663         }
38664     },
38665     
38666     _setLoaded : function(){
38667         this.loaded = true;
38668     }, 
38669     
38670     /**
38671      * Returns this panel's id
38672      * @return {String} 
38673      */
38674     getId : function(){
38675         return this.el.id;
38676     },
38677     
38678     /** 
38679      * Returns this panel's element - used by regiosn to add.
38680      * @return {Roo.Element} 
38681      */
38682     getEl : function(){
38683         return this.wrapEl || this.el;
38684     },
38685     
38686    
38687     
38688     adjustForComponents : function(width, height)
38689     {
38690         //Roo.log('adjustForComponents ');
38691         if(this.resizeEl != this.el){
38692             width -= this.el.getFrameWidth('lr');
38693             height -= this.el.getFrameWidth('tb');
38694         }
38695         if(this.toolbar){
38696             var te = this.toolbar.getEl();
38697             te.setWidth(width);
38698             height -= te.getHeight();
38699         }
38700         if(this.footer){
38701             var te = this.footer.getEl();
38702             te.setWidth(width);
38703             height -= te.getHeight();
38704         }
38705         
38706         
38707         if(this.adjustments){
38708             width += this.adjustments[0];
38709             height += this.adjustments[1];
38710         }
38711         return {"width": width, "height": height};
38712     },
38713     
38714     setSize : function(width, height){
38715         if(this.fitToFrame && !this.ignoreResize(width, height)){
38716             if(this.fitContainer && this.resizeEl != this.el){
38717                 this.el.setSize(width, height);
38718             }
38719             var size = this.adjustForComponents(width, height);
38720             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
38721             this.fireEvent('resize', this, size.width, size.height);
38722         }
38723     },
38724     
38725     /**
38726      * Returns this panel's title
38727      * @return {String} 
38728      */
38729     getTitle : function(){
38730         
38731         if (typeof(this.title) != 'object') {
38732             return this.title;
38733         }
38734         
38735         var t = '';
38736         for (var k in this.title) {
38737             if (!this.title.hasOwnProperty(k)) {
38738                 continue;
38739             }
38740             
38741             if (k.indexOf('-') >= 0) {
38742                 var s = k.split('-');
38743                 for (var i = 0; i<s.length; i++) {
38744                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
38745                 }
38746             } else {
38747                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
38748             }
38749         }
38750         return t;
38751     },
38752     
38753     /**
38754      * Set this panel's title
38755      * @param {String} title
38756      */
38757     setTitle : function(title){
38758         this.title = title;
38759         if(this.region){
38760             this.region.updatePanelTitle(this, title);
38761         }
38762     },
38763     
38764     /**
38765      * Returns true is this panel was configured to be closable
38766      * @return {Boolean} 
38767      */
38768     isClosable : function(){
38769         return this.closable;
38770     },
38771     
38772     beforeSlide : function(){
38773         this.el.clip();
38774         this.resizeEl.clip();
38775     },
38776     
38777     afterSlide : function(){
38778         this.el.unclip();
38779         this.resizeEl.unclip();
38780     },
38781     
38782     /**
38783      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
38784      *   Will fail silently if the {@link #setUrl} method has not been called.
38785      *   This does not activate the panel, just updates its content.
38786      */
38787     refresh : function(){
38788         if(this.refreshDelegate){
38789            this.loaded = false;
38790            this.refreshDelegate();
38791         }
38792     },
38793     
38794     /**
38795      * Destroys this panel
38796      */
38797     destroy : function(){
38798         this.el.removeAllListeners();
38799         var tempEl = document.createElement("span");
38800         tempEl.appendChild(this.el.dom);
38801         tempEl.innerHTML = "";
38802         this.el.remove();
38803         this.el = null;
38804     },
38805     
38806     /**
38807      * form - if the content panel contains a form - this is a reference to it.
38808      * @type {Roo.form.Form}
38809      */
38810     form : false,
38811     /**
38812      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
38813      *    This contains a reference to it.
38814      * @type {Roo.View}
38815      */
38816     view : false,
38817     
38818       /**
38819      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
38820      * <pre><code>
38821
38822 layout.addxtype({
38823        xtype : 'Form',
38824        items: [ .... ]
38825    }
38826 );
38827
38828 </code></pre>
38829      * @param {Object} cfg Xtype definition of item to add.
38830      */
38831     
38832     
38833     getChildContainer: function () {
38834         return this.getEl();
38835     }
38836     
38837     
38838     /*
38839         var  ret = new Roo.factory(cfg);
38840         return ret;
38841         
38842         
38843         // add form..
38844         if (cfg.xtype.match(/^Form$/)) {
38845             
38846             var el;
38847             //if (this.footer) {
38848             //    el = this.footer.container.insertSibling(false, 'before');
38849             //} else {
38850                 el = this.el.createChild();
38851             //}
38852
38853             this.form = new  Roo.form.Form(cfg);
38854             
38855             
38856             if ( this.form.allItems.length) {
38857                 this.form.render(el.dom);
38858             }
38859             return this.form;
38860         }
38861         // should only have one of theses..
38862         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
38863             // views.. should not be just added - used named prop 'view''
38864             
38865             cfg.el = this.el.appendChild(document.createElement("div"));
38866             // factory?
38867             
38868             var ret = new Roo.factory(cfg);
38869              
38870              ret.render && ret.render(false, ''); // render blank..
38871             this.view = ret;
38872             return ret;
38873         }
38874         return false;
38875     }
38876     \*/
38877 });
38878  
38879 /**
38880  * @class Roo.bootstrap.panel.Grid
38881  * @extends Roo.bootstrap.panel.Content
38882  * @constructor
38883  * Create a new GridPanel.
38884  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
38885  * @param {Object} config A the config object
38886   
38887  */
38888
38889
38890
38891 Roo.bootstrap.panel.Grid = function(config)
38892 {
38893     
38894       
38895     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
38896         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
38897
38898     config.el = this.wrapper;
38899     //this.el = this.wrapper;
38900     
38901       if (config.container) {
38902         // ctor'ed from a Border/panel.grid
38903         
38904         
38905         this.wrapper.setStyle("overflow", "hidden");
38906         this.wrapper.addClass('roo-grid-container');
38907
38908     }
38909     
38910     
38911     if(config.toolbar){
38912         var tool_el = this.wrapper.createChild();    
38913         this.toolbar = Roo.factory(config.toolbar);
38914         var ti = [];
38915         if (config.toolbar.items) {
38916             ti = config.toolbar.items ;
38917             delete config.toolbar.items ;
38918         }
38919         
38920         var nitems = [];
38921         this.toolbar.render(tool_el);
38922         for(var i =0;i < ti.length;i++) {
38923           //  Roo.log(['add child', items[i]]);
38924             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
38925         }
38926         this.toolbar.items = nitems;
38927         
38928         delete config.toolbar;
38929     }
38930     
38931     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
38932     config.grid.scrollBody = true;;
38933     config.grid.monitorWindowResize = false; // turn off autosizing
38934     config.grid.autoHeight = false;
38935     config.grid.autoWidth = false;
38936     
38937     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
38938     
38939     if (config.background) {
38940         // render grid on panel activation (if panel background)
38941         this.on('activate', function(gp) {
38942             if (!gp.grid.rendered) {
38943                 gp.grid.render(this.wrapper);
38944                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
38945             }
38946         });
38947             
38948     } else {
38949         this.grid.render(this.wrapper);
38950         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
38951
38952     }
38953     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
38954     // ??? needed ??? config.el = this.wrapper;
38955     
38956     
38957     
38958   
38959     // xtype created footer. - not sure if will work as we normally have to render first..
38960     if (this.footer && !this.footer.el && this.footer.xtype) {
38961         
38962         var ctr = this.grid.getView().getFooterPanel(true);
38963         this.footer.dataSource = this.grid.dataSource;
38964         this.footer = Roo.factory(this.footer, Roo);
38965         this.footer.render(ctr);
38966         
38967     }
38968     
38969     
38970     
38971     
38972      
38973 };
38974
38975 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
38976     getId : function(){
38977         return this.grid.id;
38978     },
38979     
38980     /**
38981      * Returns the grid for this panel
38982      * @return {Roo.bootstrap.Table} 
38983      */
38984     getGrid : function(){
38985         return this.grid;    
38986     },
38987     
38988     setSize : function(width, height){
38989         if(!this.ignoreResize(width, height)){
38990             var grid = this.grid;
38991             var size = this.adjustForComponents(width, height);
38992             var gridel = grid.getGridEl();
38993             gridel.setSize(size.width, size.height);
38994             /*
38995             var thd = grid.getGridEl().select('thead',true).first();
38996             var tbd = grid.getGridEl().select('tbody', true).first();
38997             if (tbd) {
38998                 tbd.setSize(width, height - thd.getHeight());
38999             }
39000             */
39001             grid.autoSize();
39002         }
39003     },
39004      
39005     
39006     
39007     beforeSlide : function(){
39008         this.grid.getView().scroller.clip();
39009     },
39010     
39011     afterSlide : function(){
39012         this.grid.getView().scroller.unclip();
39013     },
39014     
39015     destroy : function(){
39016         this.grid.destroy();
39017         delete this.grid;
39018         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
39019     }
39020 });
39021
39022 /**
39023  * @class Roo.bootstrap.panel.Nest
39024  * @extends Roo.bootstrap.panel.Content
39025  * @constructor
39026  * Create a new Panel, that can contain a layout.Border.
39027  * 
39028  * 
39029  * @param {Roo.BorderLayout} layout The layout for this panel
39030  * @param {String/Object} config A string to set only the title or a config object
39031  */
39032 Roo.bootstrap.panel.Nest = function(config)
39033 {
39034     // construct with only one argument..
39035     /* FIXME - implement nicer consturctors
39036     if (layout.layout) {
39037         config = layout;
39038         layout = config.layout;
39039         delete config.layout;
39040     }
39041     if (layout.xtype && !layout.getEl) {
39042         // then layout needs constructing..
39043         layout = Roo.factory(layout, Roo);
39044     }
39045     */
39046     
39047     config.el =  config.layout.getEl();
39048     
39049     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
39050     
39051     config.layout.monitorWindowResize = false; // turn off autosizing
39052     this.layout = config.layout;
39053     this.layout.getEl().addClass("roo-layout-nested-layout");
39054     this.layout.parent = this;
39055     
39056     
39057     
39058     
39059 };
39060
39061 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
39062
39063     setSize : function(width, height){
39064         if(!this.ignoreResize(width, height)){
39065             var size = this.adjustForComponents(width, height);
39066             var el = this.layout.getEl();
39067             if (size.height < 1) {
39068                 el.setWidth(size.width);   
39069             } else {
39070                 el.setSize(size.width, size.height);
39071             }
39072             var touch = el.dom.offsetWidth;
39073             this.layout.layout();
39074             // ie requires a double layout on the first pass
39075             if(Roo.isIE && !this.initialized){
39076                 this.initialized = true;
39077                 this.layout.layout();
39078             }
39079         }
39080     },
39081     
39082     // activate all subpanels if not currently active..
39083     
39084     setActiveState : function(active){
39085         this.active = active;
39086         this.setActiveClass(active);
39087         
39088         if(!active){
39089             this.fireEvent("deactivate", this);
39090             return;
39091         }
39092         
39093         this.fireEvent("activate", this);
39094         // not sure if this should happen before or after..
39095         if (!this.layout) {
39096             return; // should not happen..
39097         }
39098         var reg = false;
39099         for (var r in this.layout.regions) {
39100             reg = this.layout.getRegion(r);
39101             if (reg.getActivePanel()) {
39102                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
39103                 reg.setActivePanel(reg.getActivePanel());
39104                 continue;
39105             }
39106             if (!reg.panels.length) {
39107                 continue;
39108             }
39109             reg.showPanel(reg.getPanel(0));
39110         }
39111         
39112         
39113         
39114         
39115     },
39116     
39117     /**
39118      * Returns the nested BorderLayout for this panel
39119      * @return {Roo.BorderLayout} 
39120      */
39121     getLayout : function(){
39122         return this.layout;
39123     },
39124     
39125      /**
39126      * Adds a xtype elements to the layout of the nested panel
39127      * <pre><code>
39128
39129 panel.addxtype({
39130        xtype : 'ContentPanel',
39131        region: 'west',
39132        items: [ .... ]
39133    }
39134 );
39135
39136 panel.addxtype({
39137         xtype : 'NestedLayoutPanel',
39138         region: 'west',
39139         layout: {
39140            center: { },
39141            west: { }   
39142         },
39143         items : [ ... list of content panels or nested layout panels.. ]
39144    }
39145 );
39146 </code></pre>
39147      * @param {Object} cfg Xtype definition of item to add.
39148      */
39149     addxtype : function(cfg) {
39150         return this.layout.addxtype(cfg);
39151     
39152     }
39153 });/*
39154  * Based on:
39155  * Ext JS Library 1.1.1
39156  * Copyright(c) 2006-2007, Ext JS, LLC.
39157  *
39158  * Originally Released Under LGPL - original licence link has changed is not relivant.
39159  *
39160  * Fork - LGPL
39161  * <script type="text/javascript">
39162  */
39163 /**
39164  * @class Roo.TabPanel
39165  * @extends Roo.util.Observable
39166  * A lightweight tab container.
39167  * <br><br>
39168  * Usage:
39169  * <pre><code>
39170 // basic tabs 1, built from existing content
39171 var tabs = new Roo.TabPanel("tabs1");
39172 tabs.addTab("script", "View Script");
39173 tabs.addTab("markup", "View Markup");
39174 tabs.activate("script");
39175
39176 // more advanced tabs, built from javascript
39177 var jtabs = new Roo.TabPanel("jtabs");
39178 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
39179
39180 // set up the UpdateManager
39181 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
39182 var updater = tab2.getUpdateManager();
39183 updater.setDefaultUrl("ajax1.htm");
39184 tab2.on('activate', updater.refresh, updater, true);
39185
39186 // Use setUrl for Ajax loading
39187 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
39188 tab3.setUrl("ajax2.htm", null, true);
39189
39190 // Disabled tab
39191 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
39192 tab4.disable();
39193
39194 jtabs.activate("jtabs-1");
39195  * </code></pre>
39196  * @constructor
39197  * Create a new TabPanel.
39198  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
39199  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
39200  */
39201 Roo.bootstrap.panel.Tabs = function(config){
39202     /**
39203     * The container element for this TabPanel.
39204     * @type Roo.Element
39205     */
39206     this.el = Roo.get(config.el);
39207     delete config.el;
39208     if(config){
39209         if(typeof config == "boolean"){
39210             this.tabPosition = config ? "bottom" : "top";
39211         }else{
39212             Roo.apply(this, config);
39213         }
39214     }
39215     
39216     if(this.tabPosition == "bottom"){
39217         // if tabs are at the bottom = create the body first.
39218         this.bodyEl = Roo.get(this.createBody(this.el.dom));
39219         this.el.addClass("roo-tabs-bottom");
39220     }
39221     // next create the tabs holders
39222     
39223     if (this.tabPosition == "west"){
39224         
39225         var reg = this.region; // fake it..
39226         while (reg) {
39227             if (!reg.mgr.parent) {
39228                 break;
39229             }
39230             reg = reg.mgr.parent.region;
39231         }
39232         Roo.log("got nest?");
39233         Roo.log(reg);
39234         if (reg.mgr.getRegion('west')) {
39235             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
39236             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
39237             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
39238             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
39239             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
39240         
39241             
39242         }
39243         
39244         
39245     } else {
39246      
39247         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
39248         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
39249         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
39250         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
39251     }
39252     
39253     
39254     if(Roo.isIE){
39255         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
39256     }
39257     
39258     // finally - if tabs are at the top, then create the body last..
39259     if(this.tabPosition != "bottom"){
39260         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
39261          * @type Roo.Element
39262          */
39263         this.bodyEl = Roo.get(this.createBody(this.el.dom));
39264         this.el.addClass("roo-tabs-top");
39265     }
39266     this.items = [];
39267
39268     this.bodyEl.setStyle("position", "relative");
39269
39270     this.active = null;
39271     this.activateDelegate = this.activate.createDelegate(this);
39272
39273     this.addEvents({
39274         /**
39275          * @event tabchange
39276          * Fires when the active tab changes
39277          * @param {Roo.TabPanel} this
39278          * @param {Roo.TabPanelItem} activePanel The new active tab
39279          */
39280         "tabchange": true,
39281         /**
39282          * @event beforetabchange
39283          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
39284          * @param {Roo.TabPanel} this
39285          * @param {Object} e Set cancel to true on this object to cancel the tab change
39286          * @param {Roo.TabPanelItem} tab The tab being changed to
39287          */
39288         "beforetabchange" : true
39289     });
39290
39291     Roo.EventManager.onWindowResize(this.onResize, this);
39292     this.cpad = this.el.getPadding("lr");
39293     this.hiddenCount = 0;
39294
39295
39296     // toolbar on the tabbar support...
39297     if (this.toolbar) {
39298         alert("no toolbar support yet");
39299         this.toolbar  = false;
39300         /*
39301         var tcfg = this.toolbar;
39302         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
39303         this.toolbar = new Roo.Toolbar(tcfg);
39304         if (Roo.isSafari) {
39305             var tbl = tcfg.container.child('table', true);
39306             tbl.setAttribute('width', '100%');
39307         }
39308         */
39309         
39310     }
39311    
39312
39313
39314     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
39315 };
39316
39317 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
39318     /*
39319      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
39320      */
39321     tabPosition : "top",
39322     /*
39323      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
39324      */
39325     currentTabWidth : 0,
39326     /*
39327      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
39328      */
39329     minTabWidth : 40,
39330     /*
39331      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
39332      */
39333     maxTabWidth : 250,
39334     /*
39335      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
39336      */
39337     preferredTabWidth : 175,
39338     /*
39339      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
39340      */
39341     resizeTabs : false,
39342     /*
39343      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
39344      */
39345     monitorResize : true,
39346     /*
39347      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
39348      */
39349     toolbar : false,  // set by caller..
39350     
39351     region : false, /// set by caller
39352     
39353     disableTooltips : true, // not used yet...
39354
39355     /**
39356      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
39357      * @param {String} id The id of the div to use <b>or create</b>
39358      * @param {String} text The text for the tab
39359      * @param {String} content (optional) Content to put in the TabPanelItem body
39360      * @param {Boolean} closable (optional) True to create a close icon on the tab
39361      * @return {Roo.TabPanelItem} The created TabPanelItem
39362      */
39363     addTab : function(id, text, content, closable, tpl)
39364     {
39365         var item = new Roo.bootstrap.panel.TabItem({
39366             panel: this,
39367             id : id,
39368             text : text,
39369             closable : closable,
39370             tpl : tpl
39371         });
39372         this.addTabItem(item);
39373         if(content){
39374             item.setContent(content);
39375         }
39376         return item;
39377     },
39378
39379     /**
39380      * Returns the {@link Roo.TabPanelItem} with the specified id/index
39381      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
39382      * @return {Roo.TabPanelItem}
39383      */
39384     getTab : function(id){
39385         return this.items[id];
39386     },
39387
39388     /**
39389      * Hides the {@link Roo.TabPanelItem} with the specified id/index
39390      * @param {String/Number} id The id or index of the TabPanelItem to hide.
39391      */
39392     hideTab : function(id){
39393         var t = this.items[id];
39394         if(!t.isHidden()){
39395            t.setHidden(true);
39396            this.hiddenCount++;
39397            this.autoSizeTabs();
39398         }
39399     },
39400
39401     /**
39402      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
39403      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
39404      */
39405     unhideTab : function(id){
39406         var t = this.items[id];
39407         if(t.isHidden()){
39408            t.setHidden(false);
39409            this.hiddenCount--;
39410            this.autoSizeTabs();
39411         }
39412     },
39413
39414     /**
39415      * Adds an existing {@link Roo.TabPanelItem}.
39416      * @param {Roo.TabPanelItem} item The TabPanelItem to add
39417      */
39418     addTabItem : function(item)
39419     {
39420         this.items[item.id] = item;
39421         this.items.push(item);
39422         this.autoSizeTabs();
39423       //  if(this.resizeTabs){
39424     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
39425   //         this.autoSizeTabs();
39426 //        }else{
39427 //            item.autoSize();
39428        // }
39429     },
39430
39431     /**
39432      * Removes a {@link Roo.TabPanelItem}.
39433      * @param {String/Number} id The id or index of the TabPanelItem to remove.
39434      */
39435     removeTab : function(id){
39436         var items = this.items;
39437         var tab = items[id];
39438         if(!tab) { return; }
39439         var index = items.indexOf(tab);
39440         if(this.active == tab && items.length > 1){
39441             var newTab = this.getNextAvailable(index);
39442             if(newTab) {
39443                 newTab.activate();
39444             }
39445         }
39446         this.stripEl.dom.removeChild(tab.pnode.dom);
39447         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
39448             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
39449         }
39450         items.splice(index, 1);
39451         delete this.items[tab.id];
39452         tab.fireEvent("close", tab);
39453         tab.purgeListeners();
39454         this.autoSizeTabs();
39455     },
39456
39457     getNextAvailable : function(start){
39458         var items = this.items;
39459         var index = start;
39460         // look for a next tab that will slide over to
39461         // replace the one being removed
39462         while(index < items.length){
39463             var item = items[++index];
39464             if(item && !item.isHidden()){
39465                 return item;
39466             }
39467         }
39468         // if one isn't found select the previous tab (on the left)
39469         index = start;
39470         while(index >= 0){
39471             var item = items[--index];
39472             if(item && !item.isHidden()){
39473                 return item;
39474             }
39475         }
39476         return null;
39477     },
39478
39479     /**
39480      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
39481      * @param {String/Number} id The id or index of the TabPanelItem to disable.
39482      */
39483     disableTab : function(id){
39484         var tab = this.items[id];
39485         if(tab && this.active != tab){
39486             tab.disable();
39487         }
39488     },
39489
39490     /**
39491      * Enables a {@link Roo.TabPanelItem} that is disabled.
39492      * @param {String/Number} id The id or index of the TabPanelItem to enable.
39493      */
39494     enableTab : function(id){
39495         var tab = this.items[id];
39496         tab.enable();
39497     },
39498
39499     /**
39500      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
39501      * @param {String/Number} id The id or index of the TabPanelItem to activate.
39502      * @return {Roo.TabPanelItem} The TabPanelItem.
39503      */
39504     activate : function(id)
39505     {
39506         //Roo.log('activite:'  + id);
39507         
39508         var tab = this.items[id];
39509         if(!tab){
39510             return null;
39511         }
39512         if(tab == this.active || tab.disabled){
39513             return tab;
39514         }
39515         var e = {};
39516         this.fireEvent("beforetabchange", this, e, tab);
39517         if(e.cancel !== true && !tab.disabled){
39518             if(this.active){
39519                 this.active.hide();
39520             }
39521             this.active = this.items[id];
39522             this.active.show();
39523             this.fireEvent("tabchange", this, this.active);
39524         }
39525         return tab;
39526     },
39527
39528     /**
39529      * Gets the active {@link Roo.TabPanelItem}.
39530      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
39531      */
39532     getActiveTab : function(){
39533         return this.active;
39534     },
39535
39536     /**
39537      * Updates the tab body element to fit the height of the container element
39538      * for overflow scrolling
39539      * @param {Number} targetHeight (optional) Override the starting height from the elements height
39540      */
39541     syncHeight : function(targetHeight){
39542         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
39543         var bm = this.bodyEl.getMargins();
39544         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
39545         this.bodyEl.setHeight(newHeight);
39546         return newHeight;
39547     },
39548
39549     onResize : function(){
39550         if(this.monitorResize){
39551             this.autoSizeTabs();
39552         }
39553     },
39554
39555     /**
39556      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
39557      */
39558     beginUpdate : function(){
39559         this.updating = true;
39560     },
39561
39562     /**
39563      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
39564      */
39565     endUpdate : function(){
39566         this.updating = false;
39567         this.autoSizeTabs();
39568     },
39569
39570     /**
39571      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
39572      */
39573     autoSizeTabs : function()
39574     {
39575         var count = this.items.length;
39576         var vcount = count - this.hiddenCount;
39577         
39578         if (vcount < 2) {
39579             this.stripEl.hide();
39580         } else {
39581             this.stripEl.show();
39582         }
39583         
39584         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
39585             return;
39586         }
39587         
39588         
39589         var w = Math.max(this.el.getWidth() - this.cpad, 10);
39590         var availWidth = Math.floor(w / vcount);
39591         var b = this.stripBody;
39592         if(b.getWidth() > w){
39593             var tabs = this.items;
39594             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
39595             if(availWidth < this.minTabWidth){
39596                 /*if(!this.sleft){    // incomplete scrolling code
39597                     this.createScrollButtons();
39598                 }
39599                 this.showScroll();
39600                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
39601             }
39602         }else{
39603             if(this.currentTabWidth < this.preferredTabWidth){
39604                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
39605             }
39606         }
39607     },
39608
39609     /**
39610      * Returns the number of tabs in this TabPanel.
39611      * @return {Number}
39612      */
39613      getCount : function(){
39614          return this.items.length;
39615      },
39616
39617     /**
39618      * Resizes all the tabs to the passed width
39619      * @param {Number} The new width
39620      */
39621     setTabWidth : function(width){
39622         this.currentTabWidth = width;
39623         for(var i = 0, len = this.items.length; i < len; i++) {
39624                 if(!this.items[i].isHidden()) {
39625                 this.items[i].setWidth(width);
39626             }
39627         }
39628     },
39629
39630     /**
39631      * Destroys this TabPanel
39632      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
39633      */
39634     destroy : function(removeEl){
39635         Roo.EventManager.removeResizeListener(this.onResize, this);
39636         for(var i = 0, len = this.items.length; i < len; i++){
39637             this.items[i].purgeListeners();
39638         }
39639         if(removeEl === true){
39640             this.el.update("");
39641             this.el.remove();
39642         }
39643     },
39644     
39645     createStrip : function(container)
39646     {
39647         var strip = document.createElement("nav");
39648         strip.className = Roo.bootstrap.version == 4 ?
39649             "navbar-light bg-light" : 
39650             "navbar navbar-default"; //"x-tabs-wrap";
39651         container.appendChild(strip);
39652         return strip;
39653     },
39654     
39655     createStripList : function(strip)
39656     {
39657         // div wrapper for retard IE
39658         // returns the "tr" element.
39659         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
39660         //'<div class="x-tabs-strip-wrap">'+
39661           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
39662           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
39663         return strip.firstChild; //.firstChild.firstChild.firstChild;
39664     },
39665     createBody : function(container)
39666     {
39667         var body = document.createElement("div");
39668         Roo.id(body, "tab-body");
39669         //Roo.fly(body).addClass("x-tabs-body");
39670         Roo.fly(body).addClass("tab-content");
39671         container.appendChild(body);
39672         return body;
39673     },
39674     createItemBody :function(bodyEl, id){
39675         var body = Roo.getDom(id);
39676         if(!body){
39677             body = document.createElement("div");
39678             body.id = id;
39679         }
39680         //Roo.fly(body).addClass("x-tabs-item-body");
39681         Roo.fly(body).addClass("tab-pane");
39682          bodyEl.insertBefore(body, bodyEl.firstChild);
39683         return body;
39684     },
39685     /** @private */
39686     createStripElements :  function(stripEl, text, closable, tpl)
39687     {
39688         var td = document.createElement("li"); // was td..
39689         td.className = 'nav-item';
39690         
39691         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
39692         
39693         
39694         stripEl.appendChild(td);
39695         /*if(closable){
39696             td.className = "x-tabs-closable";
39697             if(!this.closeTpl){
39698                 this.closeTpl = new Roo.Template(
39699                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
39700                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
39701                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
39702                 );
39703             }
39704             var el = this.closeTpl.overwrite(td, {"text": text});
39705             var close = el.getElementsByTagName("div")[0];
39706             var inner = el.getElementsByTagName("em")[0];
39707             return {"el": el, "close": close, "inner": inner};
39708         } else {
39709         */
39710         // not sure what this is..
39711 //            if(!this.tabTpl){
39712                 //this.tabTpl = new Roo.Template(
39713                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
39714                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
39715                 //);
39716 //                this.tabTpl = new Roo.Template(
39717 //                   '<a href="#">' +
39718 //                   '<span unselectable="on"' +
39719 //                            (this.disableTooltips ? '' : ' title="{text}"') +
39720 //                            ' >{text}</span></a>'
39721 //                );
39722 //                
39723 //            }
39724
39725
39726             var template = tpl || this.tabTpl || false;
39727             
39728             if(!template){
39729                 template =  new Roo.Template(
39730                         Roo.bootstrap.version == 4 ? 
39731                             (
39732                                 '<a class="nav-link" href="#" unselectable="on"' +
39733                                      (this.disableTooltips ? '' : ' title="{text}"') +
39734                                      ' >{text}</a>'
39735                             ) : (
39736                                 '<a class="nav-link" href="#">' +
39737                                 '<span unselectable="on"' +
39738                                          (this.disableTooltips ? '' : ' title="{text}"') +
39739                                     ' >{text}</span></a>'
39740                             )
39741                 );
39742             }
39743             
39744             switch (typeof(template)) {
39745                 case 'object' :
39746                     break;
39747                 case 'string' :
39748                     template = new Roo.Template(template);
39749                     break;
39750                 default :
39751                     break;
39752             }
39753             
39754             var el = template.overwrite(td, {"text": text});
39755             
39756             var inner = el.getElementsByTagName("span")[0];
39757             
39758             return {"el": el, "inner": inner};
39759             
39760     }
39761         
39762     
39763 });
39764
39765 /**
39766  * @class Roo.TabPanelItem
39767  * @extends Roo.util.Observable
39768  * Represents an individual item (tab plus body) in a TabPanel.
39769  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
39770  * @param {String} id The id of this TabPanelItem
39771  * @param {String} text The text for the tab of this TabPanelItem
39772  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
39773  */
39774 Roo.bootstrap.panel.TabItem = function(config){
39775     /**
39776      * The {@link Roo.TabPanel} this TabPanelItem belongs to
39777      * @type Roo.TabPanel
39778      */
39779     this.tabPanel = config.panel;
39780     /**
39781      * The id for this TabPanelItem
39782      * @type String
39783      */
39784     this.id = config.id;
39785     /** @private */
39786     this.disabled = false;
39787     /** @private */
39788     this.text = config.text;
39789     /** @private */
39790     this.loaded = false;
39791     this.closable = config.closable;
39792
39793     /**
39794      * The body element for this TabPanelItem.
39795      * @type Roo.Element
39796      */
39797     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
39798     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
39799     this.bodyEl.setStyle("display", "block");
39800     this.bodyEl.setStyle("zoom", "1");
39801     //this.hideAction();
39802
39803     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
39804     /** @private */
39805     this.el = Roo.get(els.el);
39806     this.inner = Roo.get(els.inner, true);
39807      this.textEl = Roo.bootstrap.version == 4 ?
39808         this.el : Roo.get(this.el.dom.firstChild, true);
39809
39810     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
39811     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
39812
39813     
39814 //    this.el.on("mousedown", this.onTabMouseDown, this);
39815     this.el.on("click", this.onTabClick, this);
39816     /** @private */
39817     if(config.closable){
39818         var c = Roo.get(els.close, true);
39819         c.dom.title = this.closeText;
39820         c.addClassOnOver("close-over");
39821         c.on("click", this.closeClick, this);
39822      }
39823
39824     this.addEvents({
39825          /**
39826          * @event activate
39827          * Fires when this tab becomes the active tab.
39828          * @param {Roo.TabPanel} tabPanel The parent TabPanel
39829          * @param {Roo.TabPanelItem} this
39830          */
39831         "activate": true,
39832         /**
39833          * @event beforeclose
39834          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
39835          * @param {Roo.TabPanelItem} this
39836          * @param {Object} e Set cancel to true on this object to cancel the close.
39837          */
39838         "beforeclose": true,
39839         /**
39840          * @event close
39841          * Fires when this tab is closed.
39842          * @param {Roo.TabPanelItem} this
39843          */
39844          "close": true,
39845         /**
39846          * @event deactivate
39847          * Fires when this tab is no longer the active tab.
39848          * @param {Roo.TabPanel} tabPanel The parent TabPanel
39849          * @param {Roo.TabPanelItem} this
39850          */
39851          "deactivate" : true
39852     });
39853     this.hidden = false;
39854
39855     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
39856 };
39857
39858 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
39859            {
39860     purgeListeners : function(){
39861        Roo.util.Observable.prototype.purgeListeners.call(this);
39862        this.el.removeAllListeners();
39863     },
39864     /**
39865      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
39866      */
39867     show : function(){
39868         this.status_node.addClass("active");
39869         this.showAction();
39870         if(Roo.isOpera){
39871             this.tabPanel.stripWrap.repaint();
39872         }
39873         this.fireEvent("activate", this.tabPanel, this);
39874     },
39875
39876     /**
39877      * Returns true if this tab is the active tab.
39878      * @return {Boolean}
39879      */
39880     isActive : function(){
39881         return this.tabPanel.getActiveTab() == this;
39882     },
39883
39884     /**
39885      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
39886      */
39887     hide : function(){
39888         this.status_node.removeClass("active");
39889         this.hideAction();
39890         this.fireEvent("deactivate", this.tabPanel, this);
39891     },
39892
39893     hideAction : function(){
39894         this.bodyEl.hide();
39895         this.bodyEl.setStyle("position", "absolute");
39896         this.bodyEl.setLeft("-20000px");
39897         this.bodyEl.setTop("-20000px");
39898     },
39899
39900     showAction : function(){
39901         this.bodyEl.setStyle("position", "relative");
39902         this.bodyEl.setTop("");
39903         this.bodyEl.setLeft("");
39904         this.bodyEl.show();
39905     },
39906
39907     /**
39908      * Set the tooltip for the tab.
39909      * @param {String} tooltip The tab's tooltip
39910      */
39911     setTooltip : function(text){
39912         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
39913             this.textEl.dom.qtip = text;
39914             this.textEl.dom.removeAttribute('title');
39915         }else{
39916             this.textEl.dom.title = text;
39917         }
39918     },
39919
39920     onTabClick : function(e){
39921         e.preventDefault();
39922         this.tabPanel.activate(this.id);
39923     },
39924
39925     onTabMouseDown : function(e){
39926         e.preventDefault();
39927         this.tabPanel.activate(this.id);
39928     },
39929 /*
39930     getWidth : function(){
39931         return this.inner.getWidth();
39932     },
39933
39934     setWidth : function(width){
39935         var iwidth = width - this.linode.getPadding("lr");
39936         this.inner.setWidth(iwidth);
39937         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
39938         this.linode.setWidth(width);
39939     },
39940 */
39941     /**
39942      * Show or hide the tab
39943      * @param {Boolean} hidden True to hide or false to show.
39944      */
39945     setHidden : function(hidden){
39946         this.hidden = hidden;
39947         this.linode.setStyle("display", hidden ? "none" : "");
39948     },
39949
39950     /**
39951      * Returns true if this tab is "hidden"
39952      * @return {Boolean}
39953      */
39954     isHidden : function(){
39955         return this.hidden;
39956     },
39957
39958     /**
39959      * Returns the text for this tab
39960      * @return {String}
39961      */
39962     getText : function(){
39963         return this.text;
39964     },
39965     /*
39966     autoSize : function(){
39967         //this.el.beginMeasure();
39968         this.textEl.setWidth(1);
39969         /*
39970          *  #2804 [new] Tabs in Roojs
39971          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
39972          */
39973         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
39974         //this.el.endMeasure();
39975     //},
39976
39977     /**
39978      * Sets the text for the tab (Note: this also sets the tooltip text)
39979      * @param {String} text The tab's text and tooltip
39980      */
39981     setText : function(text){
39982         this.text = text;
39983         this.textEl.update(text);
39984         this.setTooltip(text);
39985         //if(!this.tabPanel.resizeTabs){
39986         //    this.autoSize();
39987         //}
39988     },
39989     /**
39990      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
39991      */
39992     activate : function(){
39993         this.tabPanel.activate(this.id);
39994     },
39995
39996     /**
39997      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
39998      */
39999     disable : function(){
40000         if(this.tabPanel.active != this){
40001             this.disabled = true;
40002             this.status_node.addClass("disabled");
40003         }
40004     },
40005
40006     /**
40007      * Enables this TabPanelItem if it was previously disabled.
40008      */
40009     enable : function(){
40010         this.disabled = false;
40011         this.status_node.removeClass("disabled");
40012     },
40013
40014     /**
40015      * Sets the content for this TabPanelItem.
40016      * @param {String} content The content
40017      * @param {Boolean} loadScripts true to look for and load scripts
40018      */
40019     setContent : function(content, loadScripts){
40020         this.bodyEl.update(content, loadScripts);
40021     },
40022
40023     /**
40024      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
40025      * @return {Roo.UpdateManager} The UpdateManager
40026      */
40027     getUpdateManager : function(){
40028         return this.bodyEl.getUpdateManager();
40029     },
40030
40031     /**
40032      * Set a URL to be used to load the content for this TabPanelItem.
40033      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
40034      * @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)
40035      * @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)
40036      * @return {Roo.UpdateManager} The UpdateManager
40037      */
40038     setUrl : function(url, params, loadOnce){
40039         if(this.refreshDelegate){
40040             this.un('activate', this.refreshDelegate);
40041         }
40042         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40043         this.on("activate", this.refreshDelegate);
40044         return this.bodyEl.getUpdateManager();
40045     },
40046
40047     /** @private */
40048     _handleRefresh : function(url, params, loadOnce){
40049         if(!loadOnce || !this.loaded){
40050             var updater = this.bodyEl.getUpdateManager();
40051             updater.update(url, params, this._setLoaded.createDelegate(this));
40052         }
40053     },
40054
40055     /**
40056      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
40057      *   Will fail silently if the setUrl method has not been called.
40058      *   This does not activate the panel, just updates its content.
40059      */
40060     refresh : function(){
40061         if(this.refreshDelegate){
40062            this.loaded = false;
40063            this.refreshDelegate();
40064         }
40065     },
40066
40067     /** @private */
40068     _setLoaded : function(){
40069         this.loaded = true;
40070     },
40071
40072     /** @private */
40073     closeClick : function(e){
40074         var o = {};
40075         e.stopEvent();
40076         this.fireEvent("beforeclose", this, o);
40077         if(o.cancel !== true){
40078             this.tabPanel.removeTab(this.id);
40079         }
40080     },
40081     /**
40082      * The text displayed in the tooltip for the close icon.
40083      * @type String
40084      */
40085     closeText : "Close this tab"
40086 });
40087 /**
40088 *    This script refer to:
40089 *    Title: International Telephone Input
40090 *    Author: Jack O'Connor
40091 *    Code version:  v12.1.12
40092 *    Availability: https://github.com/jackocnr/intl-tel-input.git
40093 **/
40094
40095 Roo.bootstrap.PhoneInputData = function() {
40096     var d = [
40097       [
40098         "Afghanistan (‫افغانستان‬‎)",
40099         "af",
40100         "93"
40101       ],
40102       [
40103         "Albania (Shqipëri)",
40104         "al",
40105         "355"
40106       ],
40107       [
40108         "Algeria (‫الجزائر‬‎)",
40109         "dz",
40110         "213"
40111       ],
40112       [
40113         "American Samoa",
40114         "as",
40115         "1684"
40116       ],
40117       [
40118         "Andorra",
40119         "ad",
40120         "376"
40121       ],
40122       [
40123         "Angola",
40124         "ao",
40125         "244"
40126       ],
40127       [
40128         "Anguilla",
40129         "ai",
40130         "1264"
40131       ],
40132       [
40133         "Antigua and Barbuda",
40134         "ag",
40135         "1268"
40136       ],
40137       [
40138         "Argentina",
40139         "ar",
40140         "54"
40141       ],
40142       [
40143         "Armenia (Հայաստան)",
40144         "am",
40145         "374"
40146       ],
40147       [
40148         "Aruba",
40149         "aw",
40150         "297"
40151       ],
40152       [
40153         "Australia",
40154         "au",
40155         "61",
40156         0
40157       ],
40158       [
40159         "Austria (Österreich)",
40160         "at",
40161         "43"
40162       ],
40163       [
40164         "Azerbaijan (Azərbaycan)",
40165         "az",
40166         "994"
40167       ],
40168       [
40169         "Bahamas",
40170         "bs",
40171         "1242"
40172       ],
40173       [
40174         "Bahrain (‫البحرين‬‎)",
40175         "bh",
40176         "973"
40177       ],
40178       [
40179         "Bangladesh (বাংলাদেশ)",
40180         "bd",
40181         "880"
40182       ],
40183       [
40184         "Barbados",
40185         "bb",
40186         "1246"
40187       ],
40188       [
40189         "Belarus (Беларусь)",
40190         "by",
40191         "375"
40192       ],
40193       [
40194         "Belgium (België)",
40195         "be",
40196         "32"
40197       ],
40198       [
40199         "Belize",
40200         "bz",
40201         "501"
40202       ],
40203       [
40204         "Benin (Bénin)",
40205         "bj",
40206         "229"
40207       ],
40208       [
40209         "Bermuda",
40210         "bm",
40211         "1441"
40212       ],
40213       [
40214         "Bhutan (འབྲུག)",
40215         "bt",
40216         "975"
40217       ],
40218       [
40219         "Bolivia",
40220         "bo",
40221         "591"
40222       ],
40223       [
40224         "Bosnia and Herzegovina (Босна и Херцеговина)",
40225         "ba",
40226         "387"
40227       ],
40228       [
40229         "Botswana",
40230         "bw",
40231         "267"
40232       ],
40233       [
40234         "Brazil (Brasil)",
40235         "br",
40236         "55"
40237       ],
40238       [
40239         "British Indian Ocean Territory",
40240         "io",
40241         "246"
40242       ],
40243       [
40244         "British Virgin Islands",
40245         "vg",
40246         "1284"
40247       ],
40248       [
40249         "Brunei",
40250         "bn",
40251         "673"
40252       ],
40253       [
40254         "Bulgaria (България)",
40255         "bg",
40256         "359"
40257       ],
40258       [
40259         "Burkina Faso",
40260         "bf",
40261         "226"
40262       ],
40263       [
40264         "Burundi (Uburundi)",
40265         "bi",
40266         "257"
40267       ],
40268       [
40269         "Cambodia (កម្ពុជា)",
40270         "kh",
40271         "855"
40272       ],
40273       [
40274         "Cameroon (Cameroun)",
40275         "cm",
40276         "237"
40277       ],
40278       [
40279         "Canada",
40280         "ca",
40281         "1",
40282         1,
40283         ["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"]
40284       ],
40285       [
40286         "Cape Verde (Kabu Verdi)",
40287         "cv",
40288         "238"
40289       ],
40290       [
40291         "Caribbean Netherlands",
40292         "bq",
40293         "599",
40294         1
40295       ],
40296       [
40297         "Cayman Islands",
40298         "ky",
40299         "1345"
40300       ],
40301       [
40302         "Central African Republic (République centrafricaine)",
40303         "cf",
40304         "236"
40305       ],
40306       [
40307         "Chad (Tchad)",
40308         "td",
40309         "235"
40310       ],
40311       [
40312         "Chile",
40313         "cl",
40314         "56"
40315       ],
40316       [
40317         "China (中国)",
40318         "cn",
40319         "86"
40320       ],
40321       [
40322         "Christmas Island",
40323         "cx",
40324         "61",
40325         2
40326       ],
40327       [
40328         "Cocos (Keeling) Islands",
40329         "cc",
40330         "61",
40331         1
40332       ],
40333       [
40334         "Colombia",
40335         "co",
40336         "57"
40337       ],
40338       [
40339         "Comoros (‫جزر القمر‬‎)",
40340         "km",
40341         "269"
40342       ],
40343       [
40344         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
40345         "cd",
40346         "243"
40347       ],
40348       [
40349         "Congo (Republic) (Congo-Brazzaville)",
40350         "cg",
40351         "242"
40352       ],
40353       [
40354         "Cook Islands",
40355         "ck",
40356         "682"
40357       ],
40358       [
40359         "Costa Rica",
40360         "cr",
40361         "506"
40362       ],
40363       [
40364         "Côte d’Ivoire",
40365         "ci",
40366         "225"
40367       ],
40368       [
40369         "Croatia (Hrvatska)",
40370         "hr",
40371         "385"
40372       ],
40373       [
40374         "Cuba",
40375         "cu",
40376         "53"
40377       ],
40378       [
40379         "Curaçao",
40380         "cw",
40381         "599",
40382         0
40383       ],
40384       [
40385         "Cyprus (Κύπρος)",
40386         "cy",
40387         "357"
40388       ],
40389       [
40390         "Czech Republic (Česká republika)",
40391         "cz",
40392         "420"
40393       ],
40394       [
40395         "Denmark (Danmark)",
40396         "dk",
40397         "45"
40398       ],
40399       [
40400         "Djibouti",
40401         "dj",
40402         "253"
40403       ],
40404       [
40405         "Dominica",
40406         "dm",
40407         "1767"
40408       ],
40409       [
40410         "Dominican Republic (República Dominicana)",
40411         "do",
40412         "1",
40413         2,
40414         ["809", "829", "849"]
40415       ],
40416       [
40417         "Ecuador",
40418         "ec",
40419         "593"
40420       ],
40421       [
40422         "Egypt (‫مصر‬‎)",
40423         "eg",
40424         "20"
40425       ],
40426       [
40427         "El Salvador",
40428         "sv",
40429         "503"
40430       ],
40431       [
40432         "Equatorial Guinea (Guinea Ecuatorial)",
40433         "gq",
40434         "240"
40435       ],
40436       [
40437         "Eritrea",
40438         "er",
40439         "291"
40440       ],
40441       [
40442         "Estonia (Eesti)",
40443         "ee",
40444         "372"
40445       ],
40446       [
40447         "Ethiopia",
40448         "et",
40449         "251"
40450       ],
40451       [
40452         "Falkland Islands (Islas Malvinas)",
40453         "fk",
40454         "500"
40455       ],
40456       [
40457         "Faroe Islands (Føroyar)",
40458         "fo",
40459         "298"
40460       ],
40461       [
40462         "Fiji",
40463         "fj",
40464         "679"
40465       ],
40466       [
40467         "Finland (Suomi)",
40468         "fi",
40469         "358",
40470         0
40471       ],
40472       [
40473         "France",
40474         "fr",
40475         "33"
40476       ],
40477       [
40478         "French Guiana (Guyane française)",
40479         "gf",
40480         "594"
40481       ],
40482       [
40483         "French Polynesia (Polynésie française)",
40484         "pf",
40485         "689"
40486       ],
40487       [
40488         "Gabon",
40489         "ga",
40490         "241"
40491       ],
40492       [
40493         "Gambia",
40494         "gm",
40495         "220"
40496       ],
40497       [
40498         "Georgia (საქართველო)",
40499         "ge",
40500         "995"
40501       ],
40502       [
40503         "Germany (Deutschland)",
40504         "de",
40505         "49"
40506       ],
40507       [
40508         "Ghana (Gaana)",
40509         "gh",
40510         "233"
40511       ],
40512       [
40513         "Gibraltar",
40514         "gi",
40515         "350"
40516       ],
40517       [
40518         "Greece (Ελλάδα)",
40519         "gr",
40520         "30"
40521       ],
40522       [
40523         "Greenland (Kalaallit Nunaat)",
40524         "gl",
40525         "299"
40526       ],
40527       [
40528         "Grenada",
40529         "gd",
40530         "1473"
40531       ],
40532       [
40533         "Guadeloupe",
40534         "gp",
40535         "590",
40536         0
40537       ],
40538       [
40539         "Guam",
40540         "gu",
40541         "1671"
40542       ],
40543       [
40544         "Guatemala",
40545         "gt",
40546         "502"
40547       ],
40548       [
40549         "Guernsey",
40550         "gg",
40551         "44",
40552         1
40553       ],
40554       [
40555         "Guinea (Guinée)",
40556         "gn",
40557         "224"
40558       ],
40559       [
40560         "Guinea-Bissau (Guiné Bissau)",
40561         "gw",
40562         "245"
40563       ],
40564       [
40565         "Guyana",
40566         "gy",
40567         "592"
40568       ],
40569       [
40570         "Haiti",
40571         "ht",
40572         "509"
40573       ],
40574       [
40575         "Honduras",
40576         "hn",
40577         "504"
40578       ],
40579       [
40580         "Hong Kong (香港)",
40581         "hk",
40582         "852"
40583       ],
40584       [
40585         "Hungary (Magyarország)",
40586         "hu",
40587         "36"
40588       ],
40589       [
40590         "Iceland (Ísland)",
40591         "is",
40592         "354"
40593       ],
40594       [
40595         "India (भारत)",
40596         "in",
40597         "91"
40598       ],
40599       [
40600         "Indonesia",
40601         "id",
40602         "62"
40603       ],
40604       [
40605         "Iran (‫ایران‬‎)",
40606         "ir",
40607         "98"
40608       ],
40609       [
40610         "Iraq (‫العراق‬‎)",
40611         "iq",
40612         "964"
40613       ],
40614       [
40615         "Ireland",
40616         "ie",
40617         "353"
40618       ],
40619       [
40620         "Isle of Man",
40621         "im",
40622         "44",
40623         2
40624       ],
40625       [
40626         "Israel (‫ישראל‬‎)",
40627         "il",
40628         "972"
40629       ],
40630       [
40631         "Italy (Italia)",
40632         "it",
40633         "39",
40634         0
40635       ],
40636       [
40637         "Jamaica",
40638         "jm",
40639         "1876"
40640       ],
40641       [
40642         "Japan (日本)",
40643         "jp",
40644         "81"
40645       ],
40646       [
40647         "Jersey",
40648         "je",
40649         "44",
40650         3
40651       ],
40652       [
40653         "Jordan (‫الأردن‬‎)",
40654         "jo",
40655         "962"
40656       ],
40657       [
40658         "Kazakhstan (Казахстан)",
40659         "kz",
40660         "7",
40661         1
40662       ],
40663       [
40664         "Kenya",
40665         "ke",
40666         "254"
40667       ],
40668       [
40669         "Kiribati",
40670         "ki",
40671         "686"
40672       ],
40673       [
40674         "Kosovo",
40675         "xk",
40676         "383"
40677       ],
40678       [
40679         "Kuwait (‫الكويت‬‎)",
40680         "kw",
40681         "965"
40682       ],
40683       [
40684         "Kyrgyzstan (Кыргызстан)",
40685         "kg",
40686         "996"
40687       ],
40688       [
40689         "Laos (ລາວ)",
40690         "la",
40691         "856"
40692       ],
40693       [
40694         "Latvia (Latvija)",
40695         "lv",
40696         "371"
40697       ],
40698       [
40699         "Lebanon (‫لبنان‬‎)",
40700         "lb",
40701         "961"
40702       ],
40703       [
40704         "Lesotho",
40705         "ls",
40706         "266"
40707       ],
40708       [
40709         "Liberia",
40710         "lr",
40711         "231"
40712       ],
40713       [
40714         "Libya (‫ليبيا‬‎)",
40715         "ly",
40716         "218"
40717       ],
40718       [
40719         "Liechtenstein",
40720         "li",
40721         "423"
40722       ],
40723       [
40724         "Lithuania (Lietuva)",
40725         "lt",
40726         "370"
40727       ],
40728       [
40729         "Luxembourg",
40730         "lu",
40731         "352"
40732       ],
40733       [
40734         "Macau (澳門)",
40735         "mo",
40736         "853"
40737       ],
40738       [
40739         "Macedonia (FYROM) (Македонија)",
40740         "mk",
40741         "389"
40742       ],
40743       [
40744         "Madagascar (Madagasikara)",
40745         "mg",
40746         "261"
40747       ],
40748       [
40749         "Malawi",
40750         "mw",
40751         "265"
40752       ],
40753       [
40754         "Malaysia",
40755         "my",
40756         "60"
40757       ],
40758       [
40759         "Maldives",
40760         "mv",
40761         "960"
40762       ],
40763       [
40764         "Mali",
40765         "ml",
40766         "223"
40767       ],
40768       [
40769         "Malta",
40770         "mt",
40771         "356"
40772       ],
40773       [
40774         "Marshall Islands",
40775         "mh",
40776         "692"
40777       ],
40778       [
40779         "Martinique",
40780         "mq",
40781         "596"
40782       ],
40783       [
40784         "Mauritania (‫موريتانيا‬‎)",
40785         "mr",
40786         "222"
40787       ],
40788       [
40789         "Mauritius (Moris)",
40790         "mu",
40791         "230"
40792       ],
40793       [
40794         "Mayotte",
40795         "yt",
40796         "262",
40797         1
40798       ],
40799       [
40800         "Mexico (México)",
40801         "mx",
40802         "52"
40803       ],
40804       [
40805         "Micronesia",
40806         "fm",
40807         "691"
40808       ],
40809       [
40810         "Moldova (Republica Moldova)",
40811         "md",
40812         "373"
40813       ],
40814       [
40815         "Monaco",
40816         "mc",
40817         "377"
40818       ],
40819       [
40820         "Mongolia (Монгол)",
40821         "mn",
40822         "976"
40823       ],
40824       [
40825         "Montenegro (Crna Gora)",
40826         "me",
40827         "382"
40828       ],
40829       [
40830         "Montserrat",
40831         "ms",
40832         "1664"
40833       ],
40834       [
40835         "Morocco (‫المغرب‬‎)",
40836         "ma",
40837         "212",
40838         0
40839       ],
40840       [
40841         "Mozambique (Moçambique)",
40842         "mz",
40843         "258"
40844       ],
40845       [
40846         "Myanmar (Burma) (မြန်မာ)",
40847         "mm",
40848         "95"
40849       ],
40850       [
40851         "Namibia (Namibië)",
40852         "na",
40853         "264"
40854       ],
40855       [
40856         "Nauru",
40857         "nr",
40858         "674"
40859       ],
40860       [
40861         "Nepal (नेपाल)",
40862         "np",
40863         "977"
40864       ],
40865       [
40866         "Netherlands (Nederland)",
40867         "nl",
40868         "31"
40869       ],
40870       [
40871         "New Caledonia (Nouvelle-Calédonie)",
40872         "nc",
40873         "687"
40874       ],
40875       [
40876         "New Zealand",
40877         "nz",
40878         "64"
40879       ],
40880       [
40881         "Nicaragua",
40882         "ni",
40883         "505"
40884       ],
40885       [
40886         "Niger (Nijar)",
40887         "ne",
40888         "227"
40889       ],
40890       [
40891         "Nigeria",
40892         "ng",
40893         "234"
40894       ],
40895       [
40896         "Niue",
40897         "nu",
40898         "683"
40899       ],
40900       [
40901         "Norfolk Island",
40902         "nf",
40903         "672"
40904       ],
40905       [
40906         "North Korea (조선 민주주의 인민 공화국)",
40907         "kp",
40908         "850"
40909       ],
40910       [
40911         "Northern Mariana Islands",
40912         "mp",
40913         "1670"
40914       ],
40915       [
40916         "Norway (Norge)",
40917         "no",
40918         "47",
40919         0
40920       ],
40921       [
40922         "Oman (‫عُمان‬‎)",
40923         "om",
40924         "968"
40925       ],
40926       [
40927         "Pakistan (‫پاکستان‬‎)",
40928         "pk",
40929         "92"
40930       ],
40931       [
40932         "Palau",
40933         "pw",
40934         "680"
40935       ],
40936       [
40937         "Palestine (‫فلسطين‬‎)",
40938         "ps",
40939         "970"
40940       ],
40941       [
40942         "Panama (Panamá)",
40943         "pa",
40944         "507"
40945       ],
40946       [
40947         "Papua New Guinea",
40948         "pg",
40949         "675"
40950       ],
40951       [
40952         "Paraguay",
40953         "py",
40954         "595"
40955       ],
40956       [
40957         "Peru (Perú)",
40958         "pe",
40959         "51"
40960       ],
40961       [
40962         "Philippines",
40963         "ph",
40964         "63"
40965       ],
40966       [
40967         "Poland (Polska)",
40968         "pl",
40969         "48"
40970       ],
40971       [
40972         "Portugal",
40973         "pt",
40974         "351"
40975       ],
40976       [
40977         "Puerto Rico",
40978         "pr",
40979         "1",
40980         3,
40981         ["787", "939"]
40982       ],
40983       [
40984         "Qatar (‫قطر‬‎)",
40985         "qa",
40986         "974"
40987       ],
40988       [
40989         "Réunion (La Réunion)",
40990         "re",
40991         "262",
40992         0
40993       ],
40994       [
40995         "Romania (România)",
40996         "ro",
40997         "40"
40998       ],
40999       [
41000         "Russia (Россия)",
41001         "ru",
41002         "7",
41003         0
41004       ],
41005       [
41006         "Rwanda",
41007         "rw",
41008         "250"
41009       ],
41010       [
41011         "Saint Barthélemy",
41012         "bl",
41013         "590",
41014         1
41015       ],
41016       [
41017         "Saint Helena",
41018         "sh",
41019         "290"
41020       ],
41021       [
41022         "Saint Kitts and Nevis",
41023         "kn",
41024         "1869"
41025       ],
41026       [
41027         "Saint Lucia",
41028         "lc",
41029         "1758"
41030       ],
41031       [
41032         "Saint Martin (Saint-Martin (partie française))",
41033         "mf",
41034         "590",
41035         2
41036       ],
41037       [
41038         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
41039         "pm",
41040         "508"
41041       ],
41042       [
41043         "Saint Vincent and the Grenadines",
41044         "vc",
41045         "1784"
41046       ],
41047       [
41048         "Samoa",
41049         "ws",
41050         "685"
41051       ],
41052       [
41053         "San Marino",
41054         "sm",
41055         "378"
41056       ],
41057       [
41058         "São Tomé and Príncipe (São Tomé e Príncipe)",
41059         "st",
41060         "239"
41061       ],
41062       [
41063         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
41064         "sa",
41065         "966"
41066       ],
41067       [
41068         "Senegal (Sénégal)",
41069         "sn",
41070         "221"
41071       ],
41072       [
41073         "Serbia (Србија)",
41074         "rs",
41075         "381"
41076       ],
41077       [
41078         "Seychelles",
41079         "sc",
41080         "248"
41081       ],
41082       [
41083         "Sierra Leone",
41084         "sl",
41085         "232"
41086       ],
41087       [
41088         "Singapore",
41089         "sg",
41090         "65"
41091       ],
41092       [
41093         "Sint Maarten",
41094         "sx",
41095         "1721"
41096       ],
41097       [
41098         "Slovakia (Slovensko)",
41099         "sk",
41100         "421"
41101       ],
41102       [
41103         "Slovenia (Slovenija)",
41104         "si",
41105         "386"
41106       ],
41107       [
41108         "Solomon Islands",
41109         "sb",
41110         "677"
41111       ],
41112       [
41113         "Somalia (Soomaaliya)",
41114         "so",
41115         "252"
41116       ],
41117       [
41118         "South Africa",
41119         "za",
41120         "27"
41121       ],
41122       [
41123         "South Korea (대한민국)",
41124         "kr",
41125         "82"
41126       ],
41127       [
41128         "South Sudan (‫جنوب السودان‬‎)",
41129         "ss",
41130         "211"
41131       ],
41132       [
41133         "Spain (España)",
41134         "es",
41135         "34"
41136       ],
41137       [
41138         "Sri Lanka (ශ්‍රී ලංකාව)",
41139         "lk",
41140         "94"
41141       ],
41142       [
41143         "Sudan (‫السودان‬‎)",
41144         "sd",
41145         "249"
41146       ],
41147       [
41148         "Suriname",
41149         "sr",
41150         "597"
41151       ],
41152       [
41153         "Svalbard and Jan Mayen",
41154         "sj",
41155         "47",
41156         1
41157       ],
41158       [
41159         "Swaziland",
41160         "sz",
41161         "268"
41162       ],
41163       [
41164         "Sweden (Sverige)",
41165         "se",
41166         "46"
41167       ],
41168       [
41169         "Switzerland (Schweiz)",
41170         "ch",
41171         "41"
41172       ],
41173       [
41174         "Syria (‫سوريا‬‎)",
41175         "sy",
41176         "963"
41177       ],
41178       [
41179         "Taiwan (台灣)",
41180         "tw",
41181         "886"
41182       ],
41183       [
41184         "Tajikistan",
41185         "tj",
41186         "992"
41187       ],
41188       [
41189         "Tanzania",
41190         "tz",
41191         "255"
41192       ],
41193       [
41194         "Thailand (ไทย)",
41195         "th",
41196         "66"
41197       ],
41198       [
41199         "Timor-Leste",
41200         "tl",
41201         "670"
41202       ],
41203       [
41204         "Togo",
41205         "tg",
41206         "228"
41207       ],
41208       [
41209         "Tokelau",
41210         "tk",
41211         "690"
41212       ],
41213       [
41214         "Tonga",
41215         "to",
41216         "676"
41217       ],
41218       [
41219         "Trinidad and Tobago",
41220         "tt",
41221         "1868"
41222       ],
41223       [
41224         "Tunisia (‫تونس‬‎)",
41225         "tn",
41226         "216"
41227       ],
41228       [
41229         "Turkey (Türkiye)",
41230         "tr",
41231         "90"
41232       ],
41233       [
41234         "Turkmenistan",
41235         "tm",
41236         "993"
41237       ],
41238       [
41239         "Turks and Caicos Islands",
41240         "tc",
41241         "1649"
41242       ],
41243       [
41244         "Tuvalu",
41245         "tv",
41246         "688"
41247       ],
41248       [
41249         "U.S. Virgin Islands",
41250         "vi",
41251         "1340"
41252       ],
41253       [
41254         "Uganda",
41255         "ug",
41256         "256"
41257       ],
41258       [
41259         "Ukraine (Україна)",
41260         "ua",
41261         "380"
41262       ],
41263       [
41264         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
41265         "ae",
41266         "971"
41267       ],
41268       [
41269         "United Kingdom",
41270         "gb",
41271         "44",
41272         0
41273       ],
41274       [
41275         "United States",
41276         "us",
41277         "1",
41278         0
41279       ],
41280       [
41281         "Uruguay",
41282         "uy",
41283         "598"
41284       ],
41285       [
41286         "Uzbekistan (Oʻzbekiston)",
41287         "uz",
41288         "998"
41289       ],
41290       [
41291         "Vanuatu",
41292         "vu",
41293         "678"
41294       ],
41295       [
41296         "Vatican City (Città del Vaticano)",
41297         "va",
41298         "39",
41299         1
41300       ],
41301       [
41302         "Venezuela",
41303         "ve",
41304         "58"
41305       ],
41306       [
41307         "Vietnam (Việt Nam)",
41308         "vn",
41309         "84"
41310       ],
41311       [
41312         "Wallis and Futuna (Wallis-et-Futuna)",
41313         "wf",
41314         "681"
41315       ],
41316       [
41317         "Western Sahara (‫الصحراء الغربية‬‎)",
41318         "eh",
41319         "212",
41320         1
41321       ],
41322       [
41323         "Yemen (‫اليمن‬‎)",
41324         "ye",
41325         "967"
41326       ],
41327       [
41328         "Zambia",
41329         "zm",
41330         "260"
41331       ],
41332       [
41333         "Zimbabwe",
41334         "zw",
41335         "263"
41336       ],
41337       [
41338         "Åland Islands",
41339         "ax",
41340         "358",
41341         1
41342       ]
41343   ];
41344   
41345   return d;
41346 }/**
41347 *    This script refer to:
41348 *    Title: International Telephone Input
41349 *    Author: Jack O'Connor
41350 *    Code version:  v12.1.12
41351 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41352 **/
41353
41354 /**
41355  * @class Roo.bootstrap.PhoneInput
41356  * @extends Roo.bootstrap.TriggerField
41357  * An input with International dial-code selection
41358  
41359  * @cfg {String} defaultDialCode default '+852'
41360  * @cfg {Array} preferedCountries default []
41361   
41362  * @constructor
41363  * Create a new PhoneInput.
41364  * @param {Object} config Configuration options
41365  */
41366
41367 Roo.bootstrap.PhoneInput = function(config) {
41368     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
41369 };
41370
41371 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
41372         
41373         listWidth: undefined,
41374         
41375         selectedClass: 'active',
41376         
41377         invalidClass : "has-warning",
41378         
41379         validClass: 'has-success',
41380         
41381         allowed: '0123456789',
41382         
41383         max_length: 15,
41384         
41385         /**
41386          * @cfg {String} defaultDialCode The default dial code when initializing the input
41387          */
41388         defaultDialCode: '+852',
41389         
41390         /**
41391          * @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
41392          */
41393         preferedCountries: false,
41394         
41395         getAutoCreate : function()
41396         {
41397             var data = Roo.bootstrap.PhoneInputData();
41398             var align = this.labelAlign || this.parentLabelAlign();
41399             var id = Roo.id();
41400             
41401             this.allCountries = [];
41402             this.dialCodeMapping = [];
41403             
41404             for (var i = 0; i < data.length; i++) {
41405               var c = data[i];
41406               this.allCountries[i] = {
41407                 name: c[0],
41408                 iso2: c[1],
41409                 dialCode: c[2],
41410                 priority: c[3] || 0,
41411                 areaCodes: c[4] || null
41412               };
41413               this.dialCodeMapping[c[2]] = {
41414                   name: c[0],
41415                   iso2: c[1],
41416                   priority: c[3] || 0,
41417                   areaCodes: c[4] || null
41418               };
41419             }
41420             
41421             var cfg = {
41422                 cls: 'form-group',
41423                 cn: []
41424             };
41425             
41426             var input =  {
41427                 tag: 'input',
41428                 id : id,
41429                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
41430                 maxlength: this.max_length,
41431                 cls : 'form-control tel-input',
41432                 autocomplete: 'new-password'
41433             };
41434             
41435             var hiddenInput = {
41436                 tag: 'input',
41437                 type: 'hidden',
41438                 cls: 'hidden-tel-input'
41439             };
41440             
41441             if (this.name) {
41442                 hiddenInput.name = this.name;
41443             }
41444             
41445             if (this.disabled) {
41446                 input.disabled = true;
41447             }
41448             
41449             var flag_container = {
41450                 tag: 'div',
41451                 cls: 'flag-box',
41452                 cn: [
41453                     {
41454                         tag: 'div',
41455                         cls: 'flag'
41456                     },
41457                     {
41458                         tag: 'div',
41459                         cls: 'caret'
41460                     }
41461                 ]
41462             };
41463             
41464             var box = {
41465                 tag: 'div',
41466                 cls: this.hasFeedback ? 'has-feedback' : '',
41467                 cn: [
41468                     hiddenInput,
41469                     input,
41470                     {
41471                         tag: 'input',
41472                         cls: 'dial-code-holder',
41473                         disabled: true
41474                     }
41475                 ]
41476             };
41477             
41478             var container = {
41479                 cls: 'roo-select2-container input-group',
41480                 cn: [
41481                     flag_container,
41482                     box
41483                 ]
41484             };
41485             
41486             if (this.fieldLabel.length) {
41487                 var indicator = {
41488                     tag: 'i',
41489                     tooltip: 'This field is required'
41490                 };
41491                 
41492                 var label = {
41493                     tag: 'label',
41494                     'for':  id,
41495                     cls: 'control-label',
41496                     cn: []
41497                 };
41498                 
41499                 var label_text = {
41500                     tag: 'span',
41501                     html: this.fieldLabel
41502                 };
41503                 
41504                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
41505                 label.cn = [
41506                     indicator,
41507                     label_text
41508                 ];
41509                 
41510                 if(this.indicatorpos == 'right') {
41511                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
41512                     label.cn = [
41513                         label_text,
41514                         indicator
41515                     ];
41516                 }
41517                 
41518                 if(align == 'left') {
41519                     container = {
41520                         tag: 'div',
41521                         cn: [
41522                             container
41523                         ]
41524                     };
41525                     
41526                     if(this.labelWidth > 12){
41527                         label.style = "width: " + this.labelWidth + 'px';
41528                     }
41529                     if(this.labelWidth < 13 && this.labelmd == 0){
41530                         this.labelmd = this.labelWidth;
41531                     }
41532                     if(this.labellg > 0){
41533                         label.cls += ' col-lg-' + this.labellg;
41534                         input.cls += ' col-lg-' + (12 - this.labellg);
41535                     }
41536                     if(this.labelmd > 0){
41537                         label.cls += ' col-md-' + this.labelmd;
41538                         container.cls += ' col-md-' + (12 - this.labelmd);
41539                     }
41540                     if(this.labelsm > 0){
41541                         label.cls += ' col-sm-' + this.labelsm;
41542                         container.cls += ' col-sm-' + (12 - this.labelsm);
41543                     }
41544                     if(this.labelxs > 0){
41545                         label.cls += ' col-xs-' + this.labelxs;
41546                         container.cls += ' col-xs-' + (12 - this.labelxs);
41547                     }
41548                 }
41549             }
41550             
41551             cfg.cn = [
41552                 label,
41553                 container
41554             ];
41555             
41556             var settings = this;
41557             
41558             ['xs','sm','md','lg'].map(function(size){
41559                 if (settings[size]) {
41560                     cfg.cls += ' col-' + size + '-' + settings[size];
41561                 }
41562             });
41563             
41564             this.store = new Roo.data.Store({
41565                 proxy : new Roo.data.MemoryProxy({}),
41566                 reader : new Roo.data.JsonReader({
41567                     fields : [
41568                         {
41569                             'name' : 'name',
41570                             'type' : 'string'
41571                         },
41572                         {
41573                             'name' : 'iso2',
41574                             'type' : 'string'
41575                         },
41576                         {
41577                             'name' : 'dialCode',
41578                             'type' : 'string'
41579                         },
41580                         {
41581                             'name' : 'priority',
41582                             'type' : 'string'
41583                         },
41584                         {
41585                             'name' : 'areaCodes',
41586                             'type' : 'string'
41587                         }
41588                     ]
41589                 })
41590             });
41591             
41592             if(!this.preferedCountries) {
41593                 this.preferedCountries = [
41594                     'hk',
41595                     'gb',
41596                     'us'
41597                 ];
41598             }
41599             
41600             var p = this.preferedCountries.reverse();
41601             
41602             if(p) {
41603                 for (var i = 0; i < p.length; i++) {
41604                     for (var j = 0; j < this.allCountries.length; j++) {
41605                         if(this.allCountries[j].iso2 == p[i]) {
41606                             var t = this.allCountries[j];
41607                             this.allCountries.splice(j,1);
41608                             this.allCountries.unshift(t);
41609                         }
41610                     } 
41611                 }
41612             }
41613             
41614             this.store.proxy.data = {
41615                 success: true,
41616                 data: this.allCountries
41617             };
41618             
41619             return cfg;
41620         },
41621         
41622         initEvents : function()
41623         {
41624             this.createList();
41625             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
41626             
41627             this.indicator = this.indicatorEl();
41628             this.flag = this.flagEl();
41629             this.dialCodeHolder = this.dialCodeHolderEl();
41630             
41631             this.trigger = this.el.select('div.flag-box',true).first();
41632             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
41633             
41634             var _this = this;
41635             
41636             (function(){
41637                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
41638                 _this.list.setWidth(lw);
41639             }).defer(100);
41640             
41641             this.list.on('mouseover', this.onViewOver, this);
41642             this.list.on('mousemove', this.onViewMove, this);
41643             this.inputEl().on("keyup", this.onKeyUp, this);
41644             this.inputEl().on("keypress", this.onKeyPress, this);
41645             
41646             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
41647
41648             this.view = new Roo.View(this.list, this.tpl, {
41649                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
41650             });
41651             
41652             this.view.on('click', this.onViewClick, this);
41653             this.setValue(this.defaultDialCode);
41654         },
41655         
41656         onTriggerClick : function(e)
41657         {
41658             Roo.log('trigger click');
41659             if(this.disabled){
41660                 return;
41661             }
41662             
41663             if(this.isExpanded()){
41664                 this.collapse();
41665                 this.hasFocus = false;
41666             }else {
41667                 this.store.load({});
41668                 this.hasFocus = true;
41669                 this.expand();
41670             }
41671         },
41672         
41673         isExpanded : function()
41674         {
41675             return this.list.isVisible();
41676         },
41677         
41678         collapse : function()
41679         {
41680             if(!this.isExpanded()){
41681                 return;
41682             }
41683             this.list.hide();
41684             Roo.get(document).un('mousedown', this.collapseIf, this);
41685             Roo.get(document).un('mousewheel', this.collapseIf, this);
41686             this.fireEvent('collapse', this);
41687             this.validate();
41688         },
41689         
41690         expand : function()
41691         {
41692             Roo.log('expand');
41693
41694             if(this.isExpanded() || !this.hasFocus){
41695                 return;
41696             }
41697             
41698             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
41699             this.list.setWidth(lw);
41700             
41701             this.list.show();
41702             this.restrictHeight();
41703             
41704             Roo.get(document).on('mousedown', this.collapseIf, this);
41705             Roo.get(document).on('mousewheel', this.collapseIf, this);
41706             
41707             this.fireEvent('expand', this);
41708         },
41709         
41710         restrictHeight : function()
41711         {
41712             this.list.alignTo(this.inputEl(), this.listAlign);
41713             this.list.alignTo(this.inputEl(), this.listAlign);
41714         },
41715         
41716         onViewOver : function(e, t)
41717         {
41718             if(this.inKeyMode){
41719                 return;
41720             }
41721             var item = this.view.findItemFromChild(t);
41722             
41723             if(item){
41724                 var index = this.view.indexOf(item);
41725                 this.select(index, false);
41726             }
41727         },
41728
41729         // private
41730         onViewClick : function(view, doFocus, el, e)
41731         {
41732             var index = this.view.getSelectedIndexes()[0];
41733             
41734             var r = this.store.getAt(index);
41735             
41736             if(r){
41737                 this.onSelect(r, index);
41738             }
41739             if(doFocus !== false && !this.blockFocus){
41740                 this.inputEl().focus();
41741             }
41742         },
41743         
41744         onViewMove : function(e, t)
41745         {
41746             this.inKeyMode = false;
41747         },
41748         
41749         select : function(index, scrollIntoView)
41750         {
41751             this.selectedIndex = index;
41752             this.view.select(index);
41753             if(scrollIntoView !== false){
41754                 var el = this.view.getNode(index);
41755                 if(el){
41756                     this.list.scrollChildIntoView(el, false);
41757                 }
41758             }
41759         },
41760         
41761         createList : function()
41762         {
41763             this.list = Roo.get(document.body).createChild({
41764                 tag: 'ul',
41765                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
41766                 style: 'display:none'
41767             });
41768             
41769             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
41770         },
41771         
41772         collapseIf : function(e)
41773         {
41774             var in_combo  = e.within(this.el);
41775             var in_list =  e.within(this.list);
41776             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
41777             
41778             if (in_combo || in_list || is_list) {
41779                 return;
41780             }
41781             this.collapse();
41782         },
41783         
41784         onSelect : function(record, index)
41785         {
41786             if(this.fireEvent('beforeselect', this, record, index) !== false){
41787                 
41788                 this.setFlagClass(record.data.iso2);
41789                 this.setDialCode(record.data.dialCode);
41790                 this.hasFocus = false;
41791                 this.collapse();
41792                 this.fireEvent('select', this, record, index);
41793             }
41794         },
41795         
41796         flagEl : function()
41797         {
41798             var flag = this.el.select('div.flag',true).first();
41799             if(!flag){
41800                 return false;
41801             }
41802             return flag;
41803         },
41804         
41805         dialCodeHolderEl : function()
41806         {
41807             var d = this.el.select('input.dial-code-holder',true).first();
41808             if(!d){
41809                 return false;
41810             }
41811             return d;
41812         },
41813         
41814         setDialCode : function(v)
41815         {
41816             this.dialCodeHolder.dom.value = '+'+v;
41817         },
41818         
41819         setFlagClass : function(n)
41820         {
41821             this.flag.dom.className = 'flag '+n;
41822         },
41823         
41824         getValue : function()
41825         {
41826             var v = this.inputEl().getValue();
41827             if(this.dialCodeHolder) {
41828                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
41829             }
41830             return v;
41831         },
41832         
41833         setValue : function(v)
41834         {
41835             var d = this.getDialCode(v);
41836             
41837             //invalid dial code
41838             if(v.length == 0 || !d || d.length == 0) {
41839                 if(this.rendered){
41840                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
41841                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41842                 }
41843                 return;
41844             }
41845             
41846             //valid dial code
41847             this.setFlagClass(this.dialCodeMapping[d].iso2);
41848             this.setDialCode(d);
41849             this.inputEl().dom.value = v.replace('+'+d,'');
41850             this.hiddenEl().dom.value = this.getValue();
41851             
41852             this.validate();
41853         },
41854         
41855         getDialCode : function(v)
41856         {
41857             v = v ||  '';
41858             
41859             if (v.length == 0) {
41860                 return this.dialCodeHolder.dom.value;
41861             }
41862             
41863             var dialCode = "";
41864             if (v.charAt(0) != "+") {
41865                 return false;
41866             }
41867             var numericChars = "";
41868             for (var i = 1; i < v.length; i++) {
41869               var c = v.charAt(i);
41870               if (!isNaN(c)) {
41871                 numericChars += c;
41872                 if (this.dialCodeMapping[numericChars]) {
41873                   dialCode = v.substr(1, i);
41874                 }
41875                 if (numericChars.length == 4) {
41876                   break;
41877                 }
41878               }
41879             }
41880             return dialCode;
41881         },
41882         
41883         reset : function()
41884         {
41885             this.setValue(this.defaultDialCode);
41886             this.validate();
41887         },
41888         
41889         hiddenEl : function()
41890         {
41891             return this.el.select('input.hidden-tel-input',true).first();
41892         },
41893         
41894         // after setting val
41895         onKeyUp : function(e){
41896             this.setValue(this.getValue());
41897         },
41898         
41899         onKeyPress : function(e){
41900             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
41901                 e.stopEvent();
41902             }
41903         }
41904         
41905 });
41906 /**
41907  * @class Roo.bootstrap.MoneyField
41908  * @extends Roo.bootstrap.ComboBox
41909  * Bootstrap MoneyField class
41910  * 
41911  * @constructor
41912  * Create a new MoneyField.
41913  * @param {Object} config Configuration options
41914  */
41915
41916 Roo.bootstrap.MoneyField = function(config) {
41917     
41918     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
41919     
41920 };
41921
41922 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
41923     
41924     /**
41925      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
41926      */
41927     allowDecimals : true,
41928     /**
41929      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
41930      */
41931     decimalSeparator : ".",
41932     /**
41933      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
41934      */
41935     decimalPrecision : 0,
41936     /**
41937      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
41938      */
41939     allowNegative : true,
41940     /**
41941      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
41942      */
41943     allowZero: true,
41944     /**
41945      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
41946      */
41947     minValue : Number.NEGATIVE_INFINITY,
41948     /**
41949      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
41950      */
41951     maxValue : Number.MAX_VALUE,
41952     /**
41953      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
41954      */
41955     minText : "The minimum value for this field is {0}",
41956     /**
41957      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
41958      */
41959     maxText : "The maximum value for this field is {0}",
41960     /**
41961      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
41962      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
41963      */
41964     nanText : "{0} is not a valid number",
41965     /**
41966      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
41967      */
41968     castInt : true,
41969     /**
41970      * @cfg {String} defaults currency of the MoneyField
41971      * value should be in lkey
41972      */
41973     defaultCurrency : false,
41974     /**
41975      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
41976      */
41977     thousandsDelimiter : false,
41978     /**
41979      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
41980      */
41981     max_length: false,
41982     
41983     inputlg : 9,
41984     inputmd : 9,
41985     inputsm : 9,
41986     inputxs : 6,
41987     
41988     store : false,
41989     
41990     getAutoCreate : function()
41991     {
41992         var align = this.labelAlign || this.parentLabelAlign();
41993         
41994         var id = Roo.id();
41995
41996         var cfg = {
41997             cls: 'form-group',
41998             cn: []
41999         };
42000
42001         var input =  {
42002             tag: 'input',
42003             id : id,
42004             cls : 'form-control roo-money-amount-input',
42005             autocomplete: 'new-password'
42006         };
42007         
42008         var hiddenInput = {
42009             tag: 'input',
42010             type: 'hidden',
42011             id: Roo.id(),
42012             cls: 'hidden-number-input'
42013         };
42014         
42015         if(this.max_length) {
42016             input.maxlength = this.max_length; 
42017         }
42018         
42019         if (this.name) {
42020             hiddenInput.name = this.name;
42021         }
42022
42023         if (this.disabled) {
42024             input.disabled = true;
42025         }
42026
42027         var clg = 12 - this.inputlg;
42028         var cmd = 12 - this.inputmd;
42029         var csm = 12 - this.inputsm;
42030         var cxs = 12 - this.inputxs;
42031         
42032         var container = {
42033             tag : 'div',
42034             cls : 'row roo-money-field',
42035             cn : [
42036                 {
42037                     tag : 'div',
42038                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
42039                     cn : [
42040                         {
42041                             tag : 'div',
42042                             cls: 'roo-select2-container input-group',
42043                             cn: [
42044                                 {
42045                                     tag : 'input',
42046                                     cls : 'form-control roo-money-currency-input',
42047                                     autocomplete: 'new-password',
42048                                     readOnly : 1,
42049                                     name : this.currencyName
42050                                 },
42051                                 {
42052                                     tag :'span',
42053                                     cls : 'input-group-addon',
42054                                     cn : [
42055                                         {
42056                                             tag: 'span',
42057                                             cls: 'caret'
42058                                         }
42059                                     ]
42060                                 }
42061                             ]
42062                         }
42063                     ]
42064                 },
42065                 {
42066                     tag : 'div',
42067                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
42068                     cn : [
42069                         {
42070                             tag: 'div',
42071                             cls: this.hasFeedback ? 'has-feedback' : '',
42072                             cn: [
42073                                 input
42074                             ]
42075                         }
42076                     ]
42077                 }
42078             ]
42079             
42080         };
42081         
42082         if (this.fieldLabel.length) {
42083             var indicator = {
42084                 tag: 'i',
42085                 tooltip: 'This field is required'
42086             };
42087
42088             var label = {
42089                 tag: 'label',
42090                 'for':  id,
42091                 cls: 'control-label',
42092                 cn: []
42093             };
42094
42095             var label_text = {
42096                 tag: 'span',
42097                 html: this.fieldLabel
42098             };
42099
42100             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42101             label.cn = [
42102                 indicator,
42103                 label_text
42104             ];
42105
42106             if(this.indicatorpos == 'right') {
42107                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42108                 label.cn = [
42109                     label_text,
42110                     indicator
42111                 ];
42112             }
42113
42114             if(align == 'left') {
42115                 container = {
42116                     tag: 'div',
42117                     cn: [
42118                         container
42119                     ]
42120                 };
42121
42122                 if(this.labelWidth > 12){
42123                     label.style = "width: " + this.labelWidth + 'px';
42124                 }
42125                 if(this.labelWidth < 13 && this.labelmd == 0){
42126                     this.labelmd = this.labelWidth;
42127                 }
42128                 if(this.labellg > 0){
42129                     label.cls += ' col-lg-' + this.labellg;
42130                     input.cls += ' col-lg-' + (12 - this.labellg);
42131                 }
42132                 if(this.labelmd > 0){
42133                     label.cls += ' col-md-' + this.labelmd;
42134                     container.cls += ' col-md-' + (12 - this.labelmd);
42135                 }
42136                 if(this.labelsm > 0){
42137                     label.cls += ' col-sm-' + this.labelsm;
42138                     container.cls += ' col-sm-' + (12 - this.labelsm);
42139                 }
42140                 if(this.labelxs > 0){
42141                     label.cls += ' col-xs-' + this.labelxs;
42142                     container.cls += ' col-xs-' + (12 - this.labelxs);
42143                 }
42144             }
42145         }
42146
42147         cfg.cn = [
42148             label,
42149             container,
42150             hiddenInput
42151         ];
42152         
42153         var settings = this;
42154
42155         ['xs','sm','md','lg'].map(function(size){
42156             if (settings[size]) {
42157                 cfg.cls += ' col-' + size + '-' + settings[size];
42158             }
42159         });
42160         
42161         return cfg;
42162     },
42163     
42164     initEvents : function()
42165     {
42166         this.indicator = this.indicatorEl();
42167         
42168         this.initCurrencyEvent();
42169         
42170         this.initNumberEvent();
42171     },
42172     
42173     initCurrencyEvent : function()
42174     {
42175         if (!this.store) {
42176             throw "can not find store for combo";
42177         }
42178         
42179         this.store = Roo.factory(this.store, Roo.data);
42180         this.store.parent = this;
42181         
42182         this.createList();
42183         
42184         this.triggerEl = this.el.select('.input-group-addon', true).first();
42185         
42186         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
42187         
42188         var _this = this;
42189         
42190         (function(){
42191             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42192             _this.list.setWidth(lw);
42193         }).defer(100);
42194         
42195         this.list.on('mouseover', this.onViewOver, this);
42196         this.list.on('mousemove', this.onViewMove, this);
42197         this.list.on('scroll', this.onViewScroll, this);
42198         
42199         if(!this.tpl){
42200             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
42201         }
42202         
42203         this.view = new Roo.View(this.list, this.tpl, {
42204             singleSelect:true, store: this.store, selectedClass: this.selectedClass
42205         });
42206         
42207         this.view.on('click', this.onViewClick, this);
42208         
42209         this.store.on('beforeload', this.onBeforeLoad, this);
42210         this.store.on('load', this.onLoad, this);
42211         this.store.on('loadexception', this.onLoadException, this);
42212         
42213         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
42214             "up" : function(e){
42215                 this.inKeyMode = true;
42216                 this.selectPrev();
42217             },
42218
42219             "down" : function(e){
42220                 if(!this.isExpanded()){
42221                     this.onTriggerClick();
42222                 }else{
42223                     this.inKeyMode = true;
42224                     this.selectNext();
42225                 }
42226             },
42227
42228             "enter" : function(e){
42229                 this.collapse();
42230                 
42231                 if(this.fireEvent("specialkey", this, e)){
42232                     this.onViewClick(false);
42233                 }
42234                 
42235                 return true;
42236             },
42237
42238             "esc" : function(e){
42239                 this.collapse();
42240             },
42241
42242             "tab" : function(e){
42243                 this.collapse();
42244                 
42245                 if(this.fireEvent("specialkey", this, e)){
42246                     this.onViewClick(false);
42247                 }
42248                 
42249                 return true;
42250             },
42251
42252             scope : this,
42253
42254             doRelay : function(foo, bar, hname){
42255                 if(hname == 'down' || this.scope.isExpanded()){
42256                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
42257                 }
42258                 return true;
42259             },
42260
42261             forceKeyDown: true
42262         });
42263         
42264         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
42265         
42266     },
42267     
42268     initNumberEvent : function(e)
42269     {
42270         this.inputEl().on("keydown" , this.fireKey,  this);
42271         this.inputEl().on("focus", this.onFocus,  this);
42272         this.inputEl().on("blur", this.onBlur,  this);
42273         
42274         this.inputEl().relayEvent('keyup', this);
42275         
42276         if(this.indicator){
42277             this.indicator.addClass('invisible');
42278         }
42279  
42280         this.originalValue = this.getValue();
42281         
42282         if(this.validationEvent == 'keyup'){
42283             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
42284             this.inputEl().on('keyup', this.filterValidation, this);
42285         }
42286         else if(this.validationEvent !== false){
42287             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
42288         }
42289         
42290         if(this.selectOnFocus){
42291             this.on("focus", this.preFocus, this);
42292             
42293         }
42294         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
42295             this.inputEl().on("keypress", this.filterKeys, this);
42296         } else {
42297             this.inputEl().relayEvent('keypress', this);
42298         }
42299         
42300         var allowed = "0123456789";
42301         
42302         if(this.allowDecimals){
42303             allowed += this.decimalSeparator;
42304         }
42305         
42306         if(this.allowNegative){
42307             allowed += "-";
42308         }
42309         
42310         if(this.thousandsDelimiter) {
42311             allowed += ",";
42312         }
42313         
42314         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
42315         
42316         var keyPress = function(e){
42317             
42318             var k = e.getKey();
42319             
42320             var c = e.getCharCode();
42321             
42322             if(
42323                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
42324                     allowed.indexOf(String.fromCharCode(c)) === -1
42325             ){
42326                 e.stopEvent();
42327                 return;
42328             }
42329             
42330             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
42331                 return;
42332             }
42333             
42334             if(allowed.indexOf(String.fromCharCode(c)) === -1){
42335                 e.stopEvent();
42336             }
42337         };
42338         
42339         this.inputEl().on("keypress", keyPress, this);
42340         
42341     },
42342     
42343     onTriggerClick : function(e)
42344     {   
42345         if(this.disabled){
42346             return;
42347         }
42348         
42349         this.page = 0;
42350         this.loadNext = false;
42351         
42352         if(this.isExpanded()){
42353             this.collapse();
42354             return;
42355         }
42356         
42357         this.hasFocus = true;
42358         
42359         if(this.triggerAction == 'all') {
42360             this.doQuery(this.allQuery, true);
42361             return;
42362         }
42363         
42364         this.doQuery(this.getRawValue());
42365     },
42366     
42367     getCurrency : function()
42368     {   
42369         var v = this.currencyEl().getValue();
42370         
42371         return v;
42372     },
42373     
42374     restrictHeight : function()
42375     {
42376         this.list.alignTo(this.currencyEl(), this.listAlign);
42377         this.list.alignTo(this.currencyEl(), this.listAlign);
42378     },
42379     
42380     onViewClick : function(view, doFocus, el, e)
42381     {
42382         var index = this.view.getSelectedIndexes()[0];
42383         
42384         var r = this.store.getAt(index);
42385         
42386         if(r){
42387             this.onSelect(r, index);
42388         }
42389     },
42390     
42391     onSelect : function(record, index){
42392         
42393         if(this.fireEvent('beforeselect', this, record, index) !== false){
42394         
42395             this.setFromCurrencyData(index > -1 ? record.data : false);
42396             
42397             this.collapse();
42398             
42399             this.fireEvent('select', this, record, index);
42400         }
42401     },
42402     
42403     setFromCurrencyData : function(o)
42404     {
42405         var currency = '';
42406         
42407         this.lastCurrency = o;
42408         
42409         if (this.currencyField) {
42410             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
42411         } else {
42412             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
42413         }
42414         
42415         this.lastSelectionText = currency;
42416         
42417         //setting default currency
42418         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
42419             this.setCurrency(this.defaultCurrency);
42420             return;
42421         }
42422         
42423         this.setCurrency(currency);
42424     },
42425     
42426     setFromData : function(o)
42427     {
42428         var c = {};
42429         
42430         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
42431         
42432         this.setFromCurrencyData(c);
42433         
42434         var value = '';
42435         
42436         if (this.name) {
42437             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
42438         } else {
42439             Roo.log('no value set for '+ (this.name ? this.name : this.id));
42440         }
42441         
42442         this.setValue(value);
42443         
42444     },
42445     
42446     setCurrency : function(v)
42447     {   
42448         this.currencyValue = v;
42449         
42450         if(this.rendered){
42451             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
42452             this.validate();
42453         }
42454     },
42455     
42456     setValue : function(v)
42457     {
42458         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
42459         
42460         this.value = v;
42461         
42462         if(this.rendered){
42463             
42464             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
42465             
42466             this.inputEl().dom.value = (v == '') ? '' :
42467                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
42468             
42469             if(!this.allowZero && v === '0') {
42470                 this.hiddenEl().dom.value = '';
42471                 this.inputEl().dom.value = '';
42472             }
42473             
42474             this.validate();
42475         }
42476     },
42477     
42478     getRawValue : function()
42479     {
42480         var v = this.inputEl().getValue();
42481         
42482         return v;
42483     },
42484     
42485     getValue : function()
42486     {
42487         return this.fixPrecision(this.parseValue(this.getRawValue()));
42488     },
42489     
42490     parseValue : function(value)
42491     {
42492         if(this.thousandsDelimiter) {
42493             value += "";
42494             r = new RegExp(",", "g");
42495             value = value.replace(r, "");
42496         }
42497         
42498         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
42499         return isNaN(value) ? '' : value;
42500         
42501     },
42502     
42503     fixPrecision : function(value)
42504     {
42505         if(this.thousandsDelimiter) {
42506             value += "";
42507             r = new RegExp(",", "g");
42508             value = value.replace(r, "");
42509         }
42510         
42511         var nan = isNaN(value);
42512         
42513         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
42514             return nan ? '' : value;
42515         }
42516         return parseFloat(value).toFixed(this.decimalPrecision);
42517     },
42518     
42519     decimalPrecisionFcn : function(v)
42520     {
42521         return Math.floor(v);
42522     },
42523     
42524     validateValue : function(value)
42525     {
42526         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
42527             return false;
42528         }
42529         
42530         var num = this.parseValue(value);
42531         
42532         if(isNaN(num)){
42533             this.markInvalid(String.format(this.nanText, value));
42534             return false;
42535         }
42536         
42537         if(num < this.minValue){
42538             this.markInvalid(String.format(this.minText, this.minValue));
42539             return false;
42540         }
42541         
42542         if(num > this.maxValue){
42543             this.markInvalid(String.format(this.maxText, this.maxValue));
42544             return false;
42545         }
42546         
42547         return true;
42548     },
42549     
42550     validate : function()
42551     {
42552         if(this.disabled || this.allowBlank){
42553             this.markValid();
42554             return true;
42555         }
42556         
42557         var currency = this.getCurrency();
42558         
42559         if(this.validateValue(this.getRawValue()) && currency.length){
42560             this.markValid();
42561             return true;
42562         }
42563         
42564         this.markInvalid();
42565         return false;
42566     },
42567     
42568     getName: function()
42569     {
42570         return this.name;
42571     },
42572     
42573     beforeBlur : function()
42574     {
42575         if(!this.castInt){
42576             return;
42577         }
42578         
42579         var v = this.parseValue(this.getRawValue());
42580         
42581         if(v || v == 0){
42582             this.setValue(v);
42583         }
42584     },
42585     
42586     onBlur : function()
42587     {
42588         this.beforeBlur();
42589         
42590         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
42591             //this.el.removeClass(this.focusClass);
42592         }
42593         
42594         this.hasFocus = false;
42595         
42596         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
42597             this.validate();
42598         }
42599         
42600         var v = this.getValue();
42601         
42602         if(String(v) !== String(this.startValue)){
42603             this.fireEvent('change', this, v, this.startValue);
42604         }
42605         
42606         this.fireEvent("blur", this);
42607     },
42608     
42609     inputEl : function()
42610     {
42611         return this.el.select('.roo-money-amount-input', true).first();
42612     },
42613     
42614     currencyEl : function()
42615     {
42616         return this.el.select('.roo-money-currency-input', true).first();
42617     },
42618     
42619     hiddenEl : function()
42620     {
42621         return this.el.select('input.hidden-number-input',true).first();
42622     }
42623     
42624 });/**
42625  * @class Roo.bootstrap.BezierSignature
42626  * @extends Roo.bootstrap.Component
42627  * Bootstrap BezierSignature class
42628  * This script refer to:
42629  *    Title: Signature Pad
42630  *    Author: szimek
42631  *    Availability: https://github.com/szimek/signature_pad
42632  *
42633  * @constructor
42634  * Create a new BezierSignature
42635  * @param {Object} config The config object
42636  */
42637
42638 Roo.bootstrap.BezierSignature = function(config){
42639     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
42640     this.addEvents({
42641         "resize" : true
42642     });
42643 };
42644
42645 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
42646 {
42647      
42648     curve_data: [],
42649     
42650     is_empty: true,
42651     
42652     mouse_btn_down: true,
42653     
42654     /**
42655      * @cfg {int} canvas height
42656      */
42657     canvas_height: '200px',
42658     
42659     /**
42660      * @cfg {float|function} Radius of a single dot.
42661      */ 
42662     dot_size: false,
42663     
42664     /**
42665      * @cfg {float} Minimum width of a line. Defaults to 0.5.
42666      */
42667     min_width: 0.5,
42668     
42669     /**
42670      * @cfg {float} Maximum width of a line. Defaults to 2.5.
42671      */
42672     max_width: 2.5,
42673     
42674     /**
42675      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
42676      */
42677     throttle: 16,
42678     
42679     /**
42680      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
42681      */
42682     min_distance: 5,
42683     
42684     /**
42685      * @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.
42686      */
42687     bg_color: 'rgba(0, 0, 0, 0)',
42688     
42689     /**
42690      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
42691      */
42692     dot_color: 'black',
42693     
42694     /**
42695      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
42696      */ 
42697     velocity_filter_weight: 0.7,
42698     
42699     /**
42700      * @cfg {function} Callback when stroke begin. 
42701      */
42702     onBegin: false,
42703     
42704     /**
42705      * @cfg {function} Callback when stroke end.
42706      */
42707     onEnd: false,
42708     
42709     getAutoCreate : function()
42710     {
42711         var cls = 'roo-signature column';
42712         
42713         if(this.cls){
42714             cls += ' ' + this.cls;
42715         }
42716         
42717         var col_sizes = [
42718             'lg',
42719             'md',
42720             'sm',
42721             'xs'
42722         ];
42723         
42724         for(var i = 0; i < col_sizes.length; i++) {
42725             if(this[col_sizes[i]]) {
42726                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
42727             }
42728         }
42729         
42730         var cfg = {
42731             tag: 'div',
42732             cls: cls,
42733             cn: [
42734                 {
42735                     tag: 'div',
42736                     cls: 'roo-signature-body',
42737                     cn: [
42738                         {
42739                             tag: 'canvas',
42740                             cls: 'roo-signature-body-canvas',
42741                             height: this.canvas_height,
42742                             width: this.canvas_width
42743                         }
42744                     ]
42745                 },
42746                 {
42747                     tag: 'input',
42748                     type: 'file',
42749                     style: 'display: none'
42750                 }
42751             ]
42752         };
42753         
42754         return cfg;
42755     },
42756     
42757     initEvents: function() 
42758     {
42759         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
42760         
42761         var canvas = this.canvasEl();
42762         
42763         // mouse && touch event swapping...
42764         canvas.dom.style.touchAction = 'none';
42765         canvas.dom.style.msTouchAction = 'none';
42766         
42767         this.mouse_btn_down = false;
42768         canvas.on('mousedown', this._handleMouseDown, this);
42769         canvas.on('mousemove', this._handleMouseMove, this);
42770         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
42771         
42772         if (window.PointerEvent) {
42773             canvas.on('pointerdown', this._handleMouseDown, this);
42774             canvas.on('pointermove', this._handleMouseMove, this);
42775             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
42776         }
42777         
42778         if ('ontouchstart' in window) {
42779             canvas.on('touchstart', this._handleTouchStart, this);
42780             canvas.on('touchmove', this._handleTouchMove, this);
42781             canvas.on('touchend', this._handleTouchEnd, this);
42782         }
42783         
42784         Roo.EventManager.onWindowResize(this.resize, this, true);
42785         
42786         // file input event
42787         this.fileEl().on('change', this.uploadImage, this);
42788         
42789         this.clear();
42790         
42791         this.resize();
42792     },
42793     
42794     resize: function(){
42795         
42796         var canvas = this.canvasEl().dom;
42797         var ctx = this.canvasElCtx();
42798         var img_data = false;
42799         
42800         if(canvas.width > 0) {
42801             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
42802         }
42803         // setting canvas width will clean img data
42804         canvas.width = 0;
42805         
42806         var style = window.getComputedStyle ? 
42807             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
42808             
42809         var padding_left = parseInt(style.paddingLeft) || 0;
42810         var padding_right = parseInt(style.paddingRight) || 0;
42811         
42812         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
42813         
42814         if(img_data) {
42815             ctx.putImageData(img_data, 0, 0);
42816         }
42817     },
42818     
42819     _handleMouseDown: function(e)
42820     {
42821         if (e.browserEvent.which === 1) {
42822             this.mouse_btn_down = true;
42823             this.strokeBegin(e);
42824         }
42825     },
42826     
42827     _handleMouseMove: function (e)
42828     {
42829         if (this.mouse_btn_down) {
42830             this.strokeMoveUpdate(e);
42831         }
42832     },
42833     
42834     _handleMouseUp: function (e)
42835     {
42836         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
42837             this.mouse_btn_down = false;
42838             this.strokeEnd(e);
42839         }
42840     },
42841     
42842     _handleTouchStart: function (e) {
42843         
42844         e.preventDefault();
42845         if (e.browserEvent.targetTouches.length === 1) {
42846             // var touch = e.browserEvent.changedTouches[0];
42847             // this.strokeBegin(touch);
42848             
42849              this.strokeBegin(e); // assume e catching the correct xy...
42850         }
42851     },
42852     
42853     _handleTouchMove: function (e) {
42854         e.preventDefault();
42855         // var touch = event.targetTouches[0];
42856         // _this._strokeMoveUpdate(touch);
42857         this.strokeMoveUpdate(e);
42858     },
42859     
42860     _handleTouchEnd: function (e) {
42861         var wasCanvasTouched = e.target === this.canvasEl().dom;
42862         if (wasCanvasTouched) {
42863             e.preventDefault();
42864             // var touch = event.changedTouches[0];
42865             // _this._strokeEnd(touch);
42866             this.strokeEnd(e);
42867         }
42868     },
42869     
42870     reset: function () {
42871         this._lastPoints = [];
42872         this._lastVelocity = 0;
42873         this._lastWidth = (this.min_width + this.max_width) / 2;
42874         this.canvasElCtx().fillStyle = this.dot_color;
42875     },
42876     
42877     strokeMoveUpdate: function(e)
42878     {
42879         this.strokeUpdate(e);
42880         
42881         if (this.throttle) {
42882             this.throttleStroke(this.strokeUpdate, this.throttle);
42883         }
42884         else {
42885             this.strokeUpdate(e);
42886         }
42887     },
42888     
42889     strokeBegin: function(e)
42890     {
42891         var newPointGroup = {
42892             color: this.dot_color,
42893             points: []
42894         };
42895         
42896         if (typeof this.onBegin === 'function') {
42897             this.onBegin(e);
42898         }
42899         
42900         this.curve_data.push(newPointGroup);
42901         this.reset();
42902         this.strokeUpdate(e);
42903     },
42904     
42905     strokeUpdate: function(e)
42906     {
42907         var rect = this.canvasEl().dom.getBoundingClientRect();
42908         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
42909         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
42910         var lastPoints = lastPointGroup.points;
42911         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
42912         var isLastPointTooClose = lastPoint
42913             ? point.distanceTo(lastPoint) <= this.min_distance
42914             : false;
42915         var color = lastPointGroup.color;
42916         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
42917             var curve = this.addPoint(point);
42918             if (!lastPoint) {
42919                 this.drawDot({color: color, point: point});
42920             }
42921             else if (curve) {
42922                 this.drawCurve({color: color, curve: curve});
42923             }
42924             lastPoints.push({
42925                 time: point.time,
42926                 x: point.x,
42927                 y: point.y
42928             });
42929         }
42930     },
42931     
42932     strokeEnd: function(e)
42933     {
42934         this.strokeUpdate(e);
42935         if (typeof this.onEnd === 'function') {
42936             this.onEnd(e);
42937         }
42938     },
42939     
42940     addPoint:  function (point) {
42941         var _lastPoints = this._lastPoints;
42942         _lastPoints.push(point);
42943         if (_lastPoints.length > 2) {
42944             if (_lastPoints.length === 3) {
42945                 _lastPoints.unshift(_lastPoints[0]);
42946             }
42947             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
42948             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
42949             _lastPoints.shift();
42950             return curve;
42951         }
42952         return null;
42953     },
42954     
42955     calculateCurveWidths: function (startPoint, endPoint) {
42956         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
42957             (1 - this.velocity_filter_weight) * this._lastVelocity;
42958
42959         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
42960         var widths = {
42961             end: newWidth,
42962             start: this._lastWidth
42963         };
42964         
42965         this._lastVelocity = velocity;
42966         this._lastWidth = newWidth;
42967         return widths;
42968     },
42969     
42970     drawDot: function (_a) {
42971         var color = _a.color, point = _a.point;
42972         var ctx = this.canvasElCtx();
42973         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
42974         ctx.beginPath();
42975         this.drawCurveSegment(point.x, point.y, width);
42976         ctx.closePath();
42977         ctx.fillStyle = color;
42978         ctx.fill();
42979     },
42980     
42981     drawCurve: function (_a) {
42982         var color = _a.color, curve = _a.curve;
42983         var ctx = this.canvasElCtx();
42984         var widthDelta = curve.endWidth - curve.startWidth;
42985         var drawSteps = Math.floor(curve.length()) * 2;
42986         ctx.beginPath();
42987         ctx.fillStyle = color;
42988         for (var i = 0; i < drawSteps; i += 1) {
42989         var t = i / drawSteps;
42990         var tt = t * t;
42991         var ttt = tt * t;
42992         var u = 1 - t;
42993         var uu = u * u;
42994         var uuu = uu * u;
42995         var x = uuu * curve.startPoint.x;
42996         x += 3 * uu * t * curve.control1.x;
42997         x += 3 * u * tt * curve.control2.x;
42998         x += ttt * curve.endPoint.x;
42999         var y = uuu * curve.startPoint.y;
43000         y += 3 * uu * t * curve.control1.y;
43001         y += 3 * u * tt * curve.control2.y;
43002         y += ttt * curve.endPoint.y;
43003         var width = curve.startWidth + ttt * widthDelta;
43004         this.drawCurveSegment(x, y, width);
43005         }
43006         ctx.closePath();
43007         ctx.fill();
43008     },
43009     
43010     drawCurveSegment: function (x, y, width) {
43011         var ctx = this.canvasElCtx();
43012         ctx.moveTo(x, y);
43013         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
43014         this.is_empty = false;
43015     },
43016     
43017     clear: function()
43018     {
43019         var ctx = this.canvasElCtx();
43020         var canvas = this.canvasEl().dom;
43021         ctx.fillStyle = this.bg_color;
43022         ctx.clearRect(0, 0, canvas.width, canvas.height);
43023         ctx.fillRect(0, 0, canvas.width, canvas.height);
43024         this.curve_data = [];
43025         this.reset();
43026         this.is_empty = true;
43027     },
43028     
43029     fileEl: function()
43030     {
43031         return  this.el.select('input',true).first();
43032     },
43033     
43034     canvasEl: function()
43035     {
43036         return this.el.select('canvas',true).first();
43037     },
43038     
43039     canvasElCtx: function()
43040     {
43041         return this.el.select('canvas',true).first().dom.getContext('2d');
43042     },
43043     
43044     getImage: function(type)
43045     {
43046         if(this.is_empty) {
43047             return false;
43048         }
43049         
43050         // encryption ?
43051         return this.canvasEl().dom.toDataURL('image/'+type, 1);
43052     },
43053     
43054     drawFromImage: function(img_src)
43055     {
43056         var img = new Image();
43057         
43058         img.onload = function(){
43059             this.canvasElCtx().drawImage(img, 0, 0);
43060         }.bind(this);
43061         
43062         img.src = img_src;
43063         
43064         this.is_empty = false;
43065     },
43066     
43067     selectImage: function()
43068     {
43069         this.fileEl().dom.click();
43070     },
43071     
43072     uploadImage: function(e)
43073     {
43074         var reader = new FileReader();
43075         
43076         reader.onload = function(e){
43077             var img = new Image();
43078             img.onload = function(){
43079                 this.reset();
43080                 this.canvasElCtx().drawImage(img, 0, 0);
43081             }.bind(this);
43082             img.src = e.target.result;
43083         }.bind(this);
43084         
43085         reader.readAsDataURL(e.target.files[0]);
43086     },
43087     
43088     // Bezier Point Constructor
43089     Point: (function () {
43090         function Point(x, y, time) {
43091             this.x = x;
43092             this.y = y;
43093             this.time = time || Date.now();
43094         }
43095         Point.prototype.distanceTo = function (start) {
43096             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
43097         };
43098         Point.prototype.equals = function (other) {
43099             return this.x === other.x && this.y === other.y && this.time === other.time;
43100         };
43101         Point.prototype.velocityFrom = function (start) {
43102             return this.time !== start.time
43103             ? this.distanceTo(start) / (this.time - start.time)
43104             : 0;
43105         };
43106         return Point;
43107     }()),
43108     
43109     
43110     // Bezier Constructor
43111     Bezier: (function () {
43112         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
43113             this.startPoint = startPoint;
43114             this.control2 = control2;
43115             this.control1 = control1;
43116             this.endPoint = endPoint;
43117             this.startWidth = startWidth;
43118             this.endWidth = endWidth;
43119         }
43120         Bezier.fromPoints = function (points, widths, scope) {
43121             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
43122             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
43123             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
43124         };
43125         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
43126             var dx1 = s1.x - s2.x;
43127             var dy1 = s1.y - s2.y;
43128             var dx2 = s2.x - s3.x;
43129             var dy2 = s2.y - s3.y;
43130             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
43131             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
43132             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
43133             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
43134             var dxm = m1.x - m2.x;
43135             var dym = m1.y - m2.y;
43136             var k = l2 / (l1 + l2);
43137             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
43138             var tx = s2.x - cm.x;
43139             var ty = s2.y - cm.y;
43140             return {
43141                 c1: new scope.Point(m1.x + tx, m1.y + ty),
43142                 c2: new scope.Point(m2.x + tx, m2.y + ty)
43143             };
43144         };
43145         Bezier.prototype.length = function () {
43146             var steps = 10;
43147             var length = 0;
43148             var px;
43149             var py;
43150             for (var i = 0; i <= steps; i += 1) {
43151                 var t = i / steps;
43152                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
43153                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
43154                 if (i > 0) {
43155                     var xdiff = cx - px;
43156                     var ydiff = cy - py;
43157                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
43158                 }
43159                 px = cx;
43160                 py = cy;
43161             }
43162             return length;
43163         };
43164         Bezier.prototype.point = function (t, start, c1, c2, end) {
43165             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
43166             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
43167             + (3.0 * c2 * (1.0 - t) * t * t)
43168             + (end * t * t * t);
43169         };
43170         return Bezier;
43171     }()),
43172     
43173     throttleStroke: function(fn, wait) {
43174       if (wait === void 0) { wait = 250; }
43175       var previous = 0;
43176       var timeout = null;
43177       var result;
43178       var storedContext;
43179       var storedArgs;
43180       var later = function () {
43181           previous = Date.now();
43182           timeout = null;
43183           result = fn.apply(storedContext, storedArgs);
43184           if (!timeout) {
43185               storedContext = null;
43186               storedArgs = [];
43187           }
43188       };
43189       return function wrapper() {
43190           var args = [];
43191           for (var _i = 0; _i < arguments.length; _i++) {
43192               args[_i] = arguments[_i];
43193           }
43194           var now = Date.now();
43195           var remaining = wait - (now - previous);
43196           storedContext = this;
43197           storedArgs = args;
43198           if (remaining <= 0 || remaining > wait) {
43199               if (timeout) {
43200                   clearTimeout(timeout);
43201                   timeout = null;
43202               }
43203               previous = now;
43204               result = fn.apply(storedContext, storedArgs);
43205               if (!timeout) {
43206                   storedContext = null;
43207                   storedArgs = [];
43208               }
43209           }
43210           else if (!timeout) {
43211               timeout = window.setTimeout(later, remaining);
43212           }
43213           return result;
43214       };
43215   }
43216   
43217 });
43218
43219  
43220
43221