fix typos
[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   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 (_self|_blank|_parent|_top|other) target for a href. 
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|Boolean} html -- html contents - or just use children.. use false to hide it..
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  * @config {Boolean} rotateable can the body be rotated by clicking on it..
1931  * @config {Boolean} rotated is the body rotated when rendered...
1932  * 
1933  * @constructor
1934  * Create a new Container
1935  * @param {Object} config The config object
1936  */
1937
1938 Roo.bootstrap.Card = function(config){
1939     Roo.bootstrap.Card.superclass.constructor.call(this, config);
1940     
1941     this.addEvents({
1942          // raw events
1943         /**
1944          * @event drop
1945          * When a element a card is dropped
1946          * @param {Roo.bootstrap.Element} this
1947          * @param {Roo.Element} n the node being dropped?
1948          * @param {Object} dd Drag and drop data
1949          * @param {Roo.EventObject} e
1950          * @param {Roo.EventObject} data  the data passed via getDragData
1951          */
1952         'drop' : true,
1953          /**
1954          * @event rotate
1955          * When a element a card is rotate
1956          * @param {Roo.bootstrap.Element} this
1957          * @param {Roo.Element} n the node being dropped?
1958          * @param {Boolean} rotate status
1959          */
1960         'rotate' : true
1961         
1962     });
1963 };
1964
1965
1966 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
1967     
1968     
1969     weight : '',
1970     
1971     margin: '', /// may be better in component?
1972     margin_top: '', 
1973     margin_bottom: '', 
1974     margin_left: '',
1975     margin_right: '',
1976     margin_x: '',
1977     margin_y: '',
1978     
1979     padding : '',
1980     padding_top: '', 
1981     padding_bottom: '', 
1982     padding_left: '',
1983     padding_right: '',
1984     padding_x: '',
1985     padding_y: '',
1986     
1987     display: '', 
1988     display_xs: '', 
1989     display_sm: '', 
1990     display_lg: '',
1991     display_xl: '',
1992  
1993     header_image  : '',
1994     header : '',
1995     header_size : 0,
1996     title : '',
1997     subtitle : '',
1998     html : '',
1999     footer: '',
2000
2001     collapsable : false,
2002     collapsed : false,
2003     rotateable : false,
2004     rotated : false,
2005     
2006     dragable : false,
2007     drag_group : false,
2008     dropable : false,
2009     drop_group : false,
2010     childContainer : false,
2011     dropEl : false, /// the dom placeholde element that indicates drop location.
2012     
2013     layoutCls : function()
2014     {
2015         var cls = '';
2016         var t = this;
2017         Roo.log(this.margin_bottom.length);
2018         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2019             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2020             
2021             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2022                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2023             }
2024             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2025                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2026             }
2027         });
2028         
2029         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2030             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2031                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['margin' + (v.length ? '_' : '') + v]
2032             }
2033         });
2034         
2035         // more generic support?
2036         if (this.hidden) {
2037             cls += ' d-none';
2038         }
2039         
2040         return cls;
2041     },
2042  
2043        // Roo.log("Call onRender: " + this.xtype);
2044         /*  We are looking at something like this.
2045 <div class="card">
2046     <img src="..." class="card-img-top" alt="...">
2047     <div class="card-body">
2048         <h5 class="card-title">Card title</h5>
2049          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2050
2051         >> this bit is really the body...
2052         <div> << we will ad dthis in hopefully it will not break shit.
2053         
2054         ** card text does not actually have any styling...
2055         
2056             <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>
2057         
2058         </div> <<
2059           <a href="#" class="card-link">Card link</a>
2060           
2061     </div>
2062     <div class="card-footer">
2063         <small class="text-muted">Last updated 3 mins ago</small>
2064     </div>
2065 </div>
2066          */
2067     getAutoCreate : function(){
2068         
2069         var cfg = {
2070             tag : 'div',
2071             cls : 'card',
2072             cn : [ ]
2073         };
2074         
2075         if (this.weight.length && this.weight != 'light') {
2076             cfg.cls += ' text-white';
2077         } else {
2078             cfg.cls += ' text-dark'; // need as it's nested..
2079         }
2080         if (this.weight.length) {
2081             cfg.cls += ' bg-' + this.weight;
2082         }
2083         
2084         cfg.cls += this.layoutCls(); 
2085         
2086         var hdr = false;
2087         if (this.header.length) {
2088             hdr = {
2089                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2090                 cls : 'card-header',
2091                 cn : []
2092             };
2093             cfg.cn.push(hdr);
2094             hdr_ctr = hdr;
2095         } else {
2096             hdr = {
2097                 tag : 'div',
2098                 cls : 'card-header d-none',
2099                 cn : []
2100             };
2101             cfg.cn.push(hdr);
2102         }
2103         if (this.collapsable) {
2104             hdr_ctr = {
2105             tag : 'a',
2106             cls : 'd-block user-select-none',
2107             cn: [
2108                     {
2109                         tag: 'i',
2110                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2111                     }
2112                    
2113                 ]
2114             };
2115             hdr.cn.push(hdr_ctr);
2116         }
2117         
2118         hdr_ctr.cn.push(        {
2119             tag: 'span',
2120             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2121             html : this.header
2122         });
2123         
2124         
2125         if (this.header_image.length) {
2126             cfg.cn.push({
2127                 tag : 'img',
2128                 cls : 'card-img-top',
2129                 src: this.header_image // escape?
2130             });
2131         } else {
2132             cfg.cn.push({
2133                     tag : 'div',
2134                     cls : 'card-img-top d-none' 
2135                 });
2136         }
2137             
2138         var body = {
2139             tag : 'div',
2140             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2141             cn : []
2142         };
2143         var obody = body;
2144         if (this.collapsable || this.rotateable) {
2145             obody = {
2146                 tag: 'div',
2147                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2148                 cn : [  body ]
2149             };
2150         }
2151         
2152         cfg.cn.push(obody);
2153         
2154         if (this.title.length) {
2155             body.cn.push({
2156                 tag : 'div',
2157                 cls : 'card-title',
2158                 src: this.title // escape?
2159             });
2160         }  
2161         
2162         if (this.subtitle.length) {
2163             body.cn.push({
2164                 tag : 'div',
2165                 cls : 'card-title',
2166                 src: this.subtitle // escape?
2167             });
2168         }
2169         
2170         body.cn.push({
2171             tag : 'div',
2172             cls : 'roo-card-body-ctr'
2173         });
2174         
2175         if (this.html.length) {
2176             body.cn.push({
2177                 tag: 'div',
2178                 html : this.html
2179             });
2180         }
2181         // fixme ? handle objects?
2182         
2183         if (this.footer.length) {
2184            
2185             cfg.cn.push({
2186                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2187                 html : this.footer
2188             });
2189             
2190         } else {
2191             cfg.cn.push({cls : 'card-footer d-none'});
2192         }
2193         
2194         // footer...
2195         
2196         return cfg;
2197     },
2198     
2199     
2200     getCardHeader : function()
2201     {
2202         var  ret = this.el.select('.card-header',true).first();
2203         if (ret.hasClass('d-none')) {
2204             ret.removeClass('d-none');
2205         }
2206         
2207         return ret;
2208     },
2209     getCardFooter : function()
2210     {
2211         var  ret = this.el.select('.card-footer',true).first();
2212         if (ret.hasClass('d-none')) {
2213             ret.removeClass('d-none');
2214         }
2215         
2216         return ret;
2217     },
2218     getCardImageTop : function()
2219     {
2220         var  ret = this.el.select('.card-img-top',true).first();
2221         if (ret.hasClass('d-none')) {
2222             ret.removeClass('d-none');
2223         }
2224             
2225         return ret;
2226     },
2227     
2228     getChildContainer : function()
2229     {
2230         
2231         if(!this.el){
2232             return false;
2233         }
2234         return this.el.select('.roo-card-body-ctr',true).first();    
2235     },
2236     
2237     initEvents: function() 
2238     {
2239         
2240         this.bodyEl = this.getChildContainer();
2241         if(this.dragable){
2242             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2243                     containerScroll: true,
2244                     ddGroup: this.drag_group || 'default_card_drag_group'
2245             });
2246             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2247         }
2248         if (this.dropable) {
2249             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2250                 containerScroll: true,
2251                 ddGroup: this.drop_group || 'default_card_drag_group'
2252             });
2253             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2254             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2255             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2256             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2257             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2258         }
2259         
2260         if (this.collapsable) {
2261             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2262         }
2263         if (this.rotateable) {
2264             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2265         }
2266         this.collapsableEl = this.el.select('.roo-collapsable').first();
2267          
2268         this.footerEl = this.el.select('.card-footer').first();
2269         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle');
2270         this.headerEl = this.el.select('.roo-card-header-ctr').first();
2271         
2272         if (this.rotated) {
2273             this.el.addClass('roo-card-rotated');
2274             this.fireEvent('rotate', this, true);
2275         }
2276         
2277     },
2278     getDragData : function(e)
2279     {
2280         var target = this.getEl();
2281         if (target) {
2282             //this.handleSelection(e);
2283             
2284             var dragData = {
2285                 source: this,
2286                 copy: false,
2287                 nodes: this.getEl(),
2288                 records: []
2289             };
2290             
2291             
2292             dragData.ddel = target.dom ;    // the div element
2293             Roo.log(target.getWidth( ));
2294             dragData.ddel.style.width = target.getWidth() + 'px';
2295             
2296             return dragData;
2297         }
2298         return false;
2299     },
2300     /**
2301     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2302     *    whole Element becomes the target, and this causes the drop gesture to append.
2303     */
2304     getTargetFromEvent : function(e, dragged_card_el)
2305     {
2306         var target = e.getTarget();
2307         while ((target !== null) && (target.parentNode != this.bodyEl.dom)) {
2308             target = target.parentNode;
2309         }
2310         
2311         var ret = {
2312             position: '',
2313             cards : [],
2314             card_n : -1,
2315             items_n : -1,
2316             card : false 
2317         };
2318         
2319         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2320         // see if target is one of the 'cards'...
2321         
2322         
2323         //Roo.log(this.items.length);
2324         var pos = false;
2325         
2326         var last_card_n = 0;
2327         var cards_len  = 0;
2328         for (var i = 0;i< this.items.length;i++) {
2329             
2330             if (!this.items[i].el.hasClass('card')) {
2331                  continue;
2332             }
2333             pos = this.getDropPoint(e, this.items[i].el.dom);
2334             
2335             cards_len = ret.cards.length;
2336             //Roo.log(this.items[i].el.dom.id);
2337             ret.cards.push(this.items[i]);
2338             last_card_n  = i;
2339             if (ret.card_n < 0 && pos == 'above') {
2340                 ret.position = cards_len > 0 ? 'below' : pos;
2341                 ret.items_n = i > 0 ? i - 1 : 0;
2342                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2343                 ret.card = ret.cards[ret.card_n];
2344             }
2345         }
2346         if (!ret.cards.length) {
2347             ret.card = true;
2348             ret.position = 'below';
2349             ret.items_n;
2350             return ret;
2351         }
2352         // could not find a card.. stick it at the end..
2353         if (ret.card_n < 0) {
2354             ret.card_n = last_card_n;
2355             ret.card = ret.cards[last_card_n];
2356             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2357             ret.position = 'below';
2358         }
2359         
2360         if (this.items[ret.items_n].el == dragged_card_el) {
2361             return false;
2362         }
2363         
2364         if (ret.position == 'below') {
2365             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2366             
2367             if (card_after  && card_after.el == dragged_card_el) {
2368                 return false;
2369             }
2370             return ret;
2371         }
2372         
2373         // its's after ..
2374         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2375         
2376         if (card_before  && card_before.el == dragged_card_el) {
2377             return false;
2378         }
2379         
2380         return ret;
2381     },
2382     
2383     onNodeEnter : function(n, dd, e, data){
2384         return false;
2385     },
2386     onNodeOver : function(n, dd, e, data)
2387     {
2388        
2389         var target_info = this.getTargetFromEvent(e,data.source.el);
2390         if (target_info === false) {
2391             this.dropPlaceHolder('hide');
2392             return false;
2393         }
2394         Roo.log(['getTargetFromEvent', target_info ]);
2395         
2396          
2397         this.dropPlaceHolder('show', target_info,data);
2398         
2399         return false; 
2400     },
2401     onNodeOut : function(n, dd, e, data){
2402         this.dropPlaceHolder('hide');
2403      
2404     },
2405     onNodeDrop : function(n, dd, e, data)
2406     {
2407         
2408         // call drop - return false if
2409         
2410         // this could actually fail - if the Network drops..
2411         // we will ignore this at present..- client should probably reload
2412         // the whole set of cards if stuff like that fails.
2413         
2414         
2415         var info = this.getTargetFromEvent(e,data.source.el);
2416         if (info === false) {
2417             return false;
2418         }
2419         
2420         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
2421             return false;
2422         }
2423          
2424         this.dropPlaceHolder('hide');
2425         
2426         // do the dom manipulation first..
2427         var dom = data.source.el.dom;
2428         dom.parentNode.removeChild(dom);
2429         
2430         
2431         if (info.card !== true) {
2432             var cardel = info.card.el.dom;
2433             
2434             if (info.position == 'above') {
2435                 cardel.parentNode.insertBefore(dom, cardel);
2436             } else if (cardel.nextSibling) {
2437                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2438             } else {
2439                 cardel.parentNode.append(dom);
2440             }
2441         } else {
2442             // card container???
2443             this.bodyEl.dom.append(dom);
2444         }
2445         
2446         //FIXME HANDLE card = true 
2447         
2448         // add this to the correct place in items.
2449         
2450         
2451         
2452         // remove Card from items.
2453         
2454         var old_parent = data.source.parent();
2455         
2456         old_parent.items = old_parent.items.filter(function(e) { return e != data.source });
2457         
2458         if (this.items.length) {
2459             var nitems = [];
2460             //Roo.log([info.items_n, info.position, this.items.length]);
2461             for (var i =0; i < this.items.length; i++) {
2462                 if (i == info.items_n && info.position == 'above') {
2463                     nitems.push(data.source);
2464                 }
2465                 nitems.push(this.items[i]);
2466                 if (i == info.items_n && info.position == 'below') {
2467                     nitems.push(data.source);
2468                 }
2469             }
2470             this.items = nitems;
2471             Roo.log(this.items);
2472         } else {
2473             this.items.push(data.source);
2474         }
2475         
2476         data.source.parentId = this.id;
2477         
2478         return true;
2479     },
2480     
2481     /**    Decide whether to drop above or below a View node. */
2482     getDropPoint : function(e, n, dd)
2483     {
2484         if (dd) {
2485              return false;
2486         }
2487         if (n == this.bodyEl.dom) {
2488             return "above";
2489         }
2490         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2491         var c = t + (b - t) / 2;
2492         var y = Roo.lib.Event.getPageY(e);
2493         if(y <= c) {
2494             return "above";
2495         }else{
2496             return "below";
2497         }
2498     },
2499     onToggleCollapse : function(e)
2500         {
2501         if (this.collapsed) {
2502             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2503             this.collapsableEl.addClass('show');
2504             this.collapsed = false;
2505             return;
2506         }
2507         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2508         this.collapsableEl.removeClass('show');
2509         this.collapsed = true;
2510         
2511     
2512     },
2513     
2514     onToggleRotate : function(e)
2515     {
2516         this.collapsableEl.removeClass('show');
2517         this.footerEl.removeClass('d-none');
2518         this.el.removeClass('roo-card-rotated');
2519         this.el.removeClass('d-none');
2520         if (this.rotated) {
2521             
2522             this.collapsableEl.addClass('show');
2523             this.rotated = false;
2524             this.fireEvent('rotate', this, this.rotated);
2525             return;
2526         }
2527         this.el.addClass('roo-card-rotated');
2528         this.footerEl.addClass('d-none');
2529         this.el.select('.roo-collapsable').removeClass('show');
2530         
2531         this.rotated = true;
2532         this.fireEvent('rotate', this, this.rotated);
2533     
2534     },
2535     
2536     dropPlaceHolder: function (action, info, data)
2537     {
2538         if (this.dropEl === false) {
2539             this.dropEl = Roo.DomHelper.append(this.bodyEl, {
2540             cls : 'd-none'
2541             },true);
2542         }
2543         this.dropEl.removeClass(['d-none', 'd-block']);        
2544         if (action == 'hide') {
2545             
2546             this.dropEl.addClass('d-none');
2547             return;
2548         }
2549         // FIXME - info.card == true!!!
2550         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2551         
2552         if (info.card !== true) {
2553             var cardel = info.card.el.dom;
2554             
2555             if (info.position == 'above') {
2556                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2557             } else if (cardel.nextSibling) {
2558                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2559             } else {
2560                 cardel.parentNode.append(this.dropEl.dom);
2561             }
2562         } else {
2563             // card container???
2564             this.bodyEl.dom.append(this.dropEl.dom);
2565         }
2566         
2567         this.dropEl.addClass('d-block roo-card-dropzone');
2568         
2569         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2570         
2571         
2572     
2573     
2574     
2575     },
2576     setHeaderText: function(html)
2577     {
2578         this.headerEl.dom.innerHTML = html;
2579     }
2580
2581     
2582 });
2583
2584 /*
2585  * - LGPL
2586  *
2587  * Card header - holder for the card header elements.
2588  * 
2589  */
2590
2591 /**
2592  * @class Roo.bootstrap.CardHeader
2593  * @extends Roo.bootstrap.Element
2594  * Bootstrap CardHeader class
2595  * @constructor
2596  * Create a new Card Header - that you can embed children into
2597  * @param {Object} config The config object
2598  */
2599
2600 Roo.bootstrap.CardHeader = function(config){
2601     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2602 };
2603
2604 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2605     
2606     
2607     container_method : 'getCardHeader' 
2608     
2609      
2610     
2611     
2612    
2613 });
2614
2615  
2616
2617  /*
2618  * - LGPL
2619  *
2620  * Card footer - holder for the card footer elements.
2621  * 
2622  */
2623
2624 /**
2625  * @class Roo.bootstrap.CardFooter
2626  * @extends Roo.bootstrap.Element
2627  * Bootstrap CardFooter class
2628  * @constructor
2629  * Create a new Card Footer - that you can embed children into
2630  * @param {Object} config The config object
2631  */
2632
2633 Roo.bootstrap.CardFooter = function(config){
2634     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2635 };
2636
2637 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2638     
2639     
2640     container_method : 'getCardFooter' 
2641     
2642      
2643     
2644     
2645    
2646 });
2647
2648  
2649
2650  /*
2651  * - LGPL
2652  *
2653  * Card header - holder for the card header elements.
2654  * 
2655  */
2656
2657 /**
2658  * @class Roo.bootstrap.CardImageTop
2659  * @extends Roo.bootstrap.Element
2660  * Bootstrap CardImageTop class
2661  * @constructor
2662  * Create a new Card Image Top container
2663  * @param {Object} config The config object
2664  */
2665
2666 Roo.bootstrap.CardImageTop = function(config){
2667     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2668 };
2669
2670 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2671     
2672    
2673     container_method : 'getCardImageTop' 
2674     
2675      
2676     
2677    
2678 });
2679
2680  
2681
2682  
2683 /*
2684 * Licence: LGPL
2685 */
2686
2687 /**
2688  * @class Roo.bootstrap.CardUploader
2689  * @extends Roo.bootstrap.Button
2690  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
2691  * @cfg {Number} errorTimeout default 3000
2692  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
2693  * @cfg {Array}  html The button text.
2694
2695  *
2696  * @constructor
2697  * Create a new CardUploader
2698  * @param {Object} config The config object
2699  */
2700
2701 Roo.bootstrap.CardUploader = function(config){
2702     
2703  
2704     
2705     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
2706     
2707     
2708     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
2709         return r.data.id
2710         });
2711     
2712     
2713 };
2714
2715 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
2716     
2717      
2718     errorTimeout : 3000,
2719      
2720     images : false,
2721    
2722     fileCollection : false,
2723     allowBlank : true,
2724     
2725     getAutoCreate : function()
2726     {
2727         
2728         var cfg =  {
2729             cls :'form-group' ,
2730             cn : [
2731                
2732                 {
2733                     tag: 'label',
2734                    //cls : 'input-group-addon',
2735                     html : this.fieldLabel
2736
2737                 },
2738
2739                 {
2740                     tag: 'input',
2741                     type : 'hidden',
2742                     value : this.value,
2743                     cls : 'd-none  form-control'
2744                 },
2745                 
2746                 {
2747                     tag: 'input',
2748                     multiple : 'multiple',
2749                     type : 'file',
2750                     cls : 'd-none  roo-card-upload-selector'
2751                 },
2752                 
2753                 {
2754                     cls : 'roo-card-uploader-button-container w-100 mb-2'
2755                 },
2756                 {
2757                     cls : 'card-columns roo-card-uploader-container'
2758                 }
2759
2760             ]
2761         };
2762            
2763          
2764         return cfg;
2765     },
2766     
2767     getChildContainer : function() /// what children are added to.
2768     {
2769         return this.containerEl;
2770     },
2771    
2772     getButtonContainer : function() /// what children are added to.
2773     {
2774         return this.el.select(".roo-card-uploader-button-container").first();
2775     },
2776    
2777     initEvents : function()
2778     {
2779         
2780         Roo.bootstrap.Input.prototype.initEvents.call(this);
2781         
2782         var t = this;
2783         this.addxtype({
2784             xns: Roo.bootstrap,
2785
2786             xtype : 'Button',
2787             container_method : 'getButtonContainer' ,            
2788             html :  this.html, // fix changable?
2789             cls : 'w-100 ',
2790             listeners : {
2791                 'click' : function(btn, e) {
2792                     t.onClick(e);
2793                 }
2794             }
2795         });
2796         
2797         
2798         
2799         
2800         this.urlAPI = (window.createObjectURL && window) || 
2801                                 (window.URL && URL.revokeObjectURL && URL) || 
2802                                 (window.webkitURL && webkitURL);
2803                         
2804          
2805          
2806          
2807         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2808         
2809         this.selectorEl.on('change', this.onFileSelected, this);
2810         if (this.images) {
2811             var t = this;
2812             this.images.forEach(function(img) {
2813                 t.addCard(img)
2814             });
2815             this.images = false;
2816         }
2817         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
2818          
2819        
2820     },
2821     
2822    
2823     onClick : function(e)
2824     {
2825         e.preventDefault();
2826          
2827         this.selectorEl.dom.click();
2828          
2829     },
2830     
2831     onFileSelected : function(e)
2832     {
2833         e.preventDefault();
2834         
2835         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
2836             return;
2837         }
2838         
2839         Roo.each(this.selectorEl.dom.files, function(file){    
2840             this.addFile(file);
2841         }, this);
2842          
2843     },
2844     
2845       
2846     
2847       
2848     
2849     addFile : function(file)
2850     {
2851            
2852         if(typeof(file) === 'string'){
2853             throw "Add file by name?"; // should not happen
2854             return;
2855         }
2856         
2857         if(!file || !this.urlAPI){
2858             return;
2859         }
2860         
2861         // file;
2862         // file.type;
2863         
2864         var _this = this;
2865         
2866         
2867         var url = _this.urlAPI.createObjectURL( file);
2868            
2869         this.addCard({
2870             id : Roo.bootstrap.CardUploader.ID--,
2871             is_uploaded : false,
2872             src : url,
2873             title : file.name,
2874             mimetype : file.type,
2875             preview : false,
2876             is_deleted : 0
2877         })
2878         
2879     },
2880     
2881     addCard : function (data)
2882     {
2883         // hidden input element?
2884         // if the file is not an image...
2885         //then we need to use something other that and header_image
2886         var t = this;
2887         //   remove.....
2888         var footer = [
2889             {
2890                 xns : Roo.bootstrap,
2891                 xtype : 'CardFooter',
2892                 items: [
2893                     {
2894                         xns : Roo.bootstrap,
2895                         xtype : 'Element',
2896                         cls : 'd-flex',
2897                         items : [
2898                             
2899                             {
2900                                 xns : Roo.bootstrap,
2901                                 xtype : 'Button',
2902                                 html : String.format("<small>{0}</small>", data.title),
2903                                 cls : 'col-11 text-left',
2904                                 size: 'sm',
2905                                 weight: 'link',
2906                                 fa : 'download',
2907                                 listeners : {
2908                                     click : function() {
2909                                         this.downloadCard(data.id)
2910                                     }
2911                                 }
2912                             },
2913                           
2914                             {
2915                                 xns : Roo.bootstrap,
2916                                 xtype : 'Button',
2917                                 
2918                                 size : 'sm',
2919                                 weight: 'danger',
2920                                 cls : 'col-1',
2921                                 fa : 'times',
2922                                 listeners : {
2923                                     click : function() {
2924                                         t.removeCard(data.id)
2925                                     }
2926                                 }
2927                             }
2928                         ]
2929                     }
2930                     
2931                 ] 
2932             }
2933             
2934         ];
2935
2936         var cn = this.addxtype(
2937             {
2938                  
2939                 xns : Roo.bootstrap,
2940                 xtype : 'Card',
2941                 closeable : true,
2942                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
2943                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
2944                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
2945                 data : data,
2946                 html : false,
2947                  
2948                 items : footer,
2949                 initEvents : function() {
2950                     Roo.bootstrap.Card.prototype.initEvents.call(this);
2951                     this.imgEl = this.el.select('.card-img-top').first();
2952                     if (this.imgEl) {
2953                         this.imgEl.on('click', function() { t.previewCard( data.id); }, this);
2954                         this.imgEl.set({ 'pointer' : 'cursor' });
2955                                   
2956                     }
2957                     
2958                   
2959                 }
2960                 
2961             }
2962         );
2963         // dont' really need ot update items.
2964         // this.items.push(cn);
2965         this.fileCollection.add(cn);
2966         this.updateInput();
2967         
2968     },
2969     removeCard : function(id)
2970     {
2971         
2972         var card  = this.fileCollection.get(id);
2973         card.data.is_deleted = 1;
2974         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
2975         this.fileCollection.remove(card);
2976         //this.items = this.items.filter(function(e) { return e != card });
2977         // dont' really need ot update items.
2978         card.el.dom.parentNode.removeChild(card.el.dom);
2979         
2980     },
2981     reset: function()
2982     {
2983         this.fileCollection.each(function(card) {
2984             card.el.dom.parentNode.removeChild(card.el.dom);    
2985         });
2986         this.fileCollection.clear();
2987         this.updateInput();
2988     },
2989     
2990     updateInput : function()
2991     {
2992         var data = [];
2993         this.fileCollection.each(function(e) {
2994             data.push(e.data);
2995         });
2996         
2997         this.inputEl().dom.value = JSON.stringify(data);
2998     }
2999     
3000     
3001 });
3002
3003
3004 Roo.bootstrap.CardUploader.ID = -1;/*
3005  * - LGPL
3006  *
3007  * image
3008  * 
3009  */
3010
3011
3012 /**
3013  * @class Roo.bootstrap.Img
3014  * @extends Roo.bootstrap.Component
3015  * Bootstrap Img class
3016  * @cfg {Boolean} imgResponsive false | true
3017  * @cfg {String} border rounded | circle | thumbnail
3018  * @cfg {String} src image source
3019  * @cfg {String} alt image alternative text
3020  * @cfg {String} href a tag href
3021  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3022  * @cfg {String} xsUrl xs image source
3023  * @cfg {String} smUrl sm image source
3024  * @cfg {String} mdUrl md image source
3025  * @cfg {String} lgUrl lg image source
3026  * 
3027  * @constructor
3028  * Create a new Input
3029  * @param {Object} config The config object
3030  */
3031
3032 Roo.bootstrap.Img = function(config){
3033     Roo.bootstrap.Img.superclass.constructor.call(this, config);
3034     
3035     this.addEvents({
3036         // img events
3037         /**
3038          * @event click
3039          * The img click event for the img.
3040          * @param {Roo.EventObject} e
3041          */
3042         "click" : true
3043     });
3044 };
3045
3046 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
3047     
3048     imgResponsive: true,
3049     border: '',
3050     src: 'about:blank',
3051     href: false,
3052     target: false,
3053     xsUrl: '',
3054     smUrl: '',
3055     mdUrl: '',
3056     lgUrl: '',
3057
3058     getAutoCreate : function()
3059     {   
3060         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3061             return this.createSingleImg();
3062         }
3063         
3064         var cfg = {
3065             tag: 'div',
3066             cls: 'roo-image-responsive-group',
3067             cn: []
3068         };
3069         var _this = this;
3070         
3071         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3072             
3073             if(!_this[size + 'Url']){
3074                 return;
3075             }
3076             
3077             var img = {
3078                 tag: 'img',
3079                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3080                 html: _this.html || cfg.html,
3081                 src: _this[size + 'Url']
3082             };
3083             
3084             img.cls += ' roo-image-responsive-' + size;
3085             
3086             var s = ['xs', 'sm', 'md', 'lg'];
3087             
3088             s.splice(s.indexOf(size), 1);
3089             
3090             Roo.each(s, function(ss){
3091                 img.cls += ' hidden-' + ss;
3092             });
3093             
3094             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3095                 cfg.cls += ' img-' + _this.border;
3096             }
3097             
3098             if(_this.alt){
3099                 cfg.alt = _this.alt;
3100             }
3101             
3102             if(_this.href){
3103                 var a = {
3104                     tag: 'a',
3105                     href: _this.href,
3106                     cn: [
3107                         img
3108                     ]
3109                 };
3110
3111                 if(this.target){
3112                     a.target = _this.target;
3113                 }
3114             }
3115             
3116             cfg.cn.push((_this.href) ? a : img);
3117             
3118         });
3119         
3120         return cfg;
3121     },
3122     
3123     createSingleImg : function()
3124     {
3125         var cfg = {
3126             tag: 'img',
3127             cls: (this.imgResponsive) ? 'img-responsive' : '',
3128             html : null,
3129             src : 'about:blank'  // just incase src get's set to undefined?!?
3130         };
3131         
3132         cfg.html = this.html || cfg.html;
3133         
3134         cfg.src = this.src || cfg.src;
3135         
3136         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3137             cfg.cls += ' img-' + this.border;
3138         }
3139         
3140         if(this.alt){
3141             cfg.alt = this.alt;
3142         }
3143         
3144         if(this.href){
3145             var a = {
3146                 tag: 'a',
3147                 href: this.href,
3148                 cn: [
3149                     cfg
3150                 ]
3151             };
3152             
3153             if(this.target){
3154                 a.target = this.target;
3155             }
3156             
3157         }
3158         
3159         return (this.href) ? a : cfg;
3160     },
3161     
3162     initEvents: function() 
3163     {
3164         if(!this.href){
3165             this.el.on('click', this.onClick, this);
3166         }
3167         
3168     },
3169     
3170     onClick : function(e)
3171     {
3172         Roo.log('img onclick');
3173         this.fireEvent('click', this, e);
3174     },
3175     /**
3176      * Sets the url of the image - used to update it
3177      * @param {String} url the url of the image
3178      */
3179     
3180     setSrc : function(url)
3181     {
3182         this.src =  url;
3183         
3184         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3185             this.el.dom.src =  url;
3186             return;
3187         }
3188         
3189         this.el.select('img', true).first().dom.src =  url;
3190     }
3191     
3192     
3193    
3194 });
3195
3196  /*
3197  * - LGPL
3198  *
3199  * image
3200  * 
3201  */
3202
3203
3204 /**
3205  * @class Roo.bootstrap.Link
3206  * @extends Roo.bootstrap.Component
3207  * Bootstrap Link Class
3208  * @cfg {String} alt image alternative text
3209  * @cfg {String} href a tag href
3210  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3211  * @cfg {String} html the content of the link.
3212  * @cfg {String} anchor name for the anchor link
3213  * @cfg {String} fa - favicon
3214
3215  * @cfg {Boolean} preventDefault (true | false) default false
3216
3217  * 
3218  * @constructor
3219  * Create a new Input
3220  * @param {Object} config The config object
3221  */
3222
3223 Roo.bootstrap.Link = function(config){
3224     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3225     
3226     this.addEvents({
3227         // img events
3228         /**
3229          * @event click
3230          * The img click event for the img.
3231          * @param {Roo.EventObject} e
3232          */
3233         "click" : true
3234     });
3235 };
3236
3237 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3238     
3239     href: false,
3240     target: false,
3241     preventDefault: false,
3242     anchor : false,
3243     alt : false,
3244     fa: false,
3245
3246
3247     getAutoCreate : function()
3248     {
3249         var html = this.html || '';
3250         
3251         if (this.fa !== false) {
3252             html = '<i class="fa fa-' + this.fa + '"></i>';
3253         }
3254         var cfg = {
3255             tag: 'a'
3256         };
3257         // anchor's do not require html/href...
3258         if (this.anchor === false) {
3259             cfg.html = html;
3260             cfg.href = this.href || '#';
3261         } else {
3262             cfg.name = this.anchor;
3263             if (this.html !== false || this.fa !== false) {
3264                 cfg.html = html;
3265             }
3266             if (this.href !== false) {
3267                 cfg.href = this.href;
3268             }
3269         }
3270         
3271         if(this.alt !== false){
3272             cfg.alt = this.alt;
3273         }
3274         
3275         
3276         if(this.target !== false) {
3277             cfg.target = this.target;
3278         }
3279         
3280         return cfg;
3281     },
3282     
3283     initEvents: function() {
3284         
3285         if(!this.href || this.preventDefault){
3286             this.el.on('click', this.onClick, this);
3287         }
3288     },
3289     
3290     onClick : function(e)
3291     {
3292         if(this.preventDefault){
3293             e.preventDefault();
3294         }
3295         //Roo.log('img onclick');
3296         this.fireEvent('click', this, e);
3297     }
3298    
3299 });
3300
3301  /*
3302  * - LGPL
3303  *
3304  * header
3305  * 
3306  */
3307
3308 /**
3309  * @class Roo.bootstrap.Header
3310  * @extends Roo.bootstrap.Component
3311  * Bootstrap Header class
3312  * @cfg {String} html content of header
3313  * @cfg {Number} level (1|2|3|4|5|6) default 1
3314  * 
3315  * @constructor
3316  * Create a new Header
3317  * @param {Object} config The config object
3318  */
3319
3320
3321 Roo.bootstrap.Header  = function(config){
3322     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3323 };
3324
3325 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3326     
3327     //href : false,
3328     html : false,
3329     level : 1,
3330     
3331     
3332     
3333     getAutoCreate : function(){
3334         
3335         
3336         
3337         var cfg = {
3338             tag: 'h' + (1 *this.level),
3339             html: this.html || ''
3340         } ;
3341         
3342         return cfg;
3343     }
3344    
3345 });
3346
3347  
3348
3349  /*
3350  * Based on:
3351  * Ext JS Library 1.1.1
3352  * Copyright(c) 2006-2007, Ext JS, LLC.
3353  *
3354  * Originally Released Under LGPL - original licence link has changed is not relivant.
3355  *
3356  * Fork - LGPL
3357  * <script type="text/javascript">
3358  */
3359  
3360 /**
3361  * @class Roo.bootstrap.MenuMgr
3362  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3363  * @singleton
3364  */
3365 Roo.bootstrap.MenuMgr = function(){
3366    var menus, active, groups = {}, attached = false, lastShow = new Date();
3367
3368    // private - called when first menu is created
3369    function init(){
3370        menus = {};
3371        active = new Roo.util.MixedCollection();
3372        Roo.get(document).addKeyListener(27, function(){
3373            if(active.length > 0){
3374                hideAll();
3375            }
3376        });
3377    }
3378
3379    // private
3380    function hideAll(){
3381        if(active && active.length > 0){
3382            var c = active.clone();
3383            c.each(function(m){
3384                m.hide();
3385            });
3386        }
3387    }
3388
3389    // private
3390    function onHide(m){
3391        active.remove(m);
3392        if(active.length < 1){
3393            Roo.get(document).un("mouseup", onMouseDown);
3394             
3395            attached = false;
3396        }
3397    }
3398
3399    // private
3400    function onShow(m){
3401        var last = active.last();
3402        lastShow = new Date();
3403        active.add(m);
3404        if(!attached){
3405           Roo.get(document).on("mouseup", onMouseDown);
3406            
3407            attached = true;
3408        }
3409        if(m.parentMenu){
3410           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3411           m.parentMenu.activeChild = m;
3412        }else if(last && last.isVisible()){
3413           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3414        }
3415    }
3416
3417    // private
3418    function onBeforeHide(m){
3419        if(m.activeChild){
3420            m.activeChild.hide();
3421        }
3422        if(m.autoHideTimer){
3423            clearTimeout(m.autoHideTimer);
3424            delete m.autoHideTimer;
3425        }
3426    }
3427
3428    // private
3429    function onBeforeShow(m){
3430        var pm = m.parentMenu;
3431        if(!pm && !m.allowOtherMenus){
3432            hideAll();
3433        }else if(pm && pm.activeChild && active != m){
3434            pm.activeChild.hide();
3435        }
3436    }
3437
3438    // private this should really trigger on mouseup..
3439    function onMouseDown(e){
3440         Roo.log("on Mouse Up");
3441         
3442         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3443             Roo.log("MenuManager hideAll");
3444             hideAll();
3445             e.stopEvent();
3446         }
3447         
3448         
3449    }
3450
3451    // private
3452    function onBeforeCheck(mi, state){
3453        if(state){
3454            var g = groups[mi.group];
3455            for(var i = 0, l = g.length; i < l; i++){
3456                if(g[i] != mi){
3457                    g[i].setChecked(false);
3458                }
3459            }
3460        }
3461    }
3462
3463    return {
3464
3465        /**
3466         * Hides all menus that are currently visible
3467         */
3468        hideAll : function(){
3469             hideAll();  
3470        },
3471
3472        // private
3473        register : function(menu){
3474            if(!menus){
3475                init();
3476            }
3477            menus[menu.id] = menu;
3478            menu.on("beforehide", onBeforeHide);
3479            menu.on("hide", onHide);
3480            menu.on("beforeshow", onBeforeShow);
3481            menu.on("show", onShow);
3482            var g = menu.group;
3483            if(g && menu.events["checkchange"]){
3484                if(!groups[g]){
3485                    groups[g] = [];
3486                }
3487                groups[g].push(menu);
3488                menu.on("checkchange", onCheck);
3489            }
3490        },
3491
3492         /**
3493          * Returns a {@link Roo.menu.Menu} object
3494          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3495          * be used to generate and return a new Menu instance.
3496          */
3497        get : function(menu){
3498            if(typeof menu == "string"){ // menu id
3499                return menus[menu];
3500            }else if(menu.events){  // menu instance
3501                return menu;
3502            }
3503            /*else if(typeof menu.length == 'number'){ // array of menu items?
3504                return new Roo.bootstrap.Menu({items:menu});
3505            }else{ // otherwise, must be a config
3506                return new Roo.bootstrap.Menu(menu);
3507            }
3508            */
3509            return false;
3510        },
3511
3512        // private
3513        unregister : function(menu){
3514            delete menus[menu.id];
3515            menu.un("beforehide", onBeforeHide);
3516            menu.un("hide", onHide);
3517            menu.un("beforeshow", onBeforeShow);
3518            menu.un("show", onShow);
3519            var g = menu.group;
3520            if(g && menu.events["checkchange"]){
3521                groups[g].remove(menu);
3522                menu.un("checkchange", onCheck);
3523            }
3524        },
3525
3526        // private
3527        registerCheckable : function(menuItem){
3528            var g = menuItem.group;
3529            if(g){
3530                if(!groups[g]){
3531                    groups[g] = [];
3532                }
3533                groups[g].push(menuItem);
3534                menuItem.on("beforecheckchange", onBeforeCheck);
3535            }
3536        },
3537
3538        // private
3539        unregisterCheckable : function(menuItem){
3540            var g = menuItem.group;
3541            if(g){
3542                groups[g].remove(menuItem);
3543                menuItem.un("beforecheckchange", onBeforeCheck);
3544            }
3545        }
3546    };
3547 }();/*
3548  * - LGPL
3549  *
3550  * menu
3551  * 
3552  */
3553
3554 /**
3555  * @class Roo.bootstrap.Menu
3556  * @extends Roo.bootstrap.Component
3557  * Bootstrap Menu class - container for MenuItems
3558  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3559  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3560  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3561  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3562  * 
3563  * @constructor
3564  * Create a new Menu
3565  * @param {Object} config The config object
3566  */
3567
3568
3569 Roo.bootstrap.Menu = function(config){
3570     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3571     if (this.registerMenu && this.type != 'treeview')  {
3572         Roo.bootstrap.MenuMgr.register(this);
3573     }
3574     
3575     
3576     this.addEvents({
3577         /**
3578          * @event beforeshow
3579          * Fires before this menu is displayed (return false to block)
3580          * @param {Roo.menu.Menu} this
3581          */
3582         beforeshow : true,
3583         /**
3584          * @event beforehide
3585          * Fires before this menu is hidden (return false to block)
3586          * @param {Roo.menu.Menu} this
3587          */
3588         beforehide : true,
3589         /**
3590          * @event show
3591          * Fires after this menu is displayed
3592          * @param {Roo.menu.Menu} this
3593          */
3594         show : true,
3595         /**
3596          * @event hide
3597          * Fires after this menu is hidden
3598          * @param {Roo.menu.Menu} this
3599          */
3600         hide : true,
3601         /**
3602          * @event click
3603          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3604          * @param {Roo.menu.Menu} this
3605          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3606          * @param {Roo.EventObject} e
3607          */
3608         click : true,
3609         /**
3610          * @event mouseover
3611          * Fires when the mouse is hovering over this menu
3612          * @param {Roo.menu.Menu} this
3613          * @param {Roo.EventObject} e
3614          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3615          */
3616         mouseover : true,
3617         /**
3618          * @event mouseout
3619          * Fires when the mouse exits this menu
3620          * @param {Roo.menu.Menu} this
3621          * @param {Roo.EventObject} e
3622          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3623          */
3624         mouseout : true,
3625         /**
3626          * @event itemclick
3627          * Fires when a menu item contained in this menu is clicked
3628          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3629          * @param {Roo.EventObject} e
3630          */
3631         itemclick: true
3632     });
3633     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3634 };
3635
3636 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3637     
3638    /// html : false,
3639     //align : '',
3640     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3641     type: false,
3642     /**
3643      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3644      */
3645     registerMenu : true,
3646     
3647     menuItems :false, // stores the menu items..
3648     
3649     hidden:true,
3650         
3651     parentMenu : false,
3652     
3653     stopEvent : true,
3654     
3655     isLink : false,
3656     
3657     getChildContainer : function() {
3658         return this.el;  
3659     },
3660     
3661     getAutoCreate : function(){
3662          
3663         //if (['right'].indexOf(this.align)!==-1) {
3664         //    cfg.cn[1].cls += ' pull-right'
3665         //}
3666         
3667         
3668         var cfg = {
3669             tag : 'ul',
3670             cls : 'dropdown-menu' ,
3671             style : 'z-index:1000'
3672             
3673         };
3674         
3675         if (this.type === 'submenu') {
3676             cfg.cls = 'submenu active';
3677         }
3678         if (this.type === 'treeview') {
3679             cfg.cls = 'treeview-menu';
3680         }
3681         
3682         return cfg;
3683     },
3684     initEvents : function() {
3685         
3686        // Roo.log("ADD event");
3687        // Roo.log(this.triggerEl.dom);
3688         
3689         this.triggerEl.on('click', this.onTriggerClick, this);
3690         
3691         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3692         
3693         
3694         if (this.triggerEl.hasClass('nav-item')) {
3695             // dropdown toggle on the 'a' in BS4?
3696             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3697         } else {
3698             this.triggerEl.addClass('dropdown-toggle');
3699         }
3700         if (Roo.isTouch) {
3701             this.el.on('touchstart'  , this.onTouch, this);
3702         }
3703         this.el.on('click' , this.onClick, this);
3704
3705         this.el.on("mouseover", this.onMouseOver, this);
3706         this.el.on("mouseout", this.onMouseOut, this);
3707         
3708     },
3709     
3710     findTargetItem : function(e)
3711     {
3712         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3713         if(!t){
3714             return false;
3715         }
3716         //Roo.log(t);         Roo.log(t.id);
3717         if(t && t.id){
3718             //Roo.log(this.menuitems);
3719             return this.menuitems.get(t.id);
3720             
3721             //return this.items.get(t.menuItemId);
3722         }
3723         
3724         return false;
3725     },
3726     
3727     onTouch : function(e) 
3728     {
3729         Roo.log("menu.onTouch");
3730         //e.stopEvent(); this make the user popdown broken
3731         this.onClick(e);
3732     },
3733     
3734     onClick : function(e)
3735     {
3736         Roo.log("menu.onClick");
3737         
3738         var t = this.findTargetItem(e);
3739         if(!t || t.isContainer){
3740             return;
3741         }
3742         Roo.log(e);
3743         /*
3744         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3745             if(t == this.activeItem && t.shouldDeactivate(e)){
3746                 this.activeItem.deactivate();
3747                 delete this.activeItem;
3748                 return;
3749             }
3750             if(t.canActivate){
3751                 this.setActiveItem(t, true);
3752             }
3753             return;
3754             
3755             
3756         }
3757         */
3758        
3759         Roo.log('pass click event');
3760         
3761         t.onClick(e);
3762         
3763         this.fireEvent("click", this, t, e);
3764         
3765         var _this = this;
3766         
3767         if(!t.href.length || t.href == '#'){
3768             (function() { _this.hide(); }).defer(100);
3769         }
3770         
3771     },
3772     
3773     onMouseOver : function(e){
3774         var t  = this.findTargetItem(e);
3775         //Roo.log(t);
3776         //if(t){
3777         //    if(t.canActivate && !t.disabled){
3778         //        this.setActiveItem(t, true);
3779         //    }
3780         //}
3781         
3782         this.fireEvent("mouseover", this, e, t);
3783     },
3784     isVisible : function(){
3785         return !this.hidden;
3786     },
3787     onMouseOut : function(e){
3788         var t  = this.findTargetItem(e);
3789         
3790         //if(t ){
3791         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3792         //        this.activeItem.deactivate();
3793         //        delete this.activeItem;
3794         //    }
3795         //}
3796         this.fireEvent("mouseout", this, e, t);
3797     },
3798     
3799     
3800     /**
3801      * Displays this menu relative to another element
3802      * @param {String/HTMLElement/Roo.Element} element The element to align to
3803      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3804      * the element (defaults to this.defaultAlign)
3805      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3806      */
3807     show : function(el, pos, parentMenu)
3808     {
3809         if (false === this.fireEvent("beforeshow", this)) {
3810             Roo.log("show canceled");
3811             return;
3812         }
3813         this.parentMenu = parentMenu;
3814         if(!this.el){
3815             this.render();
3816         }
3817         
3818         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3819     },
3820      /**
3821      * Displays this menu at a specific xy position
3822      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3823      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3824      */
3825     showAt : function(xy, parentMenu, /* private: */_e){
3826         this.parentMenu = parentMenu;
3827         if(!this.el){
3828             this.render();
3829         }
3830         if(_e !== false){
3831             this.fireEvent("beforeshow", this);
3832             //xy = this.el.adjustForConstraints(xy);
3833         }
3834         
3835         //this.el.show();
3836         this.hideMenuItems();
3837         this.hidden = false;
3838         this.triggerEl.addClass('open');
3839         this.el.addClass('show');
3840         
3841         // reassign x when hitting right
3842         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3843             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3844         }
3845         
3846         // reassign y when hitting bottom
3847         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3848             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3849         }
3850         
3851         // but the list may align on trigger left or trigger top... should it be a properity?
3852         
3853         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3854             this.el.setXY(xy);
3855         }
3856         
3857         this.focus();
3858         this.fireEvent("show", this);
3859     },
3860     
3861     focus : function(){
3862         return;
3863         if(!this.hidden){
3864             this.doFocus.defer(50, this);
3865         }
3866     },
3867
3868     doFocus : function(){
3869         if(!this.hidden){
3870             this.focusEl.focus();
3871         }
3872     },
3873
3874     /**
3875      * Hides this menu and optionally all parent menus
3876      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3877      */
3878     hide : function(deep)
3879     {
3880         if (false === this.fireEvent("beforehide", this)) {
3881             Roo.log("hide canceled");
3882             return;
3883         }
3884         this.hideMenuItems();
3885         if(this.el && this.isVisible()){
3886            
3887             if(this.activeItem){
3888                 this.activeItem.deactivate();
3889                 this.activeItem = null;
3890             }
3891             this.triggerEl.removeClass('open');;
3892             this.el.removeClass('show');
3893             this.hidden = true;
3894             this.fireEvent("hide", this);
3895         }
3896         if(deep === true && this.parentMenu){
3897             this.parentMenu.hide(true);
3898         }
3899     },
3900     
3901     onTriggerClick : function(e)
3902     {
3903         Roo.log('trigger click');
3904         
3905         var target = e.getTarget();
3906         
3907         Roo.log(target.nodeName.toLowerCase());
3908         
3909         if(target.nodeName.toLowerCase() === 'i'){
3910             e.preventDefault();
3911         }
3912         
3913     },
3914     
3915     onTriggerPress  : function(e)
3916     {
3917         Roo.log('trigger press');
3918         //Roo.log(e.getTarget());
3919        // Roo.log(this.triggerEl.dom);
3920        
3921         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3922         var pel = Roo.get(e.getTarget());
3923         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3924             Roo.log('is treeview or dropdown?');
3925             return;
3926         }
3927         
3928         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3929             return;
3930         }
3931         
3932         if (this.isVisible()) {
3933             Roo.log('hide');
3934             this.hide();
3935         } else {
3936             Roo.log('show');
3937             this.show(this.triggerEl, '?', false);
3938         }
3939         
3940         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3941             e.stopEvent();
3942         }
3943         
3944     },
3945        
3946     
3947     hideMenuItems : function()
3948     {
3949         Roo.log("hide Menu Items");
3950         if (!this.el) { 
3951             return;
3952         }
3953         
3954         this.el.select('.open',true).each(function(aa) {
3955             
3956             aa.removeClass('open');
3957          
3958         });
3959     },
3960     addxtypeChild : function (tree, cntr) {
3961         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3962           
3963         this.menuitems.add(comp);
3964         return comp;
3965
3966     },
3967     getEl : function()
3968     {
3969         Roo.log(this.el);
3970         return this.el;
3971     },
3972     
3973     clear : function()
3974     {
3975         this.getEl().dom.innerHTML = '';
3976         this.menuitems.clear();
3977     }
3978 });
3979
3980  
3981  /*
3982  * - LGPL
3983  *
3984  * menu item
3985  * 
3986  */
3987
3988
3989 /**
3990  * @class Roo.bootstrap.MenuItem
3991  * @extends Roo.bootstrap.Component
3992  * Bootstrap MenuItem class
3993  * @cfg {String} html the menu label
3994  * @cfg {String} href the link
3995  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3996  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3997  * @cfg {Boolean} active  used on sidebars to highlight active itesm
3998  * @cfg {String} fa favicon to show on left of menu item.
3999  * @cfg {Roo.bootsrap.Menu} menu the child menu.
4000  * 
4001  * 
4002  * @constructor
4003  * Create a new MenuItem
4004  * @param {Object} config The config object
4005  */
4006
4007
4008 Roo.bootstrap.MenuItem = function(config){
4009     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4010     this.addEvents({
4011         // raw events
4012         /**
4013          * @event click
4014          * The raw click event for the entire grid.
4015          * @param {Roo.bootstrap.MenuItem} this
4016          * @param {Roo.EventObject} e
4017          */
4018         "click" : true
4019     });
4020 };
4021
4022 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
4023     
4024     href : false,
4025     html : false,
4026     preventDefault: false,
4027     isContainer : false,
4028     active : false,
4029     fa: false,
4030     
4031     getAutoCreate : function(){
4032         
4033         if(this.isContainer){
4034             return {
4035                 tag: 'li',
4036                 cls: 'dropdown-menu-item '
4037             };
4038         }
4039         var ctag = {
4040             tag: 'span',
4041             html: 'Link'
4042         };
4043         
4044         var anc = {
4045             tag : 'a',
4046             cls : 'dropdown-item',
4047             href : '#',
4048             cn : [  ]
4049         };
4050         
4051         if (this.fa !== false) {
4052             anc.cn.push({
4053                 tag : 'i',
4054                 cls : 'fa fa-' + this.fa
4055             });
4056         }
4057         
4058         anc.cn.push(ctag);
4059         
4060         
4061         var cfg= {
4062             tag: 'li',
4063             cls: 'dropdown-menu-item',
4064             cn: [ anc ]
4065         };
4066         if (this.parent().type == 'treeview') {
4067             cfg.cls = 'treeview-menu';
4068         }
4069         if (this.active) {
4070             cfg.cls += ' active';
4071         }
4072         
4073         
4074         
4075         anc.href = this.href || cfg.cn[0].href ;
4076         ctag.html = this.html || cfg.cn[0].html ;
4077         return cfg;
4078     },
4079     
4080     initEvents: function()
4081     {
4082         if (this.parent().type == 'treeview') {
4083             this.el.select('a').on('click', this.onClick, this);
4084         }
4085         
4086         if (this.menu) {
4087             this.menu.parentType = this.xtype;
4088             this.menu.triggerEl = this.el;
4089             this.menu = this.addxtype(Roo.apply({}, this.menu));
4090         }
4091         
4092     },
4093     onClick : function(e)
4094     {
4095         Roo.log('item on click ');
4096         
4097         if(this.preventDefault){
4098             e.preventDefault();
4099         }
4100         //this.parent().hideMenuItems();
4101         
4102         this.fireEvent('click', this, e);
4103     },
4104     getEl : function()
4105     {
4106         return this.el;
4107     } 
4108 });
4109
4110  
4111
4112  /*
4113  * - LGPL
4114  *
4115  * menu separator
4116  * 
4117  */
4118
4119
4120 /**
4121  * @class Roo.bootstrap.MenuSeparator
4122  * @extends Roo.bootstrap.Component
4123  * Bootstrap MenuSeparator class
4124  * 
4125  * @constructor
4126  * Create a new MenuItem
4127  * @param {Object} config The config object
4128  */
4129
4130
4131 Roo.bootstrap.MenuSeparator = function(config){
4132     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4133 };
4134
4135 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
4136     
4137     getAutoCreate : function(){
4138         var cfg = {
4139             cls: 'divider',
4140             tag : 'li'
4141         };
4142         
4143         return cfg;
4144     }
4145    
4146 });
4147
4148  
4149
4150  
4151 /*
4152 * Licence: LGPL
4153 */
4154
4155 /**
4156  * @class Roo.bootstrap.Modal
4157  * @extends Roo.bootstrap.Component
4158  * Bootstrap Modal class
4159  * @cfg {String} title Title of dialog
4160  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4161  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
4162  * @cfg {Boolean} specificTitle default false
4163  * @cfg {Array} buttons Array of buttons or standard button set..
4164  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4165  * @cfg {Boolean} animate default true
4166  * @cfg {Boolean} allow_close default true
4167  * @cfg {Boolean} fitwindow default false
4168  * @cfg {Number} width fixed width - usefull for chrome extension only really.
4169  * @cfg {Number} height fixed height - usefull for chrome extension only really.
4170  * @cfg {String} size (sm|lg) default empty
4171  * @cfg {Number} max_width set the max width of modal
4172  * @cfg {Boolean} editableTitle can the title be edited
4173
4174  *
4175  *
4176  * @constructor
4177  * Create a new Modal Dialog
4178  * @param {Object} config The config object
4179  */
4180
4181 Roo.bootstrap.Modal = function(config){
4182     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4183     this.addEvents({
4184         // raw events
4185         /**
4186          * @event btnclick
4187          * The raw btnclick event for the button
4188          * @param {Roo.EventObject} e
4189          */
4190         "btnclick" : true,
4191         /**
4192          * @event resize
4193          * Fire when dialog resize
4194          * @param {Roo.bootstrap.Modal} this
4195          * @param {Roo.EventObject} e
4196          */
4197         "resize" : true,
4198         /**
4199          * @event titlechanged
4200          * Fire when the editable title has been changed
4201          * @param {Roo.bootstrap.Modal} this
4202          * @param {Roo.EventObject} value
4203          */
4204         "titlechanged" : true 
4205         
4206     });
4207     this.buttons = this.buttons || [];
4208
4209     if (this.tmpl) {
4210         this.tmpl = Roo.factory(this.tmpl);
4211     }
4212
4213 };
4214
4215 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4216
4217     title : 'test dialog',
4218
4219     buttons : false,
4220
4221     // set on load...
4222
4223     html: false,
4224
4225     tmp: false,
4226
4227     specificTitle: false,
4228
4229     buttonPosition: 'right',
4230
4231     allow_close : true,
4232
4233     animate : true,
4234
4235     fitwindow: false,
4236     
4237      // private
4238     dialogEl: false,
4239     bodyEl:  false,
4240     footerEl:  false,
4241     titleEl:  false,
4242     closeEl:  false,
4243
4244     size: '',
4245     
4246     max_width: 0,
4247     
4248     max_height: 0,
4249     
4250     fit_content: false,
4251     editableTitle  : false,
4252
4253     onRender : function(ct, position)
4254     {
4255         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4256
4257         if(!this.el){
4258             var cfg = Roo.apply({},  this.getAutoCreate());
4259             cfg.id = Roo.id();
4260             //if(!cfg.name){
4261             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4262             //}
4263             //if (!cfg.name.length) {
4264             //    delete cfg.name;
4265            // }
4266             if (this.cls) {
4267                 cfg.cls += ' ' + this.cls;
4268             }
4269             if (this.style) {
4270                 cfg.style = this.style;
4271             }
4272             this.el = Roo.get(document.body).createChild(cfg, position);
4273         }
4274         //var type = this.el.dom.type;
4275
4276
4277         if(this.tabIndex !== undefined){
4278             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4279         }
4280
4281         this.dialogEl = this.el.select('.modal-dialog',true).first();
4282         this.bodyEl = this.el.select('.modal-body',true).first();
4283         this.closeEl = this.el.select('.modal-header .close', true).first();
4284         this.headerEl = this.el.select('.modal-header',true).first();
4285         this.titleEl = this.el.select('.modal-title',true).first();
4286         this.footerEl = this.el.select('.modal-footer',true).first();
4287
4288         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4289         
4290         //this.el.addClass("x-dlg-modal");
4291
4292         if (this.buttons.length) {
4293             Roo.each(this.buttons, function(bb) {
4294                 var b = Roo.apply({}, bb);
4295                 b.xns = b.xns || Roo.bootstrap;
4296                 b.xtype = b.xtype || 'Button';
4297                 if (typeof(b.listeners) == 'undefined') {
4298                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4299                 }
4300
4301                 var btn = Roo.factory(b);
4302
4303                 btn.render(this.getButtonContainer());
4304
4305             },this);
4306         }
4307         // render the children.
4308         var nitems = [];
4309
4310         if(typeof(this.items) != 'undefined'){
4311             var items = this.items;
4312             delete this.items;
4313
4314             for(var i =0;i < items.length;i++) {
4315                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4316             }
4317         }
4318
4319         this.items = nitems;
4320
4321         // where are these used - they used to be body/close/footer
4322
4323
4324         this.initEvents();
4325         //this.el.addClass([this.fieldClass, this.cls]);
4326
4327     },
4328
4329     getAutoCreate : function()
4330     {
4331         // we will default to modal-body-overflow - might need to remove or make optional later.
4332         var bdy = {
4333                 cls : 'modal-body enable-modal-body-overflow ', 
4334                 html : this.html || ''
4335         };
4336
4337         var title = {
4338             tag: 'h4',
4339             cls : 'modal-title',
4340             html : this.title
4341         };
4342
4343         if(this.specificTitle){ // WTF is this?
4344             title = this.title;
4345         }
4346
4347         var header = [];
4348         if (this.allow_close && Roo.bootstrap.version == 3) {
4349             header.push({
4350                 tag: 'button',
4351                 cls : 'close',
4352                 html : '&times'
4353             });
4354         }
4355
4356         header.push(title);
4357
4358         if (this.editableTitle) {
4359             header.push({
4360                 cls: 'form-control roo-editable-title d-none',
4361                 tag: 'input',
4362                 type: 'text'
4363             });
4364         }
4365         
4366         if (this.allow_close && Roo.bootstrap.version == 4) {
4367             header.push({
4368                 tag: 'button',
4369                 cls : 'close',
4370                 html : '&times'
4371             });
4372         }
4373         
4374         var size = '';
4375
4376         if(this.size.length){
4377             size = 'modal-' + this.size;
4378         }
4379         
4380         var footer = Roo.bootstrap.version == 3 ?
4381             {
4382                 cls : 'modal-footer',
4383                 cn : [
4384                     {
4385                         tag: 'div',
4386                         cls: 'btn-' + this.buttonPosition
4387                     }
4388                 ]
4389
4390             } :
4391             {  // BS4 uses mr-auto on left buttons....
4392                 cls : 'modal-footer'
4393             };
4394
4395             
4396
4397         
4398         
4399         var modal = {
4400             cls: "modal",
4401              cn : [
4402                 {
4403                     cls: "modal-dialog " + size,
4404                     cn : [
4405                         {
4406                             cls : "modal-content",
4407                             cn : [
4408                                 {
4409                                     cls : 'modal-header',
4410                                     cn : header
4411                                 },
4412                                 bdy,
4413                                 footer
4414                             ]
4415
4416                         }
4417                     ]
4418
4419                 }
4420             ]
4421         };
4422
4423         if(this.animate){
4424             modal.cls += ' fade';
4425         }
4426
4427         return modal;
4428
4429     },
4430     getChildContainer : function() {
4431
4432          return this.bodyEl;
4433
4434     },
4435     getButtonContainer : function() {
4436         
4437          return Roo.bootstrap.version == 4 ?
4438             this.el.select('.modal-footer',true).first()
4439             : this.el.select('.modal-footer div',true).first();
4440
4441     },
4442     initEvents : function()
4443     {
4444         if (this.allow_close) {
4445             this.closeEl.on('click', this.hide, this);
4446         }
4447         Roo.EventManager.onWindowResize(this.resize, this, true);
4448         if (this.editableTitle) {
4449             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4450             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4451             this.headerEditEl.on('keyup', function(e) {
4452                     if(e.isNavKeyPress()){
4453                             this.toggleHeaderInput(false)
4454                     }
4455                 }, this);
4456             this.headerEditEl.on('blur', function(e) {
4457                 this.toggleHeaderInput(false)
4458             },this);
4459         }
4460
4461     },
4462   
4463
4464     resize : function()
4465     {
4466         this.maskEl.setSize(
4467             Roo.lib.Dom.getViewWidth(true),
4468             Roo.lib.Dom.getViewHeight(true)
4469         );
4470         
4471         if (this.fitwindow) {
4472             
4473            
4474             this.setSize(
4475                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4476                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4477             );
4478             return;
4479         }
4480         
4481         if(this.max_width !== 0) {
4482             
4483             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4484             
4485             if(this.height) {
4486                 this.setSize(w, this.height);
4487                 return;
4488             }
4489             
4490             if(this.max_height) {
4491                 this.setSize(w,Math.min(
4492                     this.max_height,
4493                     Roo.lib.Dom.getViewportHeight(true) - 60
4494                 ));
4495                 
4496                 return;
4497             }
4498             
4499             if(!this.fit_content) {
4500                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4501                 return;
4502             }
4503             
4504             this.setSize(w, Math.min(
4505                 60 +
4506                 this.headerEl.getHeight() + 
4507                 this.footerEl.getHeight() + 
4508                 this.getChildHeight(this.bodyEl.dom.childNodes),
4509                 Roo.lib.Dom.getViewportHeight(true) - 60)
4510             );
4511         }
4512         
4513     },
4514
4515     setSize : function(w,h)
4516     {
4517         if (!w && !h) {
4518             return;
4519         }
4520         
4521         this.resizeTo(w,h);
4522     },
4523
4524     show : function() {
4525
4526         if (!this.rendered) {
4527             this.render();
4528         }
4529
4530         //this.el.setStyle('display', 'block');
4531         this.el.removeClass('hideing');
4532         this.el.dom.style.display='block';
4533         
4534         Roo.get(document.body).addClass('modal-open');
4535  
4536         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4537             
4538             (function(){
4539                 this.el.addClass('show');
4540                 this.el.addClass('in');
4541             }).defer(50, this);
4542         }else{
4543             this.el.addClass('show');
4544             this.el.addClass('in');
4545         }
4546
4547         // not sure how we can show data in here..
4548         //if (this.tmpl) {
4549         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4550         //}
4551
4552         Roo.get(document.body).addClass("x-body-masked");
4553         
4554         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4555         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4556         this.maskEl.dom.style.display = 'block';
4557         this.maskEl.addClass('show');
4558         
4559         
4560         this.resize();
4561         
4562         this.fireEvent('show', this);
4563
4564         // set zindex here - otherwise it appears to be ignored...
4565         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4566
4567         (function () {
4568             this.items.forEach( function(e) {
4569                 e.layout ? e.layout() : false;
4570
4571             });
4572         }).defer(100,this);
4573
4574     },
4575     hide : function()
4576     {
4577         if(this.fireEvent("beforehide", this) !== false){
4578             
4579             this.maskEl.removeClass('show');
4580             
4581             this.maskEl.dom.style.display = '';
4582             Roo.get(document.body).removeClass("x-body-masked");
4583             this.el.removeClass('in');
4584             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4585
4586             if(this.animate){ // why
4587                 this.el.addClass('hideing');
4588                 this.el.removeClass('show');
4589                 (function(){
4590                     if (!this.el.hasClass('hideing')) {
4591                         return; // it's been shown again...
4592                     }
4593                     
4594                     this.el.dom.style.display='';
4595
4596                     Roo.get(document.body).removeClass('modal-open');
4597                     this.el.removeClass('hideing');
4598                 }).defer(150,this);
4599                 
4600             }else{
4601                 this.el.removeClass('show');
4602                 this.el.dom.style.display='';
4603                 Roo.get(document.body).removeClass('modal-open');
4604
4605             }
4606             this.fireEvent('hide', this);
4607         }
4608     },
4609     isVisible : function()
4610     {
4611         
4612         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4613         
4614     },
4615
4616     addButton : function(str, cb)
4617     {
4618
4619
4620         var b = Roo.apply({}, { html : str } );
4621         b.xns = b.xns || Roo.bootstrap;
4622         b.xtype = b.xtype || 'Button';
4623         if (typeof(b.listeners) == 'undefined') {
4624             b.listeners = { click : cb.createDelegate(this)  };
4625         }
4626
4627         var btn = Roo.factory(b);
4628
4629         btn.render(this.getButtonContainer());
4630
4631         return btn;
4632
4633     },
4634
4635     setDefaultButton : function(btn)
4636     {
4637         //this.el.select('.modal-footer').()
4638     },
4639
4640     resizeTo: function(w,h)
4641     {
4642         this.dialogEl.setWidth(w);
4643         
4644         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4645
4646         this.bodyEl.setHeight(h - diff);
4647         
4648         this.fireEvent('resize', this);
4649     },
4650     
4651     setContentSize  : function(w, h)
4652     {
4653
4654     },
4655     onButtonClick: function(btn,e)
4656     {
4657         //Roo.log([a,b,c]);
4658         this.fireEvent('btnclick', btn.name, e);
4659     },
4660      /**
4661      * Set the title of the Dialog
4662      * @param {String} str new Title
4663      */
4664     setTitle: function(str) {
4665         this.titleEl.dom.innerHTML = str;
4666         this.title = str;
4667     },
4668     /**
4669      * Set the body of the Dialog
4670      * @param {String} str new Title
4671      */
4672     setBody: function(str) {
4673         this.bodyEl.dom.innerHTML = str;
4674     },
4675     /**
4676      * Set the body of the Dialog using the template
4677      * @param {Obj} data - apply this data to the template and replace the body contents.
4678      */
4679     applyBody: function(obj)
4680     {
4681         if (!this.tmpl) {
4682             Roo.log("Error - using apply Body without a template");
4683             //code
4684         }
4685         this.tmpl.overwrite(this.bodyEl, obj);
4686     },
4687     
4688     getChildHeight : function(child_nodes)
4689     {
4690         if(
4691             !child_nodes ||
4692             child_nodes.length == 0
4693         ) {
4694             return 0;
4695         }
4696         
4697         var child_height = 0;
4698         
4699         for(var i = 0; i < child_nodes.length; i++) {
4700             
4701             /*
4702             * for modal with tabs...
4703             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4704                 
4705                 var layout_childs = child_nodes[i].childNodes;
4706                 
4707                 for(var j = 0; j < layout_childs.length; j++) {
4708                     
4709                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4710                         
4711                         var layout_body_childs = layout_childs[j].childNodes;
4712                         
4713                         for(var k = 0; k < layout_body_childs.length; k++) {
4714                             
4715                             if(layout_body_childs[k].classList.contains('navbar')) {
4716                                 child_height += layout_body_childs[k].offsetHeight;
4717                                 continue;
4718                             }
4719                             
4720                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4721                                 
4722                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4723                                 
4724                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4725                                     
4726                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4727                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4728                                         continue;
4729                                     }
4730                                     
4731                                 }
4732                                 
4733                             }
4734                             
4735                         }
4736                     }
4737                 }
4738                 continue;
4739             }
4740             */
4741             
4742             child_height += child_nodes[i].offsetHeight;
4743             // Roo.log(child_nodes[i].offsetHeight);
4744         }
4745         
4746         return child_height;
4747     },
4748     toggleHeaderInput : function(is_edit)
4749     {
4750         
4751         if (is_edit && this.is_header_editing) {
4752             return; // already editing..
4753         }
4754         if (is_edit) {
4755     
4756             this.headerEditEl.dom.value = this.title;
4757             this.headerEditEl.removeClass('d-none');
4758             this.headerEditEl.dom.focus();
4759             this.titleEl.addClass('d-none');
4760             
4761             this.is_header_editing = true;
4762             return
4763         }
4764         // flip back to not editing.
4765         this.title = this.headerEditEl.dom.value;
4766         this.headerEditEl.addClass('d-none');
4767         this.titleEl.removeClass('d-none');
4768         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4769         this.is_header_editing = false;
4770         this.fireEvent('titlechanged', this, this.title);
4771     
4772             
4773         
4774     }
4775
4776 });
4777
4778
4779 Roo.apply(Roo.bootstrap.Modal,  {
4780     /**
4781          * Button config that displays a single OK button
4782          * @type Object
4783          */
4784         OK :  [{
4785             name : 'ok',
4786             weight : 'primary',
4787             html : 'OK'
4788         }],
4789         /**
4790          * Button config that displays Yes and No buttons
4791          * @type Object
4792          */
4793         YESNO : [
4794             {
4795                 name  : 'no',
4796                 html : 'No'
4797             },
4798             {
4799                 name  :'yes',
4800                 weight : 'primary',
4801                 html : 'Yes'
4802             }
4803         ],
4804
4805         /**
4806          * Button config that displays OK and Cancel buttons
4807          * @type Object
4808          */
4809         OKCANCEL : [
4810             {
4811                name : 'cancel',
4812                 html : 'Cancel'
4813             },
4814             {
4815                 name : 'ok',
4816                 weight : 'primary',
4817                 html : 'OK'
4818             }
4819         ],
4820         /**
4821          * Button config that displays Yes, No and Cancel buttons
4822          * @type Object
4823          */
4824         YESNOCANCEL : [
4825             {
4826                 name : 'yes',
4827                 weight : 'primary',
4828                 html : 'Yes'
4829             },
4830             {
4831                 name : 'no',
4832                 html : 'No'
4833             },
4834             {
4835                 name : 'cancel',
4836                 html : 'Cancel'
4837             }
4838         ],
4839         
4840         zIndex : 10001
4841 });
4842
4843 /*
4844  * - LGPL
4845  *
4846  * messagebox - can be used as a replace
4847  * 
4848  */
4849 /**
4850  * @class Roo.MessageBox
4851  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4852  * Example usage:
4853  *<pre><code>
4854 // Basic alert:
4855 Roo.Msg.alert('Status', 'Changes saved successfully.');
4856
4857 // Prompt for user data:
4858 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4859     if (btn == 'ok'){
4860         // process text value...
4861     }
4862 });
4863
4864 // Show a dialog using config options:
4865 Roo.Msg.show({
4866    title:'Save Changes?',
4867    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4868    buttons: Roo.Msg.YESNOCANCEL,
4869    fn: processResult,
4870    animEl: 'elId'
4871 });
4872 </code></pre>
4873  * @singleton
4874  */
4875 Roo.bootstrap.MessageBox = function(){
4876     var dlg, opt, mask, waitTimer;
4877     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4878     var buttons, activeTextEl, bwidth;
4879
4880     
4881     // private
4882     var handleButton = function(button){
4883         dlg.hide();
4884         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4885     };
4886
4887     // private
4888     var handleHide = function(){
4889         if(opt && opt.cls){
4890             dlg.el.removeClass(opt.cls);
4891         }
4892         //if(waitTimer){
4893         //    Roo.TaskMgr.stop(waitTimer);
4894         //    waitTimer = null;
4895         //}
4896     };
4897
4898     // private
4899     var updateButtons = function(b){
4900         var width = 0;
4901         if(!b){
4902             buttons["ok"].hide();
4903             buttons["cancel"].hide();
4904             buttons["yes"].hide();
4905             buttons["no"].hide();
4906             dlg.footerEl.hide();
4907             
4908             return width;
4909         }
4910         dlg.footerEl.show();
4911         for(var k in buttons){
4912             if(typeof buttons[k] != "function"){
4913                 if(b[k]){
4914                     buttons[k].show();
4915                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4916                     width += buttons[k].el.getWidth()+15;
4917                 }else{
4918                     buttons[k].hide();
4919                 }
4920             }
4921         }
4922         return width;
4923     };
4924
4925     // private
4926     var handleEsc = function(d, k, e){
4927         if(opt && opt.closable !== false){
4928             dlg.hide();
4929         }
4930         if(e){
4931             e.stopEvent();
4932         }
4933     };
4934
4935     return {
4936         /**
4937          * Returns a reference to the underlying {@link Roo.BasicDialog} element
4938          * @return {Roo.BasicDialog} The BasicDialog element
4939          */
4940         getDialog : function(){
4941            if(!dlg){
4942                 dlg = new Roo.bootstrap.Modal( {
4943                     //draggable: true,
4944                     //resizable:false,
4945                     //constraintoviewport:false,
4946                     //fixedcenter:true,
4947                     //collapsible : false,
4948                     //shim:true,
4949                     //modal: true,
4950                 //    width: 'auto',
4951                   //  height:100,
4952                     //buttonAlign:"center",
4953                     closeClick : function(){
4954                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4955                             handleButton("no");
4956                         }else{
4957                             handleButton("cancel");
4958                         }
4959                     }
4960                 });
4961                 dlg.render();
4962                 dlg.on("hide", handleHide);
4963                 mask = dlg.mask;
4964                 //dlg.addKeyListener(27, handleEsc);
4965                 buttons = {};
4966                 this.buttons = buttons;
4967                 var bt = this.buttonText;
4968                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4969                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4970                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4971                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4972                 //Roo.log(buttons);
4973                 bodyEl = dlg.bodyEl.createChild({
4974
4975                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4976                         '<textarea class="roo-mb-textarea"></textarea>' +
4977                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
4978                 });
4979                 msgEl = bodyEl.dom.firstChild;
4980                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4981                 textboxEl.enableDisplayMode();
4982                 textboxEl.addKeyListener([10,13], function(){
4983                     if(dlg.isVisible() && opt && opt.buttons){
4984                         if(opt.buttons.ok){
4985                             handleButton("ok");
4986                         }else if(opt.buttons.yes){
4987                             handleButton("yes");
4988                         }
4989                     }
4990                 });
4991                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4992                 textareaEl.enableDisplayMode();
4993                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4994                 progressEl.enableDisplayMode();
4995                 
4996                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4997                 var pf = progressEl.dom.firstChild;
4998                 if (pf) {
4999                     pp = Roo.get(pf.firstChild);
5000                     pp.setHeight(pf.offsetHeight);
5001                 }
5002                 
5003             }
5004             return dlg;
5005         },
5006
5007         /**
5008          * Updates the message box body text
5009          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5010          * the XHTML-compliant non-breaking space character '&amp;#160;')
5011          * @return {Roo.MessageBox} This message box
5012          */
5013         updateText : function(text)
5014         {
5015             if(!dlg.isVisible() && !opt.width){
5016                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5017                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5018             }
5019             msgEl.innerHTML = text || '&#160;';
5020       
5021             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5022             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5023             var w = Math.max(
5024                     Math.min(opt.width || cw , this.maxWidth), 
5025                     Math.max(opt.minWidth || this.minWidth, bwidth)
5026             );
5027             if(opt.prompt){
5028                 activeTextEl.setWidth(w);
5029             }
5030             if(dlg.isVisible()){
5031                 dlg.fixedcenter = false;
5032             }
5033             // to big, make it scroll. = But as usual stupid IE does not support
5034             // !important..
5035             
5036             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5037                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5038                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5039             } else {
5040                 bodyEl.dom.style.height = '';
5041                 bodyEl.dom.style.overflowY = '';
5042             }
5043             if (cw > w) {
5044                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5045             } else {
5046                 bodyEl.dom.style.overflowX = '';
5047             }
5048             
5049             dlg.setContentSize(w, bodyEl.getHeight());
5050             if(dlg.isVisible()){
5051                 dlg.fixedcenter = true;
5052             }
5053             return this;
5054         },
5055
5056         /**
5057          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
5058          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5059          * @param {Number} value Any number between 0 and 1 (e.g., .5)
5060          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5061          * @return {Roo.MessageBox} This message box
5062          */
5063         updateProgress : function(value, text){
5064             if(text){
5065                 this.updateText(text);
5066             }
5067             
5068             if (pp) { // weird bug on my firefox - for some reason this is not defined
5069                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5070                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5071             }
5072             return this;
5073         },        
5074
5075         /**
5076          * Returns true if the message box is currently displayed
5077          * @return {Boolean} True if the message box is visible, else false
5078          */
5079         isVisible : function(){
5080             return dlg && dlg.isVisible();  
5081         },
5082
5083         /**
5084          * Hides the message box if it is displayed
5085          */
5086         hide : function(){
5087             if(this.isVisible()){
5088                 dlg.hide();
5089             }  
5090         },
5091
5092         /**
5093          * Displays a new message box, or reinitializes an existing message box, based on the config options
5094          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5095          * The following config object properties are supported:
5096          * <pre>
5097 Property    Type             Description
5098 ----------  ---------------  ------------------------------------------------------------------------------------
5099 animEl            String/Element   An id or Element from which the message box should animate as it opens and
5100                                    closes (defaults to undefined)
5101 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5102                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
5103 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
5104                                    progress and wait dialogs will ignore this property and always hide the
5105                                    close button as they can only be closed programmatically.
5106 cls               String           A custom CSS class to apply to the message box element
5107 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
5108                                    displayed (defaults to 75)
5109 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
5110                                    function will be btn (the name of the button that was clicked, if applicable,
5111                                    e.g. "ok"), and text (the value of the active text field, if applicable).
5112                                    Progress and wait dialogs will ignore this option since they do not respond to
5113                                    user actions and can only be closed programmatically, so any required function
5114                                    should be called by the same code after it closes the dialog.
5115 icon              String           A CSS class that provides a background image to be used as an icon for
5116                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5117 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
5118 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
5119 modal             Boolean          False to allow user interaction with the page while the message box is
5120                                    displayed (defaults to true)
5121 msg               String           A string that will replace the existing message box body text (defaults
5122                                    to the XHTML-compliant non-breaking space character '&#160;')
5123 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
5124 progress          Boolean          True to display a progress bar (defaults to false)
5125 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
5126 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
5127 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
5128 title             String           The title text
5129 value             String           The string value to set into the active textbox element if displayed
5130 wait              Boolean          True to display a progress bar (defaults to false)
5131 width             Number           The width of the dialog in pixels
5132 </pre>
5133          *
5134          * Example usage:
5135          * <pre><code>
5136 Roo.Msg.show({
5137    title: 'Address',
5138    msg: 'Please enter your address:',
5139    width: 300,
5140    buttons: Roo.MessageBox.OKCANCEL,
5141    multiline: true,
5142    fn: saveAddress,
5143    animEl: 'addAddressBtn'
5144 });
5145 </code></pre>
5146          * @param {Object} config Configuration options
5147          * @return {Roo.MessageBox} This message box
5148          */
5149         show : function(options)
5150         {
5151             
5152             // this causes nightmares if you show one dialog after another
5153             // especially on callbacks..
5154              
5155             if(this.isVisible()){
5156                 
5157                 this.hide();
5158                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5159                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
5160                 Roo.log("New Dialog Message:" +  options.msg )
5161                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5162                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5163                 
5164             }
5165             var d = this.getDialog();
5166             opt = options;
5167             d.setTitle(opt.title || "&#160;");
5168             d.closeEl.setDisplayed(opt.closable !== false);
5169             activeTextEl = textboxEl;
5170             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5171             if(opt.prompt){
5172                 if(opt.multiline){
5173                     textboxEl.hide();
5174                     textareaEl.show();
5175                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5176                         opt.multiline : this.defaultTextHeight);
5177                     activeTextEl = textareaEl;
5178                 }else{
5179                     textboxEl.show();
5180                     textareaEl.hide();
5181                 }
5182             }else{
5183                 textboxEl.hide();
5184                 textareaEl.hide();
5185             }
5186             progressEl.setDisplayed(opt.progress === true);
5187             if (opt.progress) {
5188                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5189             }
5190             this.updateProgress(0);
5191             activeTextEl.dom.value = opt.value || "";
5192             if(opt.prompt){
5193                 dlg.setDefaultButton(activeTextEl);
5194             }else{
5195                 var bs = opt.buttons;
5196                 var db = null;
5197                 if(bs && bs.ok){
5198                     db = buttons["ok"];
5199                 }else if(bs && bs.yes){
5200                     db = buttons["yes"];
5201                 }
5202                 dlg.setDefaultButton(db);
5203             }
5204             bwidth = updateButtons(opt.buttons);
5205             this.updateText(opt.msg);
5206             if(opt.cls){
5207                 d.el.addClass(opt.cls);
5208             }
5209             d.proxyDrag = opt.proxyDrag === true;
5210             d.modal = opt.modal !== false;
5211             d.mask = opt.modal !== false ? mask : false;
5212             if(!d.isVisible()){
5213                 // force it to the end of the z-index stack so it gets a cursor in FF
5214                 document.body.appendChild(dlg.el.dom);
5215                 d.animateTarget = null;
5216                 d.show(options.animEl);
5217             }
5218             return this;
5219         },
5220
5221         /**
5222          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5223          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5224          * and closing the message box when the process is complete.
5225          * @param {String} title The title bar text
5226          * @param {String} msg The message box body text
5227          * @return {Roo.MessageBox} This message box
5228          */
5229         progress : function(title, msg){
5230             this.show({
5231                 title : title,
5232                 msg : msg,
5233                 buttons: false,
5234                 progress:true,
5235                 closable:false,
5236                 minWidth: this.minProgressWidth,
5237                 modal : true
5238             });
5239             return this;
5240         },
5241
5242         /**
5243          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5244          * If a callback function is passed it will be called after the user clicks the button, and the
5245          * id of the button that was clicked will be passed as the only parameter to the callback
5246          * (could also be the top-right close button).
5247          * @param {String} title The title bar text
5248          * @param {String} msg The message box body text
5249          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5250          * @param {Object} scope (optional) The scope of the callback function
5251          * @return {Roo.MessageBox} This message box
5252          */
5253         alert : function(title, msg, fn, scope)
5254         {
5255             this.show({
5256                 title : title,
5257                 msg : msg,
5258                 buttons: this.OK,
5259                 fn: fn,
5260                 closable : false,
5261                 scope : scope,
5262                 modal : true
5263             });
5264             return this;
5265         },
5266
5267         /**
5268          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5269          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5270          * You are responsible for closing the message box when the process is complete.
5271          * @param {String} msg The message box body text
5272          * @param {String} title (optional) The title bar text
5273          * @return {Roo.MessageBox} This message box
5274          */
5275         wait : function(msg, title){
5276             this.show({
5277                 title : title,
5278                 msg : msg,
5279                 buttons: false,
5280                 closable:false,
5281                 progress:true,
5282                 modal:true,
5283                 width:300,
5284                 wait:true
5285             });
5286             waitTimer = Roo.TaskMgr.start({
5287                 run: function(i){
5288                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5289                 },
5290                 interval: 1000
5291             });
5292             return this;
5293         },
5294
5295         /**
5296          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5297          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5298          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5299          * @param {String} title The title bar text
5300          * @param {String} msg The message box body text
5301          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5302          * @param {Object} scope (optional) The scope of the callback function
5303          * @return {Roo.MessageBox} This message box
5304          */
5305         confirm : function(title, msg, fn, scope){
5306             this.show({
5307                 title : title,
5308                 msg : msg,
5309                 buttons: this.YESNO,
5310                 fn: fn,
5311                 scope : scope,
5312                 modal : true
5313             });
5314             return this;
5315         },
5316
5317         /**
5318          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5319          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5320          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5321          * (could also be the top-right close button) and the text that was entered will be passed as the two
5322          * parameters to the callback.
5323          * @param {String} title The title bar text
5324          * @param {String} msg The message box body text
5325          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5326          * @param {Object} scope (optional) The scope of the callback function
5327          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5328          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5329          * @return {Roo.MessageBox} This message box
5330          */
5331         prompt : function(title, msg, fn, scope, multiline){
5332             this.show({
5333                 title : title,
5334                 msg : msg,
5335                 buttons: this.OKCANCEL,
5336                 fn: fn,
5337                 minWidth:250,
5338                 scope : scope,
5339                 prompt:true,
5340                 multiline: multiline,
5341                 modal : true
5342             });
5343             return this;
5344         },
5345
5346         /**
5347          * Button config that displays a single OK button
5348          * @type Object
5349          */
5350         OK : {ok:true},
5351         /**
5352          * Button config that displays Yes and No buttons
5353          * @type Object
5354          */
5355         YESNO : {yes:true, no:true},
5356         /**
5357          * Button config that displays OK and Cancel buttons
5358          * @type Object
5359          */
5360         OKCANCEL : {ok:true, cancel:true},
5361         /**
5362          * Button config that displays Yes, No and Cancel buttons
5363          * @type Object
5364          */
5365         YESNOCANCEL : {yes:true, no:true, cancel:true},
5366
5367         /**
5368          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5369          * @type Number
5370          */
5371         defaultTextHeight : 75,
5372         /**
5373          * The maximum width in pixels of the message box (defaults to 600)
5374          * @type Number
5375          */
5376         maxWidth : 600,
5377         /**
5378          * The minimum width in pixels of the message box (defaults to 100)
5379          * @type Number
5380          */
5381         minWidth : 100,
5382         /**
5383          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5384          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5385          * @type Number
5386          */
5387         minProgressWidth : 250,
5388         /**
5389          * An object containing the default button text strings that can be overriden for localized language support.
5390          * Supported properties are: ok, cancel, yes and no.
5391          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5392          * @type Object
5393          */
5394         buttonText : {
5395             ok : "OK",
5396             cancel : "Cancel",
5397             yes : "Yes",
5398             no : "No"
5399         }
5400     };
5401 }();
5402
5403 /**
5404  * Shorthand for {@link Roo.MessageBox}
5405  */
5406 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5407 Roo.Msg = Roo.Msg || Roo.MessageBox;
5408 /*
5409  * - LGPL
5410  *
5411  * navbar
5412  * 
5413  */
5414
5415 /**
5416  * @class Roo.bootstrap.Navbar
5417  * @extends Roo.bootstrap.Component
5418  * Bootstrap Navbar class
5419
5420  * @constructor
5421  * Create a new Navbar
5422  * @param {Object} config The config object
5423  */
5424
5425
5426 Roo.bootstrap.Navbar = function(config){
5427     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5428     this.addEvents({
5429         // raw events
5430         /**
5431          * @event beforetoggle
5432          * Fire before toggle the menu
5433          * @param {Roo.EventObject} e
5434          */
5435         "beforetoggle" : true
5436     });
5437 };
5438
5439 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5440     
5441     
5442    
5443     // private
5444     navItems : false,
5445     loadMask : false,
5446     
5447     
5448     getAutoCreate : function(){
5449         
5450         
5451         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5452         
5453     },
5454     
5455     initEvents :function ()
5456     {
5457         //Roo.log(this.el.select('.navbar-toggle',true));
5458         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5459         
5460         var mark = {
5461             tag: "div",
5462             cls:"x-dlg-mask"
5463         };
5464         
5465         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5466         
5467         var size = this.el.getSize();
5468         this.maskEl.setSize(size.width, size.height);
5469         this.maskEl.enableDisplayMode("block");
5470         this.maskEl.hide();
5471         
5472         if(this.loadMask){
5473             this.maskEl.show();
5474         }
5475     },
5476     
5477     
5478     getChildContainer : function()
5479     {
5480         if (this.el && this.el.select('.collapse').getCount()) {
5481             return this.el.select('.collapse',true).first();
5482         }
5483         
5484         return this.el;
5485     },
5486     
5487     mask : function()
5488     {
5489         this.maskEl.show();
5490     },
5491     
5492     unmask : function()
5493     {
5494         this.maskEl.hide();
5495     },
5496     onToggle : function()
5497     {
5498         
5499         if(this.fireEvent('beforetoggle', this) === false){
5500             return;
5501         }
5502         var ce = this.el.select('.navbar-collapse',true).first();
5503       
5504         if (!ce.hasClass('show')) {
5505            this.expand();
5506         } else {
5507             this.collapse();
5508         }
5509         
5510         
5511     
5512     },
5513     /**
5514      * Expand the navbar pulldown 
5515      */
5516     expand : function ()
5517     {
5518        
5519         var ce = this.el.select('.navbar-collapse',true).first();
5520         if (ce.hasClass('collapsing')) {
5521             return;
5522         }
5523         ce.dom.style.height = '';
5524                // show it...
5525         ce.addClass('in'); // old...
5526         ce.removeClass('collapse');
5527         ce.addClass('show');
5528         var h = ce.getHeight();
5529         Roo.log(h);
5530         ce.removeClass('show');
5531         // at this point we should be able to see it..
5532         ce.addClass('collapsing');
5533         
5534         ce.setHeight(0); // resize it ...
5535         ce.on('transitionend', function() {
5536             //Roo.log('done transition');
5537             ce.removeClass('collapsing');
5538             ce.addClass('show');
5539             ce.removeClass('collapse');
5540
5541             ce.dom.style.height = '';
5542         }, this, { single: true} );
5543         ce.setHeight(h);
5544         ce.dom.scrollTop = 0;
5545     },
5546     /**
5547      * Collapse the navbar pulldown 
5548      */
5549     collapse : function()
5550     {
5551          var ce = this.el.select('.navbar-collapse',true).first();
5552        
5553         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5554             // it's collapsed or collapsing..
5555             return;
5556         }
5557         ce.removeClass('in'); // old...
5558         ce.setHeight(ce.getHeight());
5559         ce.removeClass('show');
5560         ce.addClass('collapsing');
5561         
5562         ce.on('transitionend', function() {
5563             ce.dom.style.height = '';
5564             ce.removeClass('collapsing');
5565             ce.addClass('collapse');
5566         }, this, { single: true} );
5567         ce.setHeight(0);
5568     }
5569     
5570     
5571     
5572 });
5573
5574
5575
5576  
5577
5578  /*
5579  * - LGPL
5580  *
5581  * navbar
5582  * 
5583  */
5584
5585 /**
5586  * @class Roo.bootstrap.NavSimplebar
5587  * @extends Roo.bootstrap.Navbar
5588  * Bootstrap Sidebar class
5589  *
5590  * @cfg {Boolean} inverse is inverted color
5591  * 
5592  * @cfg {String} type (nav | pills | tabs)
5593  * @cfg {Boolean} arrangement stacked | justified
5594  * @cfg {String} align (left | right) alignment
5595  * 
5596  * @cfg {Boolean} main (true|false) main nav bar? default false
5597  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5598  * 
5599  * @cfg {String} tag (header|footer|nav|div) default is nav 
5600
5601  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5602  * 
5603  * 
5604  * @constructor
5605  * Create a new Sidebar
5606  * @param {Object} config The config object
5607  */
5608
5609
5610 Roo.bootstrap.NavSimplebar = function(config){
5611     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5612 };
5613
5614 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5615     
5616     inverse: false,
5617     
5618     type: false,
5619     arrangement: '',
5620     align : false,
5621     
5622     weight : 'light',
5623     
5624     main : false,
5625     
5626     
5627     tag : false,
5628     
5629     
5630     getAutoCreate : function(){
5631         
5632         
5633         var cfg = {
5634             tag : this.tag || 'div',
5635             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5636         };
5637         if (['light','white'].indexOf(this.weight) > -1) {
5638             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5639         }
5640         cfg.cls += ' bg-' + this.weight;
5641         
5642         if (this.inverse) {
5643             cfg.cls += ' navbar-inverse';
5644             
5645         }
5646         
5647         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5648         
5649         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5650             return cfg;
5651         }
5652         
5653         
5654     
5655         
5656         cfg.cn = [
5657             {
5658                 cls: 'nav nav-' + this.xtype,
5659                 tag : 'ul'
5660             }
5661         ];
5662         
5663          
5664         this.type = this.type || 'nav';
5665         if (['tabs','pills'].indexOf(this.type) != -1) {
5666             cfg.cn[0].cls += ' nav-' + this.type
5667         
5668         
5669         } else {
5670             if (this.type!=='nav') {
5671                 Roo.log('nav type must be nav/tabs/pills')
5672             }
5673             cfg.cn[0].cls += ' navbar-nav'
5674         }
5675         
5676         
5677         
5678         
5679         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5680             cfg.cn[0].cls += ' nav-' + this.arrangement;
5681         }
5682         
5683         
5684         if (this.align === 'right') {
5685             cfg.cn[0].cls += ' navbar-right';
5686         }
5687         
5688         
5689         
5690         
5691         return cfg;
5692     
5693         
5694     }
5695     
5696     
5697     
5698 });
5699
5700
5701
5702  
5703
5704  
5705        /*
5706  * - LGPL
5707  *
5708  * navbar
5709  * navbar-fixed-top
5710  * navbar-expand-md  fixed-top 
5711  */
5712
5713 /**
5714  * @class Roo.bootstrap.NavHeaderbar
5715  * @extends Roo.bootstrap.NavSimplebar
5716  * Bootstrap Sidebar class
5717  *
5718  * @cfg {String} brand what is brand
5719  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5720  * @cfg {String} brand_href href of the brand
5721  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5722  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5723  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5724  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5725  * 
5726  * @constructor
5727  * Create a new Sidebar
5728  * @param {Object} config The config object
5729  */
5730
5731
5732 Roo.bootstrap.NavHeaderbar = function(config){
5733     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5734       
5735 };
5736
5737 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5738     
5739     position: '',
5740     brand: '',
5741     brand_href: false,
5742     srButton : true,
5743     autohide : false,
5744     desktopCenter : false,
5745    
5746     
5747     getAutoCreate : function(){
5748         
5749         var   cfg = {
5750             tag: this.nav || 'nav',
5751             cls: 'navbar navbar-expand-md',
5752             role: 'navigation',
5753             cn: []
5754         };
5755         
5756         var cn = cfg.cn;
5757         if (this.desktopCenter) {
5758             cn.push({cls : 'container', cn : []});
5759             cn = cn[0].cn;
5760         }
5761         
5762         if(this.srButton){
5763             var btn = {
5764                 tag: 'button',
5765                 type: 'button',
5766                 cls: 'navbar-toggle navbar-toggler',
5767                 'data-toggle': 'collapse',
5768                 cn: [
5769                     {
5770                         tag: 'span',
5771                         cls: 'sr-only',
5772                         html: 'Toggle navigation'
5773                     },
5774                     {
5775                         tag: 'span',
5776                         cls: 'icon-bar navbar-toggler-icon'
5777                     },
5778                     {
5779                         tag: 'span',
5780                         cls: 'icon-bar'
5781                     },
5782                     {
5783                         tag: 'span',
5784                         cls: 'icon-bar'
5785                     }
5786                 ]
5787             };
5788             
5789             cn.push( Roo.bootstrap.version == 4 ? btn : {
5790                 tag: 'div',
5791                 cls: 'navbar-header',
5792                 cn: [
5793                     btn
5794                 ]
5795             });
5796         }
5797         
5798         cn.push({
5799             tag: 'div',
5800             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5801             cn : []
5802         });
5803         
5804         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5805         
5806         if (['light','white'].indexOf(this.weight) > -1) {
5807             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5808         }
5809         cfg.cls += ' bg-' + this.weight;
5810         
5811         
5812         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5813             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5814             
5815             // tag can override this..
5816             
5817             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5818         }
5819         
5820         if (this.brand !== '') {
5821             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5822             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5823                 tag: 'a',
5824                 href: this.brand_href ? this.brand_href : '#',
5825                 cls: 'navbar-brand',
5826                 cn: [
5827                 this.brand
5828                 ]
5829             });
5830         }
5831         
5832         if(this.main){
5833             cfg.cls += ' main-nav';
5834         }
5835         
5836         
5837         return cfg;
5838
5839         
5840     },
5841     getHeaderChildContainer : function()
5842     {
5843         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5844             return this.el.select('.navbar-header',true).first();
5845         }
5846         
5847         return this.getChildContainer();
5848     },
5849     
5850     getChildContainer : function()
5851     {
5852          
5853         return this.el.select('.roo-navbar-collapse',true).first();
5854          
5855         
5856     },
5857     
5858     initEvents : function()
5859     {
5860         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5861         
5862         if (this.autohide) {
5863             
5864             var prevScroll = 0;
5865             var ft = this.el;
5866             
5867             Roo.get(document).on('scroll',function(e) {
5868                 var ns = Roo.get(document).getScroll().top;
5869                 var os = prevScroll;
5870                 prevScroll = ns;
5871                 
5872                 if(ns > os){
5873                     ft.removeClass('slideDown');
5874                     ft.addClass('slideUp');
5875                     return;
5876                 }
5877                 ft.removeClass('slideUp');
5878                 ft.addClass('slideDown');
5879                  
5880               
5881           },this);
5882         }
5883     }    
5884     
5885 });
5886
5887
5888
5889  
5890
5891  /*
5892  * - LGPL
5893  *
5894  * navbar
5895  * 
5896  */
5897
5898 /**
5899  * @class Roo.bootstrap.NavSidebar
5900  * @extends Roo.bootstrap.Navbar
5901  * Bootstrap Sidebar class
5902  * 
5903  * @constructor
5904  * Create a new Sidebar
5905  * @param {Object} config The config object
5906  */
5907
5908
5909 Roo.bootstrap.NavSidebar = function(config){
5910     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5911 };
5912
5913 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
5914     
5915     sidebar : true, // used by Navbar Item and NavbarGroup at present...
5916     
5917     getAutoCreate : function(){
5918         
5919         
5920         return  {
5921             tag: 'div',
5922             cls: 'sidebar sidebar-nav'
5923         };
5924     
5925         
5926     }
5927     
5928     
5929     
5930 });
5931
5932
5933
5934  
5935
5936  /*
5937  * - LGPL
5938  *
5939  * nav group
5940  * 
5941  */
5942
5943 /**
5944  * @class Roo.bootstrap.NavGroup
5945  * @extends Roo.bootstrap.Component
5946  * Bootstrap NavGroup class
5947  * @cfg {String} align (left|right)
5948  * @cfg {Boolean} inverse
5949  * @cfg {String} type (nav|pills|tab) default nav
5950  * @cfg {String} navId - reference Id for navbar.
5951
5952  * 
5953  * @constructor
5954  * Create a new nav group
5955  * @param {Object} config The config object
5956  */
5957
5958 Roo.bootstrap.NavGroup = function(config){
5959     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5960     this.navItems = [];
5961    
5962     Roo.bootstrap.NavGroup.register(this);
5963      this.addEvents({
5964         /**
5965              * @event changed
5966              * Fires when the active item changes
5967              * @param {Roo.bootstrap.NavGroup} this
5968              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5969              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
5970          */
5971         'changed': true
5972      });
5973     
5974 };
5975
5976 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
5977     
5978     align: '',
5979     inverse: false,
5980     form: false,
5981     type: 'nav',
5982     navId : '',
5983     // private
5984     
5985     navItems : false, 
5986     
5987     getAutoCreate : function()
5988     {
5989         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5990         
5991         cfg = {
5992             tag : 'ul',
5993             cls: 'nav' 
5994         };
5995         if (Roo.bootstrap.version == 4) {
5996             if (['tabs','pills'].indexOf(this.type) != -1) {
5997                 cfg.cls += ' nav-' + this.type; 
5998             } else {
5999                 // trying to remove so header bar can right align top?
6000                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6001                     // do not use on header bar... 
6002                     cfg.cls += ' navbar-nav';
6003                 }
6004             }
6005             
6006         } else {
6007             if (['tabs','pills'].indexOf(this.type) != -1) {
6008                 cfg.cls += ' nav-' + this.type
6009             } else {
6010                 if (this.type !== 'nav') {
6011                     Roo.log('nav type must be nav/tabs/pills')
6012                 }
6013                 cfg.cls += ' navbar-nav'
6014             }
6015         }
6016         
6017         if (this.parent() && this.parent().sidebar) {
6018             cfg = {
6019                 tag: 'ul',
6020                 cls: 'dashboard-menu sidebar-menu'
6021             };
6022             
6023             return cfg;
6024         }
6025         
6026         if (this.form === true) {
6027             cfg = {
6028                 tag: 'form',
6029                 cls: 'navbar-form form-inline'
6030             };
6031             //nav navbar-right ml-md-auto
6032             if (this.align === 'right') {
6033                 cfg.cls += ' navbar-right ml-md-auto';
6034             } else {
6035                 cfg.cls += ' navbar-left';
6036             }
6037         }
6038         
6039         if (this.align === 'right') {
6040             cfg.cls += ' navbar-right ml-md-auto';
6041         } else {
6042             cfg.cls += ' mr-auto';
6043         }
6044         
6045         if (this.inverse) {
6046             cfg.cls += ' navbar-inverse';
6047             
6048         }
6049         
6050         
6051         return cfg;
6052     },
6053     /**
6054     * sets the active Navigation item
6055     * @param {Roo.bootstrap.NavItem} the new current navitem
6056     */
6057     setActiveItem : function(item)
6058     {
6059         var prev = false;
6060         Roo.each(this.navItems, function(v){
6061             if (v == item) {
6062                 return ;
6063             }
6064             if (v.isActive()) {
6065                 v.setActive(false, true);
6066                 prev = v;
6067                 
6068             }
6069             
6070         });
6071
6072         item.setActive(true, true);
6073         this.fireEvent('changed', this, item, prev);
6074         
6075         
6076     },
6077     /**
6078     * gets the active Navigation item
6079     * @return {Roo.bootstrap.NavItem} the current navitem
6080     */
6081     getActive : function()
6082     {
6083         
6084         var prev = false;
6085         Roo.each(this.navItems, function(v){
6086             
6087             if (v.isActive()) {
6088                 prev = v;
6089                 
6090             }
6091             
6092         });
6093         return prev;
6094     },
6095     
6096     indexOfNav : function()
6097     {
6098         
6099         var prev = false;
6100         Roo.each(this.navItems, function(v,i){
6101             
6102             if (v.isActive()) {
6103                 prev = i;
6104                 
6105             }
6106             
6107         });
6108         return prev;
6109     },
6110     /**
6111     * adds a Navigation item
6112     * @param {Roo.bootstrap.NavItem} the navitem to add
6113     */
6114     addItem : function(cfg)
6115     {
6116         if (this.form && Roo.bootstrap.version == 4) {
6117             cfg.tag = 'div';
6118         }
6119         var cn = new Roo.bootstrap.NavItem(cfg);
6120         this.register(cn);
6121         cn.parentId = this.id;
6122         cn.onRender(this.el, null);
6123         return cn;
6124     },
6125     /**
6126     * register a Navigation item
6127     * @param {Roo.bootstrap.NavItem} the navitem to add
6128     */
6129     register : function(item)
6130     {
6131         this.navItems.push( item);
6132         item.navId = this.navId;
6133     
6134     },
6135     
6136     /**
6137     * clear all the Navigation item
6138     */
6139    
6140     clearAll : function()
6141     {
6142         this.navItems = [];
6143         this.el.dom.innerHTML = '';
6144     },
6145     
6146     getNavItem: function(tabId)
6147     {
6148         var ret = false;
6149         Roo.each(this.navItems, function(e) {
6150             if (e.tabId == tabId) {
6151                ret =  e;
6152                return false;
6153             }
6154             return true;
6155             
6156         });
6157         return ret;
6158     },
6159     
6160     setActiveNext : function()
6161     {
6162         var i = this.indexOfNav(this.getActive());
6163         if (i > this.navItems.length) {
6164             return;
6165         }
6166         this.setActiveItem(this.navItems[i+1]);
6167     },
6168     setActivePrev : function()
6169     {
6170         var i = this.indexOfNav(this.getActive());
6171         if (i  < 1) {
6172             return;
6173         }
6174         this.setActiveItem(this.navItems[i-1]);
6175     },
6176     clearWasActive : function(except) {
6177         Roo.each(this.navItems, function(e) {
6178             if (e.tabId != except.tabId && e.was_active) {
6179                e.was_active = false;
6180                return false;
6181             }
6182             return true;
6183             
6184         });
6185     },
6186     getWasActive : function ()
6187     {
6188         var r = false;
6189         Roo.each(this.navItems, function(e) {
6190             if (e.was_active) {
6191                r = e;
6192                return false;
6193             }
6194             return true;
6195             
6196         });
6197         return r;
6198     }
6199     
6200     
6201 });
6202
6203  
6204 Roo.apply(Roo.bootstrap.NavGroup, {
6205     
6206     groups: {},
6207      /**
6208     * register a Navigation Group
6209     * @param {Roo.bootstrap.NavGroup} the navgroup to add
6210     */
6211     register : function(navgrp)
6212     {
6213         this.groups[navgrp.navId] = navgrp;
6214         
6215     },
6216     /**
6217     * fetch a Navigation Group based on the navigation ID
6218     * @param {string} the navgroup to add
6219     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6220     */
6221     get: function(navId) {
6222         if (typeof(this.groups[navId]) == 'undefined') {
6223             return false;
6224             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6225         }
6226         return this.groups[navId] ;
6227     }
6228     
6229     
6230     
6231 });
6232
6233  /*
6234  * - LGPL
6235  *
6236  * row
6237  * 
6238  */
6239
6240 /**
6241  * @class Roo.bootstrap.NavItem
6242  * @extends Roo.bootstrap.Component
6243  * Bootstrap Navbar.NavItem class
6244  * @cfg {String} href  link to
6245  * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
6246
6247  * @cfg {String} html content of button
6248  * @cfg {String} badge text inside badge
6249  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6250  * @cfg {String} glyphicon DEPRICATED - use fa
6251  * @cfg {String} icon DEPRICATED - use fa
6252  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6253  * @cfg {Boolean} active Is item active
6254  * @cfg {Boolean} disabled Is item disabled
6255  
6256  * @cfg {Boolean} preventDefault (true | false) default false
6257  * @cfg {String} tabId the tab that this item activates.
6258  * @cfg {String} tagtype (a|span) render as a href or span?
6259  * @cfg {Boolean} animateRef (true|false) link to element default false  
6260   
6261  * @constructor
6262  * Create a new Navbar Item
6263  * @param {Object} config The config object
6264  */
6265 Roo.bootstrap.NavItem = function(config){
6266     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6267     this.addEvents({
6268         // raw events
6269         /**
6270          * @event click
6271          * The raw click event for the entire grid.
6272          * @param {Roo.EventObject} e
6273          */
6274         "click" : true,
6275          /**
6276             * @event changed
6277             * Fires when the active item active state changes
6278             * @param {Roo.bootstrap.NavItem} this
6279             * @param {boolean} state the new state
6280              
6281          */
6282         'changed': true,
6283         /**
6284             * @event scrollto
6285             * Fires when scroll to element
6286             * @param {Roo.bootstrap.NavItem} this
6287             * @param {Object} options
6288             * @param {Roo.EventObject} e
6289              
6290          */
6291         'scrollto': true
6292     });
6293    
6294 };
6295
6296 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6297     
6298     href: false,
6299     html: '',
6300     badge: '',
6301     icon: false,
6302     fa : false,
6303     glyphicon: false,
6304     active: false,
6305     preventDefault : false,
6306     tabId : false,
6307     tagtype : 'a',
6308     tag: 'li',
6309     disabled : false,
6310     animateRef : false,
6311     was_active : false,
6312     button_weight : '',
6313     button_outline : false,
6314     
6315     navLink: false,
6316     
6317     getAutoCreate : function(){
6318          
6319         var cfg = {
6320             tag: this.tag,
6321             cls: 'nav-item'
6322         };
6323         
6324         if (this.active) {
6325             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
6326         }
6327         if (this.disabled) {
6328             cfg.cls += ' disabled';
6329         }
6330         
6331         // BS4 only?
6332         if (this.button_weight.length) {
6333             cfg.tag = this.href ? 'a' : 'button';
6334             cfg.html = this.html || '';
6335             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6336             if (this.href) {
6337                 cfg.href = this.href;
6338             }
6339             if (this.fa) {
6340                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6341             }
6342             
6343             // menu .. should add dropdown-menu class - so no need for carat..
6344             
6345             if (this.badge !== '') {
6346                  
6347                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6348             }
6349             return cfg;
6350         }
6351         
6352         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6353             cfg.cn = [
6354                 {
6355                     tag: this.tagtype,
6356                     href : this.href || "#",
6357                     html: this.html || ''
6358                 }
6359             ];
6360             if (this.tagtype == 'a') {
6361                 cfg.cn[0].cls = 'nav-link';
6362             }
6363             if (this.icon) {
6364                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6365             }
6366             if (this.fa) {
6367                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6368             }
6369             if(this.glyphicon) {
6370                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6371             }
6372             
6373             if (this.menu) {
6374                 
6375                 cfg.cn[0].html += " <span class='caret'></span>";
6376              
6377             }
6378             
6379             if (this.badge !== '') {
6380                  
6381                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6382             }
6383         }
6384         
6385         
6386         
6387         return cfg;
6388     },
6389     onRender : function(ct, position)
6390     {
6391        // Roo.log("Call onRender: " + this.xtype);
6392         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6393             this.tag = 'div';
6394         }
6395         
6396         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6397         this.navLink = this.el.select('.nav-link',true).first();
6398         return ret;
6399     },
6400       
6401     
6402     initEvents: function() 
6403     {
6404         if (typeof (this.menu) != 'undefined') {
6405             this.menu.parentType = this.xtype;
6406             this.menu.triggerEl = this.el;
6407             this.menu = this.addxtype(Roo.apply({}, this.menu));
6408         }
6409         
6410         this.el.select('a',true).on('click', this.onClick, this);
6411         
6412         if(this.tagtype == 'span'){
6413             this.el.select('span',true).on('click', this.onClick, this);
6414         }
6415        
6416         // at this point parent should be available..
6417         this.parent().register(this);
6418     },
6419     
6420     onClick : function(e)
6421     {
6422         if (e.getTarget('.dropdown-menu-item')) {
6423             // did you click on a menu itemm.... - then don't trigger onclick..
6424             return;
6425         }
6426         
6427         if(
6428                 this.preventDefault || 
6429                 this.href == '#' 
6430         ){
6431             Roo.log("NavItem - prevent Default?");
6432             e.preventDefault();
6433         }
6434         
6435         if (this.disabled) {
6436             return;
6437         }
6438         
6439         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6440         if (tg && tg.transition) {
6441             Roo.log("waiting for the transitionend");
6442             return;
6443         }
6444         
6445         
6446         
6447         //Roo.log("fire event clicked");
6448         if(this.fireEvent('click', this, e) === false){
6449             return;
6450         };
6451         
6452         if(this.tagtype == 'span'){
6453             return;
6454         }
6455         
6456         //Roo.log(this.href);
6457         var ael = this.el.select('a',true).first();
6458         //Roo.log(ael);
6459         
6460         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6461             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6462             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6463                 return; // ignore... - it's a 'hash' to another page.
6464             }
6465             Roo.log("NavItem - prevent Default?");
6466             e.preventDefault();
6467             this.scrollToElement(e);
6468         }
6469         
6470         
6471         var p =  this.parent();
6472    
6473         if (['tabs','pills'].indexOf(p.type)!==-1) {
6474             if (typeof(p.setActiveItem) !== 'undefined') {
6475                 p.setActiveItem(this);
6476             }
6477         }
6478         
6479         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6480         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6481             // remove the collapsed menu expand...
6482             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6483         }
6484     },
6485     
6486     isActive: function () {
6487         return this.active
6488     },
6489     setActive : function(state, fire, is_was_active)
6490     {
6491         if (this.active && !state && this.navId) {
6492             this.was_active = true;
6493             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6494             if (nv) {
6495                 nv.clearWasActive(this);
6496             }
6497             
6498         }
6499         this.active = state;
6500         
6501         if (!state ) {
6502             this.el.removeClass('active');
6503             this.navLink ? this.navLink.removeClass('active') : false;
6504         } else if (!this.el.hasClass('active')) {
6505             
6506             this.el.addClass('active');
6507             if (Roo.bootstrap.version == 4 && this.navLink ) {
6508                 this.navLink.addClass('active');
6509             }
6510             
6511         }
6512         if (fire) {
6513             this.fireEvent('changed', this, state);
6514         }
6515         
6516         // show a panel if it's registered and related..
6517         
6518         if (!this.navId || !this.tabId || !state || is_was_active) {
6519             return;
6520         }
6521         
6522         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6523         if (!tg) {
6524             return;
6525         }
6526         var pan = tg.getPanelByName(this.tabId);
6527         if (!pan) {
6528             return;
6529         }
6530         // if we can not flip to new panel - go back to old nav highlight..
6531         if (false == tg.showPanel(pan)) {
6532             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6533             if (nv) {
6534                 var onav = nv.getWasActive();
6535                 if (onav) {
6536                     onav.setActive(true, false, true);
6537                 }
6538             }
6539             
6540         }
6541         
6542         
6543         
6544     },
6545      // this should not be here...
6546     setDisabled : function(state)
6547     {
6548         this.disabled = state;
6549         if (!state ) {
6550             this.el.removeClass('disabled');
6551         } else if (!this.el.hasClass('disabled')) {
6552             this.el.addClass('disabled');
6553         }
6554         
6555     },
6556     
6557     /**
6558      * Fetch the element to display the tooltip on.
6559      * @return {Roo.Element} defaults to this.el
6560      */
6561     tooltipEl : function()
6562     {
6563         return this.el.select('' + this.tagtype + '', true).first();
6564     },
6565     
6566     scrollToElement : function(e)
6567     {
6568         var c = document.body;
6569         
6570         /*
6571          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6572          */
6573         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6574             c = document.documentElement;
6575         }
6576         
6577         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6578         
6579         if(!target){
6580             return;
6581         }
6582
6583         var o = target.calcOffsetsTo(c);
6584         
6585         var options = {
6586             target : target,
6587             value : o[1]
6588         };
6589         
6590         this.fireEvent('scrollto', this, options, e);
6591         
6592         Roo.get(c).scrollTo('top', options.value, true);
6593         
6594         return;
6595     }
6596 });
6597  
6598
6599  /*
6600  * - LGPL
6601  *
6602  * sidebar item
6603  *
6604  *  li
6605  *    <span> icon </span>
6606  *    <span> text </span>
6607  *    <span>badge </span>
6608  */
6609
6610 /**
6611  * @class Roo.bootstrap.NavSidebarItem
6612  * @extends Roo.bootstrap.NavItem
6613  * Bootstrap Navbar.NavSidebarItem class
6614  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6615  * {Boolean} open is the menu open
6616  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6617  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6618  * {String} buttonSize (sm|md|lg)the extra classes for the button
6619  * {Boolean} showArrow show arrow next to the text (default true)
6620  * @constructor
6621  * Create a new Navbar Button
6622  * @param {Object} config The config object
6623  */
6624 Roo.bootstrap.NavSidebarItem = function(config){
6625     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6626     this.addEvents({
6627         // raw events
6628         /**
6629          * @event click
6630          * The raw click event for the entire grid.
6631          * @param {Roo.EventObject} e
6632          */
6633         "click" : true,
6634          /**
6635             * @event changed
6636             * Fires when the active item active state changes
6637             * @param {Roo.bootstrap.NavSidebarItem} this
6638             * @param {boolean} state the new state
6639              
6640          */
6641         'changed': true
6642     });
6643    
6644 };
6645
6646 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6647     
6648     badgeWeight : 'default',
6649     
6650     open: false,
6651     
6652     buttonView : false,
6653     
6654     buttonWeight : 'default',
6655     
6656     buttonSize : 'md',
6657     
6658     showArrow : true,
6659     
6660     getAutoCreate : function(){
6661         
6662         
6663         var a = {
6664                 tag: 'a',
6665                 href : this.href || '#',
6666                 cls: '',
6667                 html : '',
6668                 cn : []
6669         };
6670         
6671         if(this.buttonView){
6672             a = {
6673                 tag: 'button',
6674                 href : this.href || '#',
6675                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6676                 html : this.html,
6677                 cn : []
6678             };
6679         }
6680         
6681         var cfg = {
6682             tag: 'li',
6683             cls: '',
6684             cn: [ a ]
6685         };
6686         
6687         if (this.active) {
6688             cfg.cls += ' active';
6689         }
6690         
6691         if (this.disabled) {
6692             cfg.cls += ' disabled';
6693         }
6694         if (this.open) {
6695             cfg.cls += ' open x-open';
6696         }
6697         // left icon..
6698         if (this.glyphicon || this.icon) {
6699             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6700             a.cn.push({ tag : 'i', cls : c }) ;
6701         }
6702         
6703         if(!this.buttonView){
6704             var span = {
6705                 tag: 'span',
6706                 html : this.html || ''
6707             };
6708
6709             a.cn.push(span);
6710             
6711         }
6712         
6713         if (this.badge !== '') {
6714             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6715         }
6716         
6717         if (this.menu) {
6718             
6719             if(this.showArrow){
6720                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6721             }
6722             
6723             a.cls += ' dropdown-toggle treeview' ;
6724         }
6725         
6726         return cfg;
6727     },
6728     
6729     initEvents : function()
6730     { 
6731         if (typeof (this.menu) != 'undefined') {
6732             this.menu.parentType = this.xtype;
6733             this.menu.triggerEl = this.el;
6734             this.menu = this.addxtype(Roo.apply({}, this.menu));
6735         }
6736         
6737         this.el.on('click', this.onClick, this);
6738         
6739         if(this.badge !== ''){
6740             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6741         }
6742         
6743     },
6744     
6745     onClick : function(e)
6746     {
6747         if(this.disabled){
6748             e.preventDefault();
6749             return;
6750         }
6751         
6752         if(this.preventDefault){
6753             e.preventDefault();
6754         }
6755         
6756         this.fireEvent('click', this, e);
6757     },
6758     
6759     disable : function()
6760     {
6761         this.setDisabled(true);
6762     },
6763     
6764     enable : function()
6765     {
6766         this.setDisabled(false);
6767     },
6768     
6769     setDisabled : function(state)
6770     {
6771         if(this.disabled == state){
6772             return;
6773         }
6774         
6775         this.disabled = state;
6776         
6777         if (state) {
6778             this.el.addClass('disabled');
6779             return;
6780         }
6781         
6782         this.el.removeClass('disabled');
6783         
6784         return;
6785     },
6786     
6787     setActive : function(state)
6788     {
6789         if(this.active == state){
6790             return;
6791         }
6792         
6793         this.active = state;
6794         
6795         if (state) {
6796             this.el.addClass('active');
6797             return;
6798         }
6799         
6800         this.el.removeClass('active');
6801         
6802         return;
6803     },
6804     
6805     isActive: function () 
6806     {
6807         return this.active;
6808     },
6809     
6810     setBadge : function(str)
6811     {
6812         if(!this.badgeEl){
6813             return;
6814         }
6815         
6816         this.badgeEl.dom.innerHTML = str;
6817     }
6818     
6819    
6820      
6821  
6822 });
6823  
6824
6825  /*
6826  * - LGPL
6827  *
6828  * row
6829  * 
6830  */
6831
6832 /**
6833  * @class Roo.bootstrap.Row
6834  * @extends Roo.bootstrap.Component
6835  * Bootstrap Row class (contains columns...)
6836  * 
6837  * @constructor
6838  * Create a new Row
6839  * @param {Object} config The config object
6840  */
6841
6842 Roo.bootstrap.Row = function(config){
6843     Roo.bootstrap.Row.superclass.constructor.call(this, config);
6844 };
6845
6846 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
6847     
6848     getAutoCreate : function(){
6849        return {
6850             cls: 'row clearfix'
6851        };
6852     }
6853     
6854     
6855 });
6856
6857  
6858
6859  /*
6860  * - LGPL
6861  *
6862  * pagination
6863  * 
6864  */
6865
6866 /**
6867  * @class Roo.bootstrap.Pagination
6868  * @extends Roo.bootstrap.Component
6869  * Bootstrap Pagination class
6870  * @cfg {String} size xs | sm | md | lg
6871  * @cfg {Boolean} inverse false | true
6872  * 
6873  * @constructor
6874  * Create a new Pagination
6875  * @param {Object} config The config object
6876  */
6877
6878 Roo.bootstrap.Pagination = function(config){
6879     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6880 };
6881
6882 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
6883     
6884     cls: false,
6885     size: false,
6886     inverse: false,
6887     
6888     getAutoCreate : function(){
6889         var cfg = {
6890             tag: 'ul',
6891                 cls: 'pagination'
6892         };
6893         if (this.inverse) {
6894             cfg.cls += ' inverse';
6895         }
6896         if (this.html) {
6897             cfg.html=this.html;
6898         }
6899         if (this.cls) {
6900             cfg.cls += " " + this.cls;
6901         }
6902         return cfg;
6903     }
6904    
6905 });
6906
6907  
6908
6909  /*
6910  * - LGPL
6911  *
6912  * Pagination item
6913  * 
6914  */
6915
6916
6917 /**
6918  * @class Roo.bootstrap.PaginationItem
6919  * @extends Roo.bootstrap.Component
6920  * Bootstrap PaginationItem class
6921  * @cfg {String} html text
6922  * @cfg {String} href the link
6923  * @cfg {Boolean} preventDefault (true | false) default true
6924  * @cfg {Boolean} active (true | false) default false
6925  * @cfg {Boolean} disabled default false
6926  * 
6927  * 
6928  * @constructor
6929  * Create a new PaginationItem
6930  * @param {Object} config The config object
6931  */
6932
6933
6934 Roo.bootstrap.PaginationItem = function(config){
6935     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6936     this.addEvents({
6937         // raw events
6938         /**
6939          * @event click
6940          * The raw click event for the entire grid.
6941          * @param {Roo.EventObject} e
6942          */
6943         "click" : true
6944     });
6945 };
6946
6947 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
6948     
6949     href : false,
6950     html : false,
6951     preventDefault: true,
6952     active : false,
6953     cls : false,
6954     disabled: false,
6955     
6956     getAutoCreate : function(){
6957         var cfg= {
6958             tag: 'li',
6959             cn: [
6960                 {
6961                     tag : 'a',
6962                     href : this.href ? this.href : '#',
6963                     html : this.html ? this.html : ''
6964                 }
6965             ]
6966         };
6967         
6968         if(this.cls){
6969             cfg.cls = this.cls;
6970         }
6971         
6972         if(this.disabled){
6973             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6974         }
6975         
6976         if(this.active){
6977             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6978         }
6979         
6980         return cfg;
6981     },
6982     
6983     initEvents: function() {
6984         
6985         this.el.on('click', this.onClick, this);
6986         
6987     },
6988     onClick : function(e)
6989     {
6990         Roo.log('PaginationItem on click ');
6991         if(this.preventDefault){
6992             e.preventDefault();
6993         }
6994         
6995         if(this.disabled){
6996             return;
6997         }
6998         
6999         this.fireEvent('click', this, e);
7000     }
7001    
7002 });
7003
7004  
7005
7006  /*
7007  * - LGPL
7008  *
7009  * slider
7010  * 
7011  */
7012
7013
7014 /**
7015  * @class Roo.bootstrap.Slider
7016  * @extends Roo.bootstrap.Component
7017  * Bootstrap Slider class
7018  *    
7019  * @constructor
7020  * Create a new Slider
7021  * @param {Object} config The config object
7022  */
7023
7024 Roo.bootstrap.Slider = function(config){
7025     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7026 };
7027
7028 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7029     
7030     getAutoCreate : function(){
7031         
7032         var cfg = {
7033             tag: 'div',
7034             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7035             cn: [
7036                 {
7037                     tag: 'a',
7038                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7039                 }
7040             ]
7041         };
7042         
7043         return cfg;
7044     }
7045    
7046 });
7047
7048  /*
7049  * Based on:
7050  * Ext JS Library 1.1.1
7051  * Copyright(c) 2006-2007, Ext JS, LLC.
7052  *
7053  * Originally Released Under LGPL - original licence link has changed is not relivant.
7054  *
7055  * Fork - LGPL
7056  * <script type="text/javascript">
7057  */
7058  
7059
7060 /**
7061  * @class Roo.grid.ColumnModel
7062  * @extends Roo.util.Observable
7063  * This is the default implementation of a ColumnModel used by the Grid. It defines
7064  * the columns in the grid.
7065  * <br>Usage:<br>
7066  <pre><code>
7067  var colModel = new Roo.grid.ColumnModel([
7068         {header: "Ticker", width: 60, sortable: true, locked: true},
7069         {header: "Company Name", width: 150, sortable: true},
7070         {header: "Market Cap.", width: 100, sortable: true},
7071         {header: "$ Sales", width: 100, sortable: true, renderer: money},
7072         {header: "Employees", width: 100, sortable: true, resizable: false}
7073  ]);
7074  </code></pre>
7075  * <p>
7076  
7077  * The config options listed for this class are options which may appear in each
7078  * individual column definition.
7079  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7080  * @constructor
7081  * @param {Object} config An Array of column config objects. See this class's
7082  * config objects for details.
7083 */
7084 Roo.grid.ColumnModel = function(config){
7085         /**
7086      * The config passed into the constructor
7087      */
7088     this.config = config;
7089     this.lookup = {};
7090
7091     // if no id, create one
7092     // if the column does not have a dataIndex mapping,
7093     // map it to the order it is in the config
7094     for(var i = 0, len = config.length; i < len; i++){
7095         var c = config[i];
7096         if(typeof c.dataIndex == "undefined"){
7097             c.dataIndex = i;
7098         }
7099         if(typeof c.renderer == "string"){
7100             c.renderer = Roo.util.Format[c.renderer];
7101         }
7102         if(typeof c.id == "undefined"){
7103             c.id = Roo.id();
7104         }
7105         if(c.editor && c.editor.xtype){
7106             c.editor  = Roo.factory(c.editor, Roo.grid);
7107         }
7108         if(c.editor && c.editor.isFormField){
7109             c.editor = new Roo.grid.GridEditor(c.editor);
7110         }
7111         this.lookup[c.id] = c;
7112     }
7113
7114     /**
7115      * The width of columns which have no width specified (defaults to 100)
7116      * @type Number
7117      */
7118     this.defaultWidth = 100;
7119
7120     /**
7121      * Default sortable of columns which have no sortable specified (defaults to false)
7122      * @type Boolean
7123      */
7124     this.defaultSortable = false;
7125
7126     this.addEvents({
7127         /**
7128              * @event widthchange
7129              * Fires when the width of a column changes.
7130              * @param {ColumnModel} this
7131              * @param {Number} columnIndex The column index
7132              * @param {Number} newWidth The new width
7133              */
7134             "widthchange": true,
7135         /**
7136              * @event headerchange
7137              * Fires when the text of a header changes.
7138              * @param {ColumnModel} this
7139              * @param {Number} columnIndex The column index
7140              * @param {Number} newText The new header text
7141              */
7142             "headerchange": true,
7143         /**
7144              * @event hiddenchange
7145              * Fires when a column is hidden or "unhidden".
7146              * @param {ColumnModel} this
7147              * @param {Number} columnIndex The column index
7148              * @param {Boolean} hidden true if hidden, false otherwise
7149              */
7150             "hiddenchange": true,
7151             /**
7152          * @event columnmoved
7153          * Fires when a column is moved.
7154          * @param {ColumnModel} this
7155          * @param {Number} oldIndex
7156          * @param {Number} newIndex
7157          */
7158         "columnmoved" : true,
7159         /**
7160          * @event columlockchange
7161          * Fires when a column's locked state is changed
7162          * @param {ColumnModel} this
7163          * @param {Number} colIndex
7164          * @param {Boolean} locked true if locked
7165          */
7166         "columnlockchange" : true
7167     });
7168     Roo.grid.ColumnModel.superclass.constructor.call(this);
7169 };
7170 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7171     /**
7172      * @cfg {String} header The header text to display in the Grid view.
7173      */
7174     /**
7175      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7176      * {@link Roo.data.Record} definition from which to draw the column's value. If not
7177      * specified, the column's index is used as an index into the Record's data Array.
7178      */
7179     /**
7180      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7181      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7182      */
7183     /**
7184      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7185      * Defaults to the value of the {@link #defaultSortable} property.
7186      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7187      */
7188     /**
7189      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
7190      */
7191     /**
7192      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
7193      */
7194     /**
7195      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7196      */
7197     /**
7198      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7199      */
7200     /**
7201      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7202      * given the cell's data value. See {@link #setRenderer}. If not specified, the
7203      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7204      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7205      */
7206        /**
7207      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
7208      */
7209     /**
7210      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
7211      */
7212     /**
7213      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
7214      */
7215     /**
7216      * @cfg {String} cursor (Optional)
7217      */
7218     /**
7219      * @cfg {String} tooltip (Optional)
7220      */
7221     /**
7222      * @cfg {Number} xs (Optional)
7223      */
7224     /**
7225      * @cfg {Number} sm (Optional)
7226      */
7227     /**
7228      * @cfg {Number} md (Optional)
7229      */
7230     /**
7231      * @cfg {Number} lg (Optional)
7232      */
7233     /**
7234      * Returns the id of the column at the specified index.
7235      * @param {Number} index The column index
7236      * @return {String} the id
7237      */
7238     getColumnId : function(index){
7239         return this.config[index].id;
7240     },
7241
7242     /**
7243      * Returns the column for a specified id.
7244      * @param {String} id The column id
7245      * @return {Object} the column
7246      */
7247     getColumnById : function(id){
7248         return this.lookup[id];
7249     },
7250
7251     
7252     /**
7253      * Returns the column for a specified dataIndex.
7254      * @param {String} dataIndex The column dataIndex
7255      * @return {Object|Boolean} the column or false if not found
7256      */
7257     getColumnByDataIndex: function(dataIndex){
7258         var index = this.findColumnIndex(dataIndex);
7259         return index > -1 ? this.config[index] : false;
7260     },
7261     
7262     /**
7263      * Returns the index for a specified column id.
7264      * @param {String} id The column id
7265      * @return {Number} the index, or -1 if not found
7266      */
7267     getIndexById : function(id){
7268         for(var i = 0, len = this.config.length; i < len; i++){
7269             if(this.config[i].id == id){
7270                 return i;
7271             }
7272         }
7273         return -1;
7274     },
7275     
7276     /**
7277      * Returns the index for a specified column dataIndex.
7278      * @param {String} dataIndex The column dataIndex
7279      * @return {Number} the index, or -1 if not found
7280      */
7281     
7282     findColumnIndex : function(dataIndex){
7283         for(var i = 0, len = this.config.length; i < len; i++){
7284             if(this.config[i].dataIndex == dataIndex){
7285                 return i;
7286             }
7287         }
7288         return -1;
7289     },
7290     
7291     
7292     moveColumn : function(oldIndex, newIndex){
7293         var c = this.config[oldIndex];
7294         this.config.splice(oldIndex, 1);
7295         this.config.splice(newIndex, 0, c);
7296         this.dataMap = null;
7297         this.fireEvent("columnmoved", this, oldIndex, newIndex);
7298     },
7299
7300     isLocked : function(colIndex){
7301         return this.config[colIndex].locked === true;
7302     },
7303
7304     setLocked : function(colIndex, value, suppressEvent){
7305         if(this.isLocked(colIndex) == value){
7306             return;
7307         }
7308         this.config[colIndex].locked = value;
7309         if(!suppressEvent){
7310             this.fireEvent("columnlockchange", this, colIndex, value);
7311         }
7312     },
7313
7314     getTotalLockedWidth : function(){
7315         var totalWidth = 0;
7316         for(var i = 0; i < this.config.length; i++){
7317             if(this.isLocked(i) && !this.isHidden(i)){
7318                 this.totalWidth += this.getColumnWidth(i);
7319             }
7320         }
7321         return totalWidth;
7322     },
7323
7324     getLockedCount : function(){
7325         for(var i = 0, len = this.config.length; i < len; i++){
7326             if(!this.isLocked(i)){
7327                 return i;
7328             }
7329         }
7330         
7331         return this.config.length;
7332     },
7333
7334     /**
7335      * Returns the number of columns.
7336      * @return {Number}
7337      */
7338     getColumnCount : function(visibleOnly){
7339         if(visibleOnly === true){
7340             var c = 0;
7341             for(var i = 0, len = this.config.length; i < len; i++){
7342                 if(!this.isHidden(i)){
7343                     c++;
7344                 }
7345             }
7346             return c;
7347         }
7348         return this.config.length;
7349     },
7350
7351     /**
7352      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7353      * @param {Function} fn
7354      * @param {Object} scope (optional)
7355      * @return {Array} result
7356      */
7357     getColumnsBy : function(fn, scope){
7358         var r = [];
7359         for(var i = 0, len = this.config.length; i < len; i++){
7360             var c = this.config[i];
7361             if(fn.call(scope||this, c, i) === true){
7362                 r[r.length] = c;
7363             }
7364         }
7365         return r;
7366     },
7367
7368     /**
7369      * Returns true if the specified column is sortable.
7370      * @param {Number} col The column index
7371      * @return {Boolean}
7372      */
7373     isSortable : function(col){
7374         if(typeof this.config[col].sortable == "undefined"){
7375             return this.defaultSortable;
7376         }
7377         return this.config[col].sortable;
7378     },
7379
7380     /**
7381      * Returns the rendering (formatting) function defined for the column.
7382      * @param {Number} col The column index.
7383      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7384      */
7385     getRenderer : function(col){
7386         if(!this.config[col].renderer){
7387             return Roo.grid.ColumnModel.defaultRenderer;
7388         }
7389         return this.config[col].renderer;
7390     },
7391
7392     /**
7393      * Sets the rendering (formatting) function for a column.
7394      * @param {Number} col The column index
7395      * @param {Function} fn The function to use to process the cell's raw data
7396      * to return HTML markup for the grid view. The render function is called with
7397      * the following parameters:<ul>
7398      * <li>Data value.</li>
7399      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7400      * <li>css A CSS style string to apply to the table cell.</li>
7401      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7402      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7403      * <li>Row index</li>
7404      * <li>Column index</li>
7405      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7406      */
7407     setRenderer : function(col, fn){
7408         this.config[col].renderer = fn;
7409     },
7410
7411     /**
7412      * Returns the width for the specified column.
7413      * @param {Number} col The column index
7414      * @return {Number}
7415      */
7416     getColumnWidth : function(col){
7417         return this.config[col].width * 1 || this.defaultWidth;
7418     },
7419
7420     /**
7421      * Sets the width for a column.
7422      * @param {Number} col The column index
7423      * @param {Number} width The new width
7424      */
7425     setColumnWidth : function(col, width, suppressEvent){
7426         this.config[col].width = width;
7427         this.totalWidth = null;
7428         if(!suppressEvent){
7429              this.fireEvent("widthchange", this, col, width);
7430         }
7431     },
7432
7433     /**
7434      * Returns the total width of all columns.
7435      * @param {Boolean} includeHidden True to include hidden column widths
7436      * @return {Number}
7437      */
7438     getTotalWidth : function(includeHidden){
7439         if(!this.totalWidth){
7440             this.totalWidth = 0;
7441             for(var i = 0, len = this.config.length; i < len; i++){
7442                 if(includeHidden || !this.isHidden(i)){
7443                     this.totalWidth += this.getColumnWidth(i);
7444                 }
7445             }
7446         }
7447         return this.totalWidth;
7448     },
7449
7450     /**
7451      * Returns the header for the specified column.
7452      * @param {Number} col The column index
7453      * @return {String}
7454      */
7455     getColumnHeader : function(col){
7456         return this.config[col].header;
7457     },
7458
7459     /**
7460      * Sets the header for a column.
7461      * @param {Number} col The column index
7462      * @param {String} header The new header
7463      */
7464     setColumnHeader : function(col, header){
7465         this.config[col].header = header;
7466         this.fireEvent("headerchange", this, col, header);
7467     },
7468
7469     /**
7470      * Returns the tooltip for the specified column.
7471      * @param {Number} col The column index
7472      * @return {String}
7473      */
7474     getColumnTooltip : function(col){
7475             return this.config[col].tooltip;
7476     },
7477     /**
7478      * Sets the tooltip for a column.
7479      * @param {Number} col The column index
7480      * @param {String} tooltip The new tooltip
7481      */
7482     setColumnTooltip : function(col, tooltip){
7483             this.config[col].tooltip = tooltip;
7484     },
7485
7486     /**
7487      * Returns the dataIndex for the specified column.
7488      * @param {Number} col The column index
7489      * @return {Number}
7490      */
7491     getDataIndex : function(col){
7492         return this.config[col].dataIndex;
7493     },
7494
7495     /**
7496      * Sets the dataIndex for a column.
7497      * @param {Number} col The column index
7498      * @param {Number} dataIndex The new dataIndex
7499      */
7500     setDataIndex : function(col, dataIndex){
7501         this.config[col].dataIndex = dataIndex;
7502     },
7503
7504     
7505     
7506     /**
7507      * Returns true if the cell is editable.
7508      * @param {Number} colIndex The column index
7509      * @param {Number} rowIndex The row index - this is nto actually used..?
7510      * @return {Boolean}
7511      */
7512     isCellEditable : function(colIndex, rowIndex){
7513         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7514     },
7515
7516     /**
7517      * Returns the editor defined for the cell/column.
7518      * return false or null to disable editing.
7519      * @param {Number} colIndex The column index
7520      * @param {Number} rowIndex The row index
7521      * @return {Object}
7522      */
7523     getCellEditor : function(colIndex, rowIndex){
7524         return this.config[colIndex].editor;
7525     },
7526
7527     /**
7528      * Sets if a column is editable.
7529      * @param {Number} col The column index
7530      * @param {Boolean} editable True if the column is editable
7531      */
7532     setEditable : function(col, editable){
7533         this.config[col].editable = editable;
7534     },
7535
7536
7537     /**
7538      * Returns true if the column is hidden.
7539      * @param {Number} colIndex The column index
7540      * @return {Boolean}
7541      */
7542     isHidden : function(colIndex){
7543         return this.config[colIndex].hidden;
7544     },
7545
7546
7547     /**
7548      * Returns true if the column width cannot be changed
7549      */
7550     isFixed : function(colIndex){
7551         return this.config[colIndex].fixed;
7552     },
7553
7554     /**
7555      * Returns true if the column can be resized
7556      * @return {Boolean}
7557      */
7558     isResizable : function(colIndex){
7559         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7560     },
7561     /**
7562      * Sets if a column is hidden.
7563      * @param {Number} colIndex The column index
7564      * @param {Boolean} hidden True if the column is hidden
7565      */
7566     setHidden : function(colIndex, hidden){
7567         this.config[colIndex].hidden = hidden;
7568         this.totalWidth = null;
7569         this.fireEvent("hiddenchange", this, colIndex, hidden);
7570     },
7571
7572     /**
7573      * Sets the editor for a column.
7574      * @param {Number} col The column index
7575      * @param {Object} editor The editor object
7576      */
7577     setEditor : function(col, editor){
7578         this.config[col].editor = editor;
7579     }
7580 });
7581
7582 Roo.grid.ColumnModel.defaultRenderer = function(value)
7583 {
7584     if(typeof value == "object") {
7585         return value;
7586     }
7587         if(typeof value == "string" && value.length < 1){
7588             return "&#160;";
7589         }
7590     
7591         return String.format("{0}", value);
7592 };
7593
7594 // Alias for backwards compatibility
7595 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7596 /*
7597  * Based on:
7598  * Ext JS Library 1.1.1
7599  * Copyright(c) 2006-2007, Ext JS, LLC.
7600  *
7601  * Originally Released Under LGPL - original licence link has changed is not relivant.
7602  *
7603  * Fork - LGPL
7604  * <script type="text/javascript">
7605  */
7606  
7607 /**
7608  * @class Roo.LoadMask
7609  * A simple utility class for generically masking elements while loading data.  If the element being masked has
7610  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7611  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
7612  * element's UpdateManager load indicator and will be destroyed after the initial load.
7613  * @constructor
7614  * Create a new LoadMask
7615  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7616  * @param {Object} config The config object
7617  */
7618 Roo.LoadMask = function(el, config){
7619     this.el = Roo.get(el);
7620     Roo.apply(this, config);
7621     if(this.store){
7622         this.store.on('beforeload', this.onBeforeLoad, this);
7623         this.store.on('load', this.onLoad, this);
7624         this.store.on('loadexception', this.onLoadException, this);
7625         this.removeMask = false;
7626     }else{
7627         var um = this.el.getUpdateManager();
7628         um.showLoadIndicator = false; // disable the default indicator
7629         um.on('beforeupdate', this.onBeforeLoad, this);
7630         um.on('update', this.onLoad, this);
7631         um.on('failure', this.onLoad, this);
7632         this.removeMask = true;
7633     }
7634 };
7635
7636 Roo.LoadMask.prototype = {
7637     /**
7638      * @cfg {Boolean} removeMask
7639      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7640      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7641      */
7642     /**
7643      * @cfg {String} msg
7644      * The text to display in a centered loading message box (defaults to 'Loading...')
7645      */
7646     msg : 'Loading...',
7647     /**
7648      * @cfg {String} msgCls
7649      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7650      */
7651     msgCls : 'x-mask-loading',
7652
7653     /**
7654      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7655      * @type Boolean
7656      */
7657     disabled: false,
7658
7659     /**
7660      * Disables the mask to prevent it from being displayed
7661      */
7662     disable : function(){
7663        this.disabled = true;
7664     },
7665
7666     /**
7667      * Enables the mask so that it can be displayed
7668      */
7669     enable : function(){
7670         this.disabled = false;
7671     },
7672     
7673     onLoadException : function()
7674     {
7675         Roo.log(arguments);
7676         
7677         if (typeof(arguments[3]) != 'undefined') {
7678             Roo.MessageBox.alert("Error loading",arguments[3]);
7679         } 
7680         /*
7681         try {
7682             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7683                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7684             }   
7685         } catch(e) {
7686             
7687         }
7688         */
7689     
7690         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7691     },
7692     // private
7693     onLoad : function()
7694     {
7695         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7696     },
7697
7698     // private
7699     onBeforeLoad : function(){
7700         if(!this.disabled){
7701             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7702         }
7703     },
7704
7705     // private
7706     destroy : function(){
7707         if(this.store){
7708             this.store.un('beforeload', this.onBeforeLoad, this);
7709             this.store.un('load', this.onLoad, this);
7710             this.store.un('loadexception', this.onLoadException, this);
7711         }else{
7712             var um = this.el.getUpdateManager();
7713             um.un('beforeupdate', this.onBeforeLoad, this);
7714             um.un('update', this.onLoad, this);
7715             um.un('failure', this.onLoad, this);
7716         }
7717     }
7718 };/*
7719  * - LGPL
7720  *
7721  * table
7722  * 
7723  */
7724
7725 /**
7726  * @class Roo.bootstrap.Table
7727  * @extends Roo.bootstrap.Component
7728  * Bootstrap Table class
7729  * @cfg {String} cls table class
7730  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7731  * @cfg {String} bgcolor Specifies the background color for a table
7732  * @cfg {Number} border Specifies whether the table cells should have borders or not
7733  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7734  * @cfg {Number} cellspacing Specifies the space between cells
7735  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7736  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7737  * @cfg {String} sortable Specifies that the table should be sortable
7738  * @cfg {String} summary Specifies a summary of the content of a table
7739  * @cfg {Number} width Specifies the width of a table
7740  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7741  * 
7742  * @cfg {boolean} striped Should the rows be alternative striped
7743  * @cfg {boolean} bordered Add borders to the table
7744  * @cfg {boolean} hover Add hover highlighting
7745  * @cfg {boolean} condensed Format condensed
7746  * @cfg {boolean} responsive Format condensed
7747  * @cfg {Boolean} loadMask (true|false) default false
7748  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7749  * @cfg {Boolean} headerShow (true|false) generate thead, default true
7750  * @cfg {Boolean} rowSelection (true|false) default false
7751  * @cfg {Boolean} cellSelection (true|false) default false
7752  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7753  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
7754  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
7755  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
7756  
7757  * 
7758  * @constructor
7759  * Create a new Table
7760  * @param {Object} config The config object
7761  */
7762
7763 Roo.bootstrap.Table = function(config){
7764     Roo.bootstrap.Table.superclass.constructor.call(this, config);
7765     
7766   
7767     
7768     // BC...
7769     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7770     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7771     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7772     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7773     
7774     this.sm = this.sm || {xtype: 'RowSelectionModel'};
7775     if (this.sm) {
7776         this.sm.grid = this;
7777         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7778         this.sm = this.selModel;
7779         this.sm.xmodule = this.xmodule || false;
7780     }
7781     
7782     if (this.cm && typeof(this.cm.config) == 'undefined') {
7783         this.colModel = new Roo.grid.ColumnModel(this.cm);
7784         this.cm = this.colModel;
7785         this.cm.xmodule = this.xmodule || false;
7786     }
7787     if (this.store) {
7788         this.store= Roo.factory(this.store, Roo.data);
7789         this.ds = this.store;
7790         this.ds.xmodule = this.xmodule || false;
7791          
7792     }
7793     if (this.footer && this.store) {
7794         this.footer.dataSource = this.ds;
7795         this.footer = Roo.factory(this.footer);
7796     }
7797     
7798     /** @private */
7799     this.addEvents({
7800         /**
7801          * @event cellclick
7802          * Fires when a cell is clicked
7803          * @param {Roo.bootstrap.Table} this
7804          * @param {Roo.Element} el
7805          * @param {Number} rowIndex
7806          * @param {Number} columnIndex
7807          * @param {Roo.EventObject} e
7808          */
7809         "cellclick" : true,
7810         /**
7811          * @event celldblclick
7812          * Fires when a cell is double clicked
7813          * @param {Roo.bootstrap.Table} this
7814          * @param {Roo.Element} el
7815          * @param {Number} rowIndex
7816          * @param {Number} columnIndex
7817          * @param {Roo.EventObject} e
7818          */
7819         "celldblclick" : true,
7820         /**
7821          * @event rowclick
7822          * Fires when a row is clicked
7823          * @param {Roo.bootstrap.Table} this
7824          * @param {Roo.Element} el
7825          * @param {Number} rowIndex
7826          * @param {Roo.EventObject} e
7827          */
7828         "rowclick" : true,
7829         /**
7830          * @event rowdblclick
7831          * Fires when a row is double clicked
7832          * @param {Roo.bootstrap.Table} this
7833          * @param {Roo.Element} el
7834          * @param {Number} rowIndex
7835          * @param {Roo.EventObject} e
7836          */
7837         "rowdblclick" : true,
7838         /**
7839          * @event mouseover
7840          * Fires when a mouseover occur
7841          * @param {Roo.bootstrap.Table} this
7842          * @param {Roo.Element} el
7843          * @param {Number} rowIndex
7844          * @param {Number} columnIndex
7845          * @param {Roo.EventObject} e
7846          */
7847         "mouseover" : true,
7848         /**
7849          * @event mouseout
7850          * Fires when a mouseout occur
7851          * @param {Roo.bootstrap.Table} this
7852          * @param {Roo.Element} el
7853          * @param {Number} rowIndex
7854          * @param {Number} columnIndex
7855          * @param {Roo.EventObject} e
7856          */
7857         "mouseout" : true,
7858         /**
7859          * @event rowclass
7860          * Fires when a row is rendered, so you can change add a style to it.
7861          * @param {Roo.bootstrap.Table} this
7862          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
7863          */
7864         'rowclass' : true,
7865           /**
7866          * @event rowsrendered
7867          * Fires when all the  rows have been rendered
7868          * @param {Roo.bootstrap.Table} this
7869          */
7870         'rowsrendered' : true,
7871         /**
7872          * @event contextmenu
7873          * The raw contextmenu event for the entire grid.
7874          * @param {Roo.EventObject} e
7875          */
7876         "contextmenu" : true,
7877         /**
7878          * @event rowcontextmenu
7879          * Fires when a row is right clicked
7880          * @param {Roo.bootstrap.Table} this
7881          * @param {Number} rowIndex
7882          * @param {Roo.EventObject} e
7883          */
7884         "rowcontextmenu" : true,
7885         /**
7886          * @event cellcontextmenu
7887          * Fires when a cell is right clicked
7888          * @param {Roo.bootstrap.Table} this
7889          * @param {Number} rowIndex
7890          * @param {Number} cellIndex
7891          * @param {Roo.EventObject} e
7892          */
7893          "cellcontextmenu" : true,
7894          /**
7895          * @event headercontextmenu
7896          * Fires when a header is right clicked
7897          * @param {Roo.bootstrap.Table} this
7898          * @param {Number} columnIndex
7899          * @param {Roo.EventObject} e
7900          */
7901         "headercontextmenu" : true
7902     });
7903 };
7904
7905 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
7906     
7907     cls: false,
7908     align: false,
7909     bgcolor: false,
7910     border: false,
7911     cellpadding: false,
7912     cellspacing: false,
7913     frame: false,
7914     rules: false,
7915     sortable: false,
7916     summary: false,
7917     width: false,
7918     striped : false,
7919     scrollBody : false,
7920     bordered: false,
7921     hover:  false,
7922     condensed : false,
7923     responsive : false,
7924     sm : false,
7925     cm : false,
7926     store : false,
7927     loadMask : false,
7928     footerShow : true,
7929     headerShow : true,
7930   
7931     rowSelection : false,
7932     cellSelection : false,
7933     layout : false,
7934     
7935     // Roo.Element - the tbody
7936     mainBody: false,
7937     // Roo.Element - thead element
7938     mainHead: false,
7939     
7940     container: false, // used by gridpanel...
7941     
7942     lazyLoad : false,
7943     
7944     CSS : Roo.util.CSS,
7945     
7946     auto_hide_footer : false,
7947     
7948     getAutoCreate : function()
7949     {
7950         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7951         
7952         cfg = {
7953             tag: 'table',
7954             cls : 'table',
7955             cn : []
7956         };
7957         if (this.scrollBody) {
7958             cfg.cls += ' table-body-fixed';
7959         }    
7960         if (this.striped) {
7961             cfg.cls += ' table-striped';
7962         }
7963         
7964         if (this.hover) {
7965             cfg.cls += ' table-hover';
7966         }
7967         if (this.bordered) {
7968             cfg.cls += ' table-bordered';
7969         }
7970         if (this.condensed) {
7971             cfg.cls += ' table-condensed';
7972         }
7973         if (this.responsive) {
7974             cfg.cls += ' table-responsive';
7975         }
7976         
7977         if (this.cls) {
7978             cfg.cls+=  ' ' +this.cls;
7979         }
7980         
7981         // this lot should be simplifed...
7982         var _t = this;
7983         var cp = [
7984             'align',
7985             'bgcolor',
7986             'border',
7987             'cellpadding',
7988             'cellspacing',
7989             'frame',
7990             'rules',
7991             'sortable',
7992             'summary',
7993             'width'
7994         ].forEach(function(k) {
7995             if (_t[k]) {
7996                 cfg[k] = _t[k];
7997             }
7998         });
7999         
8000         
8001         if (this.layout) {
8002             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8003         }
8004         
8005         if(this.store || this.cm){
8006             if(this.headerShow){
8007                 cfg.cn.push(this.renderHeader());
8008             }
8009             
8010             cfg.cn.push(this.renderBody());
8011             
8012             if(this.footerShow){
8013                 cfg.cn.push(this.renderFooter());
8014             }
8015             // where does this come from?
8016             //cfg.cls+=  ' TableGrid';
8017         }
8018         
8019         return { cn : [ cfg ] };
8020     },
8021     
8022     initEvents : function()
8023     {   
8024         if(!this.store || !this.cm){
8025             return;
8026         }
8027         if (this.selModel) {
8028             this.selModel.initEvents();
8029         }
8030         
8031         
8032         //Roo.log('initEvents with ds!!!!');
8033         
8034         this.mainBody = this.el.select('tbody', true).first();
8035         this.mainHead = this.el.select('thead', true).first();
8036         this.mainFoot = this.el.select('tfoot', true).first();
8037         
8038         
8039         
8040         var _this = this;
8041         
8042         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8043             e.on('click', _this.sort, _this);
8044         });
8045         
8046         this.mainBody.on("click", this.onClick, this);
8047         this.mainBody.on("dblclick", this.onDblClick, this);
8048         
8049         // why is this done????? = it breaks dialogs??
8050         //this.parent().el.setStyle('position', 'relative');
8051         
8052         
8053         if (this.footer) {
8054             this.footer.parentId = this.id;
8055             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
8056             
8057             if(this.lazyLoad){
8058                 this.el.select('tfoot tr td').first().addClass('hide');
8059             }
8060         } 
8061         
8062         if(this.loadMask) {
8063             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
8064         }
8065         
8066         this.store.on('load', this.onLoad, this);
8067         this.store.on('beforeload', this.onBeforeLoad, this);
8068         this.store.on('update', this.onUpdate, this);
8069         this.store.on('add', this.onAdd, this);
8070         this.store.on("clear", this.clear, this);
8071         
8072         this.el.on("contextmenu", this.onContextMenu, this);
8073         
8074         this.mainBody.on('scroll', this.onBodyScroll, this);
8075         
8076         this.cm.on("headerchange", this.onHeaderChange, this);
8077         
8078         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8079         
8080     },
8081     
8082     onContextMenu : function(e, t)
8083     {
8084         this.processEvent("contextmenu", e);
8085     },
8086     
8087     processEvent : function(name, e)
8088     {
8089         if (name != 'touchstart' ) {
8090             this.fireEvent(name, e);    
8091         }
8092         
8093         var t = e.getTarget();
8094         
8095         var cell = Roo.get(t);
8096         
8097         if(!cell){
8098             return;
8099         }
8100         
8101         if(cell.findParent('tfoot', false, true)){
8102             return;
8103         }
8104         
8105         if(cell.findParent('thead', false, true)){
8106             
8107             if(e.getTarget().nodeName.toLowerCase() != 'th'){
8108                 cell = Roo.get(t).findParent('th', false, true);
8109                 if (!cell) {
8110                     Roo.log("failed to find th in thead?");
8111                     Roo.log(e.getTarget());
8112                     return;
8113                 }
8114             }
8115             
8116             var cellIndex = cell.dom.cellIndex;
8117             
8118             var ename = name == 'touchstart' ? 'click' : name;
8119             this.fireEvent("header" + ename, this, cellIndex, e);
8120             
8121             return;
8122         }
8123         
8124         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8125             cell = Roo.get(t).findParent('td', false, true);
8126             if (!cell) {
8127                 Roo.log("failed to find th in tbody?");
8128                 Roo.log(e.getTarget());
8129                 return;
8130             }
8131         }
8132         
8133         var row = cell.findParent('tr', false, true);
8134         var cellIndex = cell.dom.cellIndex;
8135         var rowIndex = row.dom.rowIndex - 1;
8136         
8137         if(row !== false){
8138             
8139             this.fireEvent("row" + name, this, rowIndex, e);
8140             
8141             if(cell !== false){
8142             
8143                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8144             }
8145         }
8146         
8147     },
8148     
8149     onMouseover : function(e, el)
8150     {
8151         var cell = Roo.get(el);
8152         
8153         if(!cell){
8154             return;
8155         }
8156         
8157         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8158             cell = cell.findParent('td', false, true);
8159         }
8160         
8161         var row = cell.findParent('tr', false, true);
8162         var cellIndex = cell.dom.cellIndex;
8163         var rowIndex = row.dom.rowIndex - 1; // start from 0
8164         
8165         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8166         
8167     },
8168     
8169     onMouseout : function(e, el)
8170     {
8171         var cell = Roo.get(el);
8172         
8173         if(!cell){
8174             return;
8175         }
8176         
8177         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8178             cell = cell.findParent('td', false, true);
8179         }
8180         
8181         var row = cell.findParent('tr', false, true);
8182         var cellIndex = cell.dom.cellIndex;
8183         var rowIndex = row.dom.rowIndex - 1; // start from 0
8184         
8185         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8186         
8187     },
8188     
8189     onClick : function(e, el)
8190     {
8191         var cell = Roo.get(el);
8192         
8193         if(!cell || (!this.cellSelection && !this.rowSelection)){
8194             return;
8195         }
8196         
8197         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8198             cell = cell.findParent('td', false, true);
8199         }
8200         
8201         if(!cell || typeof(cell) == 'undefined'){
8202             return;
8203         }
8204         
8205         var row = cell.findParent('tr', false, true);
8206         
8207         if(!row || typeof(row) == 'undefined'){
8208             return;
8209         }
8210         
8211         var cellIndex = cell.dom.cellIndex;
8212         var rowIndex = this.getRowIndex(row);
8213         
8214         // why??? - should these not be based on SelectionModel?
8215         if(this.cellSelection){
8216             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8217         }
8218         
8219         if(this.rowSelection){
8220             this.fireEvent('rowclick', this, row, rowIndex, e);
8221         }
8222         
8223         
8224     },
8225         
8226     onDblClick : function(e,el)
8227     {
8228         var cell = Roo.get(el);
8229         
8230         if(!cell || (!this.cellSelection && !this.rowSelection)){
8231             return;
8232         }
8233         
8234         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8235             cell = cell.findParent('td', false, true);
8236         }
8237         
8238         if(!cell || typeof(cell) == 'undefined'){
8239             return;
8240         }
8241         
8242         var row = cell.findParent('tr', false, true);
8243         
8244         if(!row || typeof(row) == 'undefined'){
8245             return;
8246         }
8247         
8248         var cellIndex = cell.dom.cellIndex;
8249         var rowIndex = this.getRowIndex(row);
8250         
8251         if(this.cellSelection){
8252             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8253         }
8254         
8255         if(this.rowSelection){
8256             this.fireEvent('rowdblclick', this, row, rowIndex, e);
8257         }
8258     },
8259     
8260     sort : function(e,el)
8261     {
8262         var col = Roo.get(el);
8263         
8264         if(!col.hasClass('sortable')){
8265             return;
8266         }
8267         
8268         var sort = col.attr('sort');
8269         var dir = 'ASC';
8270         
8271         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8272             dir = 'DESC';
8273         }
8274         
8275         this.store.sortInfo = {field : sort, direction : dir};
8276         
8277         if (this.footer) {
8278             Roo.log("calling footer first");
8279             this.footer.onClick('first');
8280         } else {
8281         
8282             this.store.load({ params : { start : 0 } });
8283         }
8284     },
8285     
8286     renderHeader : function()
8287     {
8288         var header = {
8289             tag: 'thead',
8290             cn : []
8291         };
8292         
8293         var cm = this.cm;
8294         this.totalWidth = 0;
8295         
8296         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8297             
8298             var config = cm.config[i];
8299             
8300             var c = {
8301                 tag: 'th',
8302                 cls : 'x-hcol-' + i,
8303                 style : '',
8304                 html: cm.getColumnHeader(i)
8305             };
8306             
8307             var hh = '';
8308             
8309             if(typeof(config.sortable) != 'undefined' && config.sortable){
8310                 c.cls = 'sortable';
8311                 c.html = '<i class="glyphicon"></i>' + c.html;
8312             }
8313             
8314             // could use BS4 hidden-..-down 
8315             
8316             if(typeof(config.lgHeader) != 'undefined'){
8317                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8318             }
8319             
8320             if(typeof(config.mdHeader) != 'undefined'){
8321                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8322             }
8323             
8324             if(typeof(config.smHeader) != 'undefined'){
8325                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8326             }
8327             
8328             if(typeof(config.xsHeader) != 'undefined'){
8329                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8330             }
8331             
8332             if(hh.length){
8333                 c.html = hh;
8334             }
8335             
8336             if(typeof(config.tooltip) != 'undefined'){
8337                 c.tooltip = config.tooltip;
8338             }
8339             
8340             if(typeof(config.colspan) != 'undefined'){
8341                 c.colspan = config.colspan;
8342             }
8343             
8344             if(typeof(config.hidden) != 'undefined' && config.hidden){
8345                 c.style += ' display:none;';
8346             }
8347             
8348             if(typeof(config.dataIndex) != 'undefined'){
8349                 c.sort = config.dataIndex;
8350             }
8351             
8352            
8353             
8354             if(typeof(config.align) != 'undefined' && config.align.length){
8355                 c.style += ' text-align:' + config.align + ';';
8356             }
8357             
8358             if(typeof(config.width) != 'undefined'){
8359                 c.style += ' width:' + config.width + 'px;';
8360                 this.totalWidth += config.width;
8361             } else {
8362                 this.totalWidth += 100; // assume minimum of 100 per column?
8363             }
8364             
8365             if(typeof(config.cls) != 'undefined'){
8366                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8367             }
8368             
8369             ['xs','sm','md','lg'].map(function(size){
8370                 
8371                 if(typeof(config[size]) == 'undefined'){
8372                     return;
8373                 }
8374                  
8375                 if (!config[size]) { // 0 = hidden
8376                     // BS 4 '0' is treated as hide that column and below.
8377                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8378                     return;
8379                 }
8380                 
8381                 c.cls += ' col-' + size + '-' + config[size] + (
8382                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8383                 );
8384                 
8385                 
8386             });
8387             
8388             header.cn.push(c)
8389         }
8390         
8391         return header;
8392     },
8393     
8394     renderBody : function()
8395     {
8396         var body = {
8397             tag: 'tbody',
8398             cn : [
8399                 {
8400                     tag: 'tr',
8401                     cn : [
8402                         {
8403                             tag : 'td',
8404                             colspan :  this.cm.getColumnCount()
8405                         }
8406                     ]
8407                 }
8408             ]
8409         };
8410         
8411         return body;
8412     },
8413     
8414     renderFooter : function()
8415     {
8416         var footer = {
8417             tag: 'tfoot',
8418             cn : [
8419                 {
8420                     tag: 'tr',
8421                     cn : [
8422                         {
8423                             tag : 'td',
8424                             colspan :  this.cm.getColumnCount()
8425                         }
8426                     ]
8427                 }
8428             ]
8429         };
8430         
8431         return footer;
8432     },
8433     
8434     
8435     
8436     onLoad : function()
8437     {
8438 //        Roo.log('ds onload');
8439         this.clear();
8440         
8441         var _this = this;
8442         var cm = this.cm;
8443         var ds = this.store;
8444         
8445         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8446             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8447             if (_this.store.sortInfo) {
8448                     
8449                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8450                     e.select('i', true).addClass(['glyphicon-arrow-up']);
8451                 }
8452                 
8453                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8454                     e.select('i', true).addClass(['glyphicon-arrow-down']);
8455                 }
8456             }
8457         });
8458         
8459         var tbody =  this.mainBody;
8460               
8461         if(ds.getCount() > 0){
8462             ds.data.each(function(d,rowIndex){
8463                 var row =  this.renderRow(cm, ds, rowIndex);
8464                 
8465                 tbody.createChild(row);
8466                 
8467                 var _this = this;
8468                 
8469                 if(row.cellObjects.length){
8470                     Roo.each(row.cellObjects, function(r){
8471                         _this.renderCellObject(r);
8472                     })
8473                 }
8474                 
8475             }, this);
8476         }
8477         
8478         var tfoot = this.el.select('tfoot', true).first();
8479         
8480         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8481             
8482             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8483             
8484             var total = this.ds.getTotalCount();
8485             
8486             if(this.footer.pageSize < total){
8487                 this.mainFoot.show();
8488             }
8489         }
8490         
8491         Roo.each(this.el.select('tbody td', true).elements, function(e){
8492             e.on('mouseover', _this.onMouseover, _this);
8493         });
8494         
8495         Roo.each(this.el.select('tbody td', true).elements, function(e){
8496             e.on('mouseout', _this.onMouseout, _this);
8497         });
8498         this.fireEvent('rowsrendered', this);
8499         
8500         this.autoSize();
8501     },
8502     
8503     
8504     onUpdate : function(ds,record)
8505     {
8506         this.refreshRow(record);
8507         this.autoSize();
8508     },
8509     
8510     onRemove : function(ds, record, index, isUpdate){
8511         if(isUpdate !== true){
8512             this.fireEvent("beforerowremoved", this, index, record);
8513         }
8514         var bt = this.mainBody.dom;
8515         
8516         var rows = this.el.select('tbody > tr', true).elements;
8517         
8518         if(typeof(rows[index]) != 'undefined'){
8519             bt.removeChild(rows[index].dom);
8520         }
8521         
8522 //        if(bt.rows[index]){
8523 //            bt.removeChild(bt.rows[index]);
8524 //        }
8525         
8526         if(isUpdate !== true){
8527             //this.stripeRows(index);
8528             //this.syncRowHeights(index, index);
8529             //this.layout();
8530             this.fireEvent("rowremoved", this, index, record);
8531         }
8532     },
8533     
8534     onAdd : function(ds, records, rowIndex)
8535     {
8536         //Roo.log('on Add called');
8537         // - note this does not handle multiple adding very well..
8538         var bt = this.mainBody.dom;
8539         for (var i =0 ; i < records.length;i++) {
8540             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8541             //Roo.log(records[i]);
8542             //Roo.log(this.store.getAt(rowIndex+i));
8543             this.insertRow(this.store, rowIndex + i, false);
8544             return;
8545         }
8546         
8547     },
8548     
8549     
8550     refreshRow : function(record){
8551         var ds = this.store, index;
8552         if(typeof record == 'number'){
8553             index = record;
8554             record = ds.getAt(index);
8555         }else{
8556             index = ds.indexOf(record);
8557         }
8558         this.insertRow(ds, index, true);
8559         this.autoSize();
8560         this.onRemove(ds, record, index+1, true);
8561         this.autoSize();
8562         //this.syncRowHeights(index, index);
8563         //this.layout();
8564         this.fireEvent("rowupdated", this, index, record);
8565     },
8566     
8567     insertRow : function(dm, rowIndex, isUpdate){
8568         
8569         if(!isUpdate){
8570             this.fireEvent("beforerowsinserted", this, rowIndex);
8571         }
8572             //var s = this.getScrollState();
8573         var row = this.renderRow(this.cm, this.store, rowIndex);
8574         // insert before rowIndex..
8575         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8576         
8577         var _this = this;
8578                 
8579         if(row.cellObjects.length){
8580             Roo.each(row.cellObjects, function(r){
8581                 _this.renderCellObject(r);
8582             })
8583         }
8584             
8585         if(!isUpdate){
8586             this.fireEvent("rowsinserted", this, rowIndex);
8587             //this.syncRowHeights(firstRow, lastRow);
8588             //this.stripeRows(firstRow);
8589             //this.layout();
8590         }
8591         
8592     },
8593     
8594     
8595     getRowDom : function(rowIndex)
8596     {
8597         var rows = this.el.select('tbody > tr', true).elements;
8598         
8599         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8600         
8601     },
8602     // returns the object tree for a tr..
8603   
8604     
8605     renderRow : function(cm, ds, rowIndex) 
8606     {
8607         var d = ds.getAt(rowIndex);
8608         
8609         var row = {
8610             tag : 'tr',
8611             cls : 'x-row-' + rowIndex,
8612             cn : []
8613         };
8614             
8615         var cellObjects = [];
8616         
8617         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8618             var config = cm.config[i];
8619             
8620             var renderer = cm.getRenderer(i);
8621             var value = '';
8622             var id = false;
8623             
8624             if(typeof(renderer) !== 'undefined'){
8625                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8626             }
8627             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8628             // and are rendered into the cells after the row is rendered - using the id for the element.
8629             
8630             if(typeof(value) === 'object'){
8631                 id = Roo.id();
8632                 cellObjects.push({
8633                     container : id,
8634                     cfg : value 
8635                 })
8636             }
8637             
8638             var rowcfg = {
8639                 record: d,
8640                 rowIndex : rowIndex,
8641                 colIndex : i,
8642                 rowClass : ''
8643             };
8644
8645             this.fireEvent('rowclass', this, rowcfg);
8646             
8647             var td = {
8648                 tag: 'td',
8649                 cls : rowcfg.rowClass + ' x-col-' + i,
8650                 style: '',
8651                 html: (typeof(value) === 'object') ? '' : value
8652             };
8653             
8654             if (id) {
8655                 td.id = id;
8656             }
8657             
8658             if(typeof(config.colspan) != 'undefined'){
8659                 td.colspan = config.colspan;
8660             }
8661             
8662             if(typeof(config.hidden) != 'undefined' && config.hidden){
8663                 td.style += ' display:none;';
8664             }
8665             
8666             if(typeof(config.align) != 'undefined' && config.align.length){
8667                 td.style += ' text-align:' + config.align + ';';
8668             }
8669             if(typeof(config.valign) != 'undefined' && config.valign.length){
8670                 td.style += ' vertical-align:' + config.valign + ';';
8671             }
8672             
8673             if(typeof(config.width) != 'undefined'){
8674                 td.style += ' width:' +  config.width + 'px;';
8675             }
8676             
8677             if(typeof(config.cursor) != 'undefined'){
8678                 td.style += ' cursor:' +  config.cursor + ';';
8679             }
8680             
8681             if(typeof(config.cls) != 'undefined'){
8682                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8683             }
8684             
8685             ['xs','sm','md','lg'].map(function(size){
8686                 
8687                 if(typeof(config[size]) == 'undefined'){
8688                     return;
8689                 }
8690                 
8691                 
8692                   
8693                 if (!config[size]) { // 0 = hidden
8694                     // BS 4 '0' is treated as hide that column and below.
8695                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8696                     return;
8697                 }
8698                 
8699                 td.cls += ' col-' + size + '-' + config[size] + (
8700                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8701                 );
8702                  
8703
8704             });
8705             
8706             row.cn.push(td);
8707            
8708         }
8709         
8710         row.cellObjects = cellObjects;
8711         
8712         return row;
8713           
8714     },
8715     
8716     
8717     
8718     onBeforeLoad : function()
8719     {
8720         
8721     },
8722      /**
8723      * Remove all rows
8724      */
8725     clear : function()
8726     {
8727         this.el.select('tbody', true).first().dom.innerHTML = '';
8728     },
8729     /**
8730      * Show or hide a row.
8731      * @param {Number} rowIndex to show or hide
8732      * @param {Boolean} state hide
8733      */
8734     setRowVisibility : function(rowIndex, state)
8735     {
8736         var bt = this.mainBody.dom;
8737         
8738         var rows = this.el.select('tbody > tr', true).elements;
8739         
8740         if(typeof(rows[rowIndex]) == 'undefined'){
8741             return;
8742         }
8743         rows[rowIndex].dom.style.display = state ? '' : 'none';
8744     },
8745     
8746     
8747     getSelectionModel : function(){
8748         if(!this.selModel){
8749             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8750         }
8751         return this.selModel;
8752     },
8753     /*
8754      * Render the Roo.bootstrap object from renderder
8755      */
8756     renderCellObject : function(r)
8757     {
8758         var _this = this;
8759         
8760         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8761         
8762         var t = r.cfg.render(r.container);
8763         
8764         if(r.cfg.cn){
8765             Roo.each(r.cfg.cn, function(c){
8766                 var child = {
8767                     container: t.getChildContainer(),
8768                     cfg: c
8769                 };
8770                 _this.renderCellObject(child);
8771             })
8772         }
8773     },
8774     
8775     getRowIndex : function(row)
8776     {
8777         var rowIndex = -1;
8778         
8779         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8780             if(el != row){
8781                 return;
8782             }
8783             
8784             rowIndex = index;
8785         });
8786         
8787         return rowIndex;
8788     },
8789      /**
8790      * Returns the grid's underlying element = used by panel.Grid
8791      * @return {Element} The element
8792      */
8793     getGridEl : function(){
8794         return this.el;
8795     },
8796      /**
8797      * Forces a resize - used by panel.Grid
8798      * @return {Element} The element
8799      */
8800     autoSize : function()
8801     {
8802         //var ctr = Roo.get(this.container.dom.parentElement);
8803         var ctr = Roo.get(this.el.dom);
8804         
8805         var thd = this.getGridEl().select('thead',true).first();
8806         var tbd = this.getGridEl().select('tbody', true).first();
8807         var tfd = this.getGridEl().select('tfoot', true).first();
8808         
8809         var cw = ctr.getWidth();
8810         
8811         if (tbd) {
8812             
8813             tbd.setWidth(ctr.getWidth());
8814             // if the body has a max height - and then scrolls - we should perhaps set up the height here
8815             // this needs fixing for various usage - currently only hydra job advers I think..
8816             //tdb.setHeight(
8817             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8818             //); 
8819             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8820             cw -= barsize;
8821         }
8822         cw = Math.max(cw, this.totalWidth);
8823         this.getGridEl().select('tr',true).setWidth(cw);
8824         // resize 'expandable coloumn?
8825         
8826         return; // we doe not have a view in this design..
8827         
8828     },
8829     onBodyScroll: function()
8830     {
8831         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8832         if(this.mainHead){
8833             this.mainHead.setStyle({
8834                 'position' : 'relative',
8835                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8836             });
8837         }
8838         
8839         if(this.lazyLoad){
8840             
8841             var scrollHeight = this.mainBody.dom.scrollHeight;
8842             
8843             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8844             
8845             var height = this.mainBody.getHeight();
8846             
8847             if(scrollHeight - height == scrollTop) {
8848                 
8849                 var total = this.ds.getTotalCount();
8850                 
8851                 if(this.footer.cursor + this.footer.pageSize < total){
8852                     
8853                     this.footer.ds.load({
8854                         params : {
8855                             start : this.footer.cursor + this.footer.pageSize,
8856                             limit : this.footer.pageSize
8857                         },
8858                         add : true
8859                     });
8860                 }
8861             }
8862             
8863         }
8864     },
8865     
8866     onHeaderChange : function()
8867     {
8868         var header = this.renderHeader();
8869         var table = this.el.select('table', true).first();
8870         
8871         this.mainHead.remove();
8872         this.mainHead = table.createChild(header, this.mainBody, false);
8873     },
8874     
8875     onHiddenChange : function(colModel, colIndex, hidden)
8876     {
8877         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8878         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8879         
8880         this.CSS.updateRule(thSelector, "display", "");
8881         this.CSS.updateRule(tdSelector, "display", "");
8882         
8883         if(hidden){
8884             this.CSS.updateRule(thSelector, "display", "none");
8885             this.CSS.updateRule(tdSelector, "display", "none");
8886         }
8887         
8888         this.onHeaderChange();
8889         this.onLoad();
8890     },
8891     
8892     setColumnWidth: function(col_index, width)
8893     {
8894         // width = "md-2 xs-2..."
8895         if(!this.colModel.config[col_index]) {
8896             return;
8897         }
8898         
8899         var w = width.split(" ");
8900         
8901         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8902         
8903         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8904         
8905         
8906         for(var j = 0; j < w.length; j++) {
8907             
8908             if(!w[j]) {
8909                 continue;
8910             }
8911             
8912             var size_cls = w[j].split("-");
8913             
8914             if(!Number.isInteger(size_cls[1] * 1)) {
8915                 continue;
8916             }
8917             
8918             if(!this.colModel.config[col_index][size_cls[0]]) {
8919                 continue;
8920             }
8921             
8922             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8923                 continue;
8924             }
8925             
8926             h_row[0].classList.replace(
8927                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8928                 "col-"+size_cls[0]+"-"+size_cls[1]
8929             );
8930             
8931             for(var i = 0; i < rows.length; i++) {
8932                 
8933                 var size_cls = w[j].split("-");
8934                 
8935                 if(!Number.isInteger(size_cls[1] * 1)) {
8936                     continue;
8937                 }
8938                 
8939                 if(!this.colModel.config[col_index][size_cls[0]]) {
8940                     continue;
8941                 }
8942                 
8943                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8944                     continue;
8945                 }
8946                 
8947                 rows[i].classList.replace(
8948                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8949                     "col-"+size_cls[0]+"-"+size_cls[1]
8950                 );
8951             }
8952             
8953             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8954         }
8955     }
8956 });
8957
8958  
8959
8960  /*
8961  * - LGPL
8962  *
8963  * table cell
8964  * 
8965  */
8966
8967 /**
8968  * @class Roo.bootstrap.TableCell
8969  * @extends Roo.bootstrap.Component
8970  * Bootstrap TableCell class
8971  * @cfg {String} html cell contain text
8972  * @cfg {String} cls cell class
8973  * @cfg {String} tag cell tag (td|th) default td
8974  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8975  * @cfg {String} align Aligns the content in a cell
8976  * @cfg {String} axis Categorizes cells
8977  * @cfg {String} bgcolor Specifies the background color of a cell
8978  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8979  * @cfg {Number} colspan Specifies the number of columns a cell should span
8980  * @cfg {String} headers Specifies one or more header cells a cell is related to
8981  * @cfg {Number} height Sets the height of a cell
8982  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8983  * @cfg {Number} rowspan Sets the number of rows a cell should span
8984  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8985  * @cfg {String} valign Vertical aligns the content in a cell
8986  * @cfg {Number} width Specifies the width of a cell
8987  * 
8988  * @constructor
8989  * Create a new TableCell
8990  * @param {Object} config The config object
8991  */
8992
8993 Roo.bootstrap.TableCell = function(config){
8994     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8995 };
8996
8997 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
8998     
8999     html: false,
9000     cls: false,
9001     tag: false,
9002     abbr: false,
9003     align: false,
9004     axis: false,
9005     bgcolor: false,
9006     charoff: false,
9007     colspan: false,
9008     headers: false,
9009     height: false,
9010     nowrap: false,
9011     rowspan: false,
9012     scope: false,
9013     valign: false,
9014     width: false,
9015     
9016     
9017     getAutoCreate : function(){
9018         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
9019         
9020         cfg = {
9021             tag: 'td'
9022         };
9023         
9024         if(this.tag){
9025             cfg.tag = this.tag;
9026         }
9027         
9028         if (this.html) {
9029             cfg.html=this.html
9030         }
9031         if (this.cls) {
9032             cfg.cls=this.cls
9033         }
9034         if (this.abbr) {
9035             cfg.abbr=this.abbr
9036         }
9037         if (this.align) {
9038             cfg.align=this.align
9039         }
9040         if (this.axis) {
9041             cfg.axis=this.axis
9042         }
9043         if (this.bgcolor) {
9044             cfg.bgcolor=this.bgcolor
9045         }
9046         if (this.charoff) {
9047             cfg.charoff=this.charoff
9048         }
9049         if (this.colspan) {
9050             cfg.colspan=this.colspan
9051         }
9052         if (this.headers) {
9053             cfg.headers=this.headers
9054         }
9055         if (this.height) {
9056             cfg.height=this.height
9057         }
9058         if (this.nowrap) {
9059             cfg.nowrap=this.nowrap
9060         }
9061         if (this.rowspan) {
9062             cfg.rowspan=this.rowspan
9063         }
9064         if (this.scope) {
9065             cfg.scope=this.scope
9066         }
9067         if (this.valign) {
9068             cfg.valign=this.valign
9069         }
9070         if (this.width) {
9071             cfg.width=this.width
9072         }
9073         
9074         
9075         return cfg;
9076     }
9077    
9078 });
9079
9080  
9081
9082  /*
9083  * - LGPL
9084  *
9085  * table row
9086  * 
9087  */
9088
9089 /**
9090  * @class Roo.bootstrap.TableRow
9091  * @extends Roo.bootstrap.Component
9092  * Bootstrap TableRow class
9093  * @cfg {String} cls row class
9094  * @cfg {String} align Aligns the content in a table row
9095  * @cfg {String} bgcolor Specifies a background color for a table row
9096  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9097  * @cfg {String} valign Vertical aligns the content in a table row
9098  * 
9099  * @constructor
9100  * Create a new TableRow
9101  * @param {Object} config The config object
9102  */
9103
9104 Roo.bootstrap.TableRow = function(config){
9105     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9106 };
9107
9108 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
9109     
9110     cls: false,
9111     align: false,
9112     bgcolor: false,
9113     charoff: false,
9114     valign: false,
9115     
9116     getAutoCreate : function(){
9117         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9118         
9119         cfg = {
9120             tag: 'tr'
9121         };
9122             
9123         if(this.cls){
9124             cfg.cls = this.cls;
9125         }
9126         if(this.align){
9127             cfg.align = this.align;
9128         }
9129         if(this.bgcolor){
9130             cfg.bgcolor = this.bgcolor;
9131         }
9132         if(this.charoff){
9133             cfg.charoff = this.charoff;
9134         }
9135         if(this.valign){
9136             cfg.valign = this.valign;
9137         }
9138         
9139         return cfg;
9140     }
9141    
9142 });
9143
9144  
9145
9146  /*
9147  * - LGPL
9148  *
9149  * table body
9150  * 
9151  */
9152
9153 /**
9154  * @class Roo.bootstrap.TableBody
9155  * @extends Roo.bootstrap.Component
9156  * Bootstrap TableBody class
9157  * @cfg {String} cls element class
9158  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9159  * @cfg {String} align Aligns the content inside the element
9160  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9161  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9162  * 
9163  * @constructor
9164  * Create a new TableBody
9165  * @param {Object} config The config object
9166  */
9167
9168 Roo.bootstrap.TableBody = function(config){
9169     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9170 };
9171
9172 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
9173     
9174     cls: false,
9175     tag: false,
9176     align: false,
9177     charoff: false,
9178     valign: false,
9179     
9180     getAutoCreate : function(){
9181         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9182         
9183         cfg = {
9184             tag: 'tbody'
9185         };
9186             
9187         if (this.cls) {
9188             cfg.cls=this.cls
9189         }
9190         if(this.tag){
9191             cfg.tag = this.tag;
9192         }
9193         
9194         if(this.align){
9195             cfg.align = this.align;
9196         }
9197         if(this.charoff){
9198             cfg.charoff = this.charoff;
9199         }
9200         if(this.valign){
9201             cfg.valign = this.valign;
9202         }
9203         
9204         return cfg;
9205     }
9206     
9207     
9208 //    initEvents : function()
9209 //    {
9210 //        
9211 //        if(!this.store){
9212 //            return;
9213 //        }
9214 //        
9215 //        this.store = Roo.factory(this.store, Roo.data);
9216 //        this.store.on('load', this.onLoad, this);
9217 //        
9218 //        this.store.load();
9219 //        
9220 //    },
9221 //    
9222 //    onLoad: function () 
9223 //    {   
9224 //        this.fireEvent('load', this);
9225 //    }
9226 //    
9227 //   
9228 });
9229
9230  
9231
9232  /*
9233  * Based on:
9234  * Ext JS Library 1.1.1
9235  * Copyright(c) 2006-2007, Ext JS, LLC.
9236  *
9237  * Originally Released Under LGPL - original licence link has changed is not relivant.
9238  *
9239  * Fork - LGPL
9240  * <script type="text/javascript">
9241  */
9242
9243 // as we use this in bootstrap.
9244 Roo.namespace('Roo.form');
9245  /**
9246  * @class Roo.form.Action
9247  * Internal Class used to handle form actions
9248  * @constructor
9249  * @param {Roo.form.BasicForm} el The form element or its id
9250  * @param {Object} config Configuration options
9251  */
9252
9253  
9254  
9255 // define the action interface
9256 Roo.form.Action = function(form, options){
9257     this.form = form;
9258     this.options = options || {};
9259 };
9260 /**
9261  * Client Validation Failed
9262  * @const 
9263  */
9264 Roo.form.Action.CLIENT_INVALID = 'client';
9265 /**
9266  * Server Validation Failed
9267  * @const 
9268  */
9269 Roo.form.Action.SERVER_INVALID = 'server';
9270  /**
9271  * Connect to Server Failed
9272  * @const 
9273  */
9274 Roo.form.Action.CONNECT_FAILURE = 'connect';
9275 /**
9276  * Reading Data from Server Failed
9277  * @const 
9278  */
9279 Roo.form.Action.LOAD_FAILURE = 'load';
9280
9281 Roo.form.Action.prototype = {
9282     type : 'default',
9283     failureType : undefined,
9284     response : undefined,
9285     result : undefined,
9286
9287     // interface method
9288     run : function(options){
9289
9290     },
9291
9292     // interface method
9293     success : function(response){
9294
9295     },
9296
9297     // interface method
9298     handleResponse : function(response){
9299
9300     },
9301
9302     // default connection failure
9303     failure : function(response){
9304         
9305         this.response = response;
9306         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9307         this.form.afterAction(this, false);
9308     },
9309
9310     processResponse : function(response){
9311         this.response = response;
9312         if(!response.responseText){
9313             return true;
9314         }
9315         this.result = this.handleResponse(response);
9316         return this.result;
9317     },
9318
9319     // utility functions used internally
9320     getUrl : function(appendParams){
9321         var url = this.options.url || this.form.url || this.form.el.dom.action;
9322         if(appendParams){
9323             var p = this.getParams();
9324             if(p){
9325                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9326             }
9327         }
9328         return url;
9329     },
9330
9331     getMethod : function(){
9332         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9333     },
9334
9335     getParams : function(){
9336         var bp = this.form.baseParams;
9337         var p = this.options.params;
9338         if(p){
9339             if(typeof p == "object"){
9340                 p = Roo.urlEncode(Roo.applyIf(p, bp));
9341             }else if(typeof p == 'string' && bp){
9342                 p += '&' + Roo.urlEncode(bp);
9343             }
9344         }else if(bp){
9345             p = Roo.urlEncode(bp);
9346         }
9347         return p;
9348     },
9349
9350     createCallback : function(){
9351         return {
9352             success: this.success,
9353             failure: this.failure,
9354             scope: this,
9355             timeout: (this.form.timeout*1000),
9356             upload: this.form.fileUpload ? this.success : undefined
9357         };
9358     }
9359 };
9360
9361 Roo.form.Action.Submit = function(form, options){
9362     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9363 };
9364
9365 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9366     type : 'submit',
9367
9368     haveProgress : false,
9369     uploadComplete : false,
9370     
9371     // uploadProgress indicator.
9372     uploadProgress : function()
9373     {
9374         if (!this.form.progressUrl) {
9375             return;
9376         }
9377         
9378         if (!this.haveProgress) {
9379             Roo.MessageBox.progress("Uploading", "Uploading");
9380         }
9381         if (this.uploadComplete) {
9382            Roo.MessageBox.hide();
9383            return;
9384         }
9385         
9386         this.haveProgress = true;
9387    
9388         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9389         
9390         var c = new Roo.data.Connection();
9391         c.request({
9392             url : this.form.progressUrl,
9393             params: {
9394                 id : uid
9395             },
9396             method: 'GET',
9397             success : function(req){
9398                //console.log(data);
9399                 var rdata = false;
9400                 var edata;
9401                 try  {
9402                    rdata = Roo.decode(req.responseText)
9403                 } catch (e) {
9404                     Roo.log("Invalid data from server..");
9405                     Roo.log(edata);
9406                     return;
9407                 }
9408                 if (!rdata || !rdata.success) {
9409                     Roo.log(rdata);
9410                     Roo.MessageBox.alert(Roo.encode(rdata));
9411                     return;
9412                 }
9413                 var data = rdata.data;
9414                 
9415                 if (this.uploadComplete) {
9416                    Roo.MessageBox.hide();
9417                    return;
9418                 }
9419                    
9420                 if (data){
9421                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9422                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9423                     );
9424                 }
9425                 this.uploadProgress.defer(2000,this);
9426             },
9427        
9428             failure: function(data) {
9429                 Roo.log('progress url failed ');
9430                 Roo.log(data);
9431             },
9432             scope : this
9433         });
9434            
9435     },
9436     
9437     
9438     run : function()
9439     {
9440         // run get Values on the form, so it syncs any secondary forms.
9441         this.form.getValues();
9442         
9443         var o = this.options;
9444         var method = this.getMethod();
9445         var isPost = method == 'POST';
9446         if(o.clientValidation === false || this.form.isValid()){
9447             
9448             if (this.form.progressUrl) {
9449                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9450                     (new Date() * 1) + '' + Math.random());
9451                     
9452             } 
9453             
9454             
9455             Roo.Ajax.request(Roo.apply(this.createCallback(), {
9456                 form:this.form.el.dom,
9457                 url:this.getUrl(!isPost),
9458                 method: method,
9459                 params:isPost ? this.getParams() : null,
9460                 isUpload: this.form.fileUpload,
9461                 formData : this.form.formData
9462             }));
9463             
9464             this.uploadProgress();
9465
9466         }else if (o.clientValidation !== false){ // client validation failed
9467             this.failureType = Roo.form.Action.CLIENT_INVALID;
9468             this.form.afterAction(this, false);
9469         }
9470     },
9471
9472     success : function(response)
9473     {
9474         this.uploadComplete= true;
9475         if (this.haveProgress) {
9476             Roo.MessageBox.hide();
9477         }
9478         
9479         
9480         var result = this.processResponse(response);
9481         if(result === true || result.success){
9482             this.form.afterAction(this, true);
9483             return;
9484         }
9485         if(result.errors){
9486             this.form.markInvalid(result.errors);
9487             this.failureType = Roo.form.Action.SERVER_INVALID;
9488         }
9489         this.form.afterAction(this, false);
9490     },
9491     failure : function(response)
9492     {
9493         this.uploadComplete= true;
9494         if (this.haveProgress) {
9495             Roo.MessageBox.hide();
9496         }
9497         
9498         this.response = response;
9499         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9500         this.form.afterAction(this, false);
9501     },
9502     
9503     handleResponse : function(response){
9504         if(this.form.errorReader){
9505             var rs = this.form.errorReader.read(response);
9506             var errors = [];
9507             if(rs.records){
9508                 for(var i = 0, len = rs.records.length; i < len; i++) {
9509                     var r = rs.records[i];
9510                     errors[i] = r.data;
9511                 }
9512             }
9513             if(errors.length < 1){
9514                 errors = null;
9515             }
9516             return {
9517                 success : rs.success,
9518                 errors : errors
9519             };
9520         }
9521         var ret = false;
9522         try {
9523             ret = Roo.decode(response.responseText);
9524         } catch (e) {
9525             ret = {
9526                 success: false,
9527                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9528                 errors : []
9529             };
9530         }
9531         return ret;
9532         
9533     }
9534 });
9535
9536
9537 Roo.form.Action.Load = function(form, options){
9538     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9539     this.reader = this.form.reader;
9540 };
9541
9542 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9543     type : 'load',
9544
9545     run : function(){
9546         
9547         Roo.Ajax.request(Roo.apply(
9548                 this.createCallback(), {
9549                     method:this.getMethod(),
9550                     url:this.getUrl(false),
9551                     params:this.getParams()
9552         }));
9553     },
9554
9555     success : function(response){
9556         
9557         var result = this.processResponse(response);
9558         if(result === true || !result.success || !result.data){
9559             this.failureType = Roo.form.Action.LOAD_FAILURE;
9560             this.form.afterAction(this, false);
9561             return;
9562         }
9563         this.form.clearInvalid();
9564         this.form.setValues(result.data);
9565         this.form.afterAction(this, true);
9566     },
9567
9568     handleResponse : function(response){
9569         if(this.form.reader){
9570             var rs = this.form.reader.read(response);
9571             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9572             return {
9573                 success : rs.success,
9574                 data : data
9575             };
9576         }
9577         return Roo.decode(response.responseText);
9578     }
9579 });
9580
9581 Roo.form.Action.ACTION_TYPES = {
9582     'load' : Roo.form.Action.Load,
9583     'submit' : Roo.form.Action.Submit
9584 };/*
9585  * - LGPL
9586  *
9587  * form
9588  *
9589  */
9590
9591 /**
9592  * @class Roo.bootstrap.Form
9593  * @extends Roo.bootstrap.Component
9594  * Bootstrap Form class
9595  * @cfg {String} method  GET | POST (default POST)
9596  * @cfg {String} labelAlign top | left (default top)
9597  * @cfg {String} align left  | right - for navbars
9598  * @cfg {Boolean} loadMask load mask when submit (default true)
9599
9600  *
9601  * @constructor
9602  * Create a new Form
9603  * @param {Object} config The config object
9604  */
9605
9606
9607 Roo.bootstrap.Form = function(config){
9608     
9609     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9610     
9611     Roo.bootstrap.Form.popover.apply();
9612     
9613     this.addEvents({
9614         /**
9615          * @event clientvalidation
9616          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9617          * @param {Form} this
9618          * @param {Boolean} valid true if the form has passed client-side validation
9619          */
9620         clientvalidation: true,
9621         /**
9622          * @event beforeaction
9623          * Fires before any action is performed. Return false to cancel the action.
9624          * @param {Form} this
9625          * @param {Action} action The action to be performed
9626          */
9627         beforeaction: true,
9628         /**
9629          * @event actionfailed
9630          * Fires when an action fails.
9631          * @param {Form} this
9632          * @param {Action} action The action that failed
9633          */
9634         actionfailed : true,
9635         /**
9636          * @event actioncomplete
9637          * Fires when an action is completed.
9638          * @param {Form} this
9639          * @param {Action} action The action that completed
9640          */
9641         actioncomplete : true
9642     });
9643 };
9644
9645 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9646
9647      /**
9648      * @cfg {String} method
9649      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9650      */
9651     method : 'POST',
9652     /**
9653      * @cfg {String} url
9654      * The URL to use for form actions if one isn't supplied in the action options.
9655      */
9656     /**
9657      * @cfg {Boolean} fileUpload
9658      * Set to true if this form is a file upload.
9659      */
9660
9661     /**
9662      * @cfg {Object} baseParams
9663      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9664      */
9665
9666     /**
9667      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9668      */
9669     timeout: 30,
9670     /**
9671      * @cfg {Sting} align (left|right) for navbar forms
9672      */
9673     align : 'left',
9674
9675     // private
9676     activeAction : null,
9677
9678     /**
9679      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9680      * element by passing it or its id or mask the form itself by passing in true.
9681      * @type Mixed
9682      */
9683     waitMsgTarget : false,
9684
9685     loadMask : true,
9686     
9687     /**
9688      * @cfg {Boolean} errorMask (true|false) default false
9689      */
9690     errorMask : false,
9691     
9692     /**
9693      * @cfg {Number} maskOffset Default 100
9694      */
9695     maskOffset : 100,
9696     
9697     /**
9698      * @cfg {Boolean} maskBody
9699      */
9700     maskBody : false,
9701
9702     getAutoCreate : function(){
9703
9704         var cfg = {
9705             tag: 'form',
9706             method : this.method || 'POST',
9707             id : this.id || Roo.id(),
9708             cls : ''
9709         };
9710         if (this.parent().xtype.match(/^Nav/)) {
9711             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9712
9713         }
9714
9715         if (this.labelAlign == 'left' ) {
9716             cfg.cls += ' form-horizontal';
9717         }
9718
9719
9720         return cfg;
9721     },
9722     initEvents : function()
9723     {
9724         this.el.on('submit', this.onSubmit, this);
9725         // this was added as random key presses on the form where triggering form submit.
9726         this.el.on('keypress', function(e) {
9727             if (e.getCharCode() != 13) {
9728                 return true;
9729             }
9730             // we might need to allow it for textareas.. and some other items.
9731             // check e.getTarget().
9732
9733             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9734                 return true;
9735             }
9736
9737             Roo.log("keypress blocked");
9738
9739             e.preventDefault();
9740             return false;
9741         });
9742         
9743     },
9744     // private
9745     onSubmit : function(e){
9746         e.stopEvent();
9747     },
9748
9749      /**
9750      * Returns true if client-side validation on the form is successful.
9751      * @return Boolean
9752      */
9753     isValid : function(){
9754         var items = this.getItems();
9755         var valid = true;
9756         var target = false;
9757         
9758         items.each(function(f){
9759             
9760             if(f.validate()){
9761                 return;
9762             }
9763             
9764             Roo.log('invalid field: ' + f.name);
9765             
9766             valid = false;
9767
9768             if(!target && f.el.isVisible(true)){
9769                 target = f;
9770             }
9771            
9772         });
9773         
9774         if(this.errorMask && !valid){
9775             Roo.bootstrap.Form.popover.mask(this, target);
9776         }
9777         
9778         return valid;
9779     },
9780     
9781     /**
9782      * Returns true if any fields in this form have changed since their original load.
9783      * @return Boolean
9784      */
9785     isDirty : function(){
9786         var dirty = false;
9787         var items = this.getItems();
9788         items.each(function(f){
9789            if(f.isDirty()){
9790                dirty = true;
9791                return false;
9792            }
9793            return true;
9794         });
9795         return dirty;
9796     },
9797      /**
9798      * Performs a predefined action (submit or load) or custom actions you define on this form.
9799      * @param {String} actionName The name of the action type
9800      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
9801      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9802      * accept other config options):
9803      * <pre>
9804 Property          Type             Description
9805 ----------------  ---------------  ----------------------------------------------------------------------------------
9806 url               String           The url for the action (defaults to the form's url)
9807 method            String           The form method to use (defaults to the form's method, or POST if not defined)
9808 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
9809 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
9810                                    validate the form on the client (defaults to false)
9811      * </pre>
9812      * @return {BasicForm} this
9813      */
9814     doAction : function(action, options){
9815         if(typeof action == 'string'){
9816             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9817         }
9818         if(this.fireEvent('beforeaction', this, action) !== false){
9819             this.beforeAction(action);
9820             action.run.defer(100, action);
9821         }
9822         return this;
9823     },
9824
9825     // private
9826     beforeAction : function(action){
9827         var o = action.options;
9828         
9829         if(this.loadMask){
9830             
9831             if(this.maskBody){
9832                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9833             } else {
9834                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9835             }
9836         }
9837         // not really supported yet.. ??
9838
9839         //if(this.waitMsgTarget === true){
9840         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9841         //}else if(this.waitMsgTarget){
9842         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9843         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9844         //}else {
9845         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9846        // }
9847
9848     },
9849
9850     // private
9851     afterAction : function(action, success){
9852         this.activeAction = null;
9853         var o = action.options;
9854
9855         if(this.loadMask){
9856             
9857             if(this.maskBody){
9858                 Roo.get(document.body).unmask();
9859             } else {
9860                 this.el.unmask();
9861             }
9862         }
9863         
9864         //if(this.waitMsgTarget === true){
9865 //            this.el.unmask();
9866         //}else if(this.waitMsgTarget){
9867         //    this.waitMsgTarget.unmask();
9868         //}else{
9869         //    Roo.MessageBox.updateProgress(1);
9870         //    Roo.MessageBox.hide();
9871        // }
9872         //
9873         if(success){
9874             if(o.reset){
9875                 this.reset();
9876             }
9877             Roo.callback(o.success, o.scope, [this, action]);
9878             this.fireEvent('actioncomplete', this, action);
9879
9880         }else{
9881
9882             // failure condition..
9883             // we have a scenario where updates need confirming.
9884             // eg. if a locking scenario exists..
9885             // we look for { errors : { needs_confirm : true }} in the response.
9886             if (
9887                 (typeof(action.result) != 'undefined')  &&
9888                 (typeof(action.result.errors) != 'undefined')  &&
9889                 (typeof(action.result.errors.needs_confirm) != 'undefined')
9890            ){
9891                 var _t = this;
9892                 Roo.log("not supported yet");
9893                  /*
9894
9895                 Roo.MessageBox.confirm(
9896                     "Change requires confirmation",
9897                     action.result.errorMsg,
9898                     function(r) {
9899                         if (r != 'yes') {
9900                             return;
9901                         }
9902                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
9903                     }
9904
9905                 );
9906                 */
9907
9908
9909                 return;
9910             }
9911
9912             Roo.callback(o.failure, o.scope, [this, action]);
9913             // show an error message if no failed handler is set..
9914             if (!this.hasListener('actionfailed')) {
9915                 Roo.log("need to add dialog support");
9916                 /*
9917                 Roo.MessageBox.alert("Error",
9918                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9919                         action.result.errorMsg :
9920                         "Saving Failed, please check your entries or try again"
9921                 );
9922                 */
9923             }
9924
9925             this.fireEvent('actionfailed', this, action);
9926         }
9927
9928     },
9929     /**
9930      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9931      * @param {String} id The value to search for
9932      * @return Field
9933      */
9934     findField : function(id){
9935         var items = this.getItems();
9936         var field = items.get(id);
9937         if(!field){
9938              items.each(function(f){
9939                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9940                     field = f;
9941                     return false;
9942                 }
9943                 return true;
9944             });
9945         }
9946         return field || null;
9947     },
9948      /**
9949      * Mark fields in this form invalid in bulk.
9950      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9951      * @return {BasicForm} this
9952      */
9953     markInvalid : function(errors){
9954         if(errors instanceof Array){
9955             for(var i = 0, len = errors.length; i < len; i++){
9956                 var fieldError = errors[i];
9957                 var f = this.findField(fieldError.id);
9958                 if(f){
9959                     f.markInvalid(fieldError.msg);
9960                 }
9961             }
9962         }else{
9963             var field, id;
9964             for(id in errors){
9965                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9966                     field.markInvalid(errors[id]);
9967                 }
9968             }
9969         }
9970         //Roo.each(this.childForms || [], function (f) {
9971         //    f.markInvalid(errors);
9972         //});
9973
9974         return this;
9975     },
9976
9977     /**
9978      * Set values for fields in this form in bulk.
9979      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9980      * @return {BasicForm} this
9981      */
9982     setValues : function(values){
9983         if(values instanceof Array){ // array of objects
9984             for(var i = 0, len = values.length; i < len; i++){
9985                 var v = values[i];
9986                 var f = this.findField(v.id);
9987                 if(f){
9988                     f.setValue(v.value);
9989                     if(this.trackResetOnLoad){
9990                         f.originalValue = f.getValue();
9991                     }
9992                 }
9993             }
9994         }else{ // object hash
9995             var field, id;
9996             for(id in values){
9997                 if(typeof values[id] != 'function' && (field = this.findField(id))){
9998
9999                     if (field.setFromData &&
10000                         field.valueField &&
10001                         field.displayField &&
10002                         // combos' with local stores can
10003                         // be queried via setValue()
10004                         // to set their value..
10005                         (field.store && !field.store.isLocal)
10006                         ) {
10007                         // it's a combo
10008                         var sd = { };
10009                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
10010                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
10011                         field.setFromData(sd);
10012
10013                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
10014                         
10015                         field.setFromData(values);
10016                         
10017                     } else {
10018                         field.setValue(values[id]);
10019                     }
10020
10021
10022                     if(this.trackResetOnLoad){
10023                         field.originalValue = field.getValue();
10024                     }
10025                 }
10026             }
10027         }
10028
10029         //Roo.each(this.childForms || [], function (f) {
10030         //    f.setValues(values);
10031         //});
10032
10033         return this;
10034     },
10035
10036     /**
10037      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
10038      * they are returned as an array.
10039      * @param {Boolean} asString
10040      * @return {Object}
10041      */
10042     getValues : function(asString){
10043         //if (this.childForms) {
10044             // copy values from the child forms
10045         //    Roo.each(this.childForms, function (f) {
10046         //        this.setValues(f.getValues());
10047         //    }, this);
10048         //}
10049
10050
10051
10052         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
10053         if(asString === true){
10054             return fs;
10055         }
10056         return Roo.urlDecode(fs);
10057     },
10058
10059     /**
10060      * Returns the fields in this form as an object with key/value pairs.
10061      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
10062      * @return {Object}
10063      */
10064     getFieldValues : function(with_hidden)
10065     {
10066         var items = this.getItems();
10067         var ret = {};
10068         items.each(function(f){
10069             
10070             if (!f.getName()) {
10071                 return;
10072             }
10073             
10074             var v = f.getValue();
10075             
10076             if (f.inputType =='radio') {
10077                 if (typeof(ret[f.getName()]) == 'undefined') {
10078                     ret[f.getName()] = ''; // empty..
10079                 }
10080
10081                 if (!f.el.dom.checked) {
10082                     return;
10083
10084                 }
10085                 v = f.el.dom.value;
10086
10087             }
10088             
10089             if(f.xtype == 'MoneyField'){
10090                 ret[f.currencyName] = f.getCurrency();
10091             }
10092
10093             // not sure if this supported any more..
10094             if ((typeof(v) == 'object') && f.getRawValue) {
10095                 v = f.getRawValue() ; // dates..
10096             }
10097             // combo boxes where name != hiddenName...
10098             if (f.name !== false && f.name != '' && f.name != f.getName()) {
10099                 ret[f.name] = f.getRawValue();
10100             }
10101             ret[f.getName()] = v;
10102         });
10103
10104         return ret;
10105     },
10106
10107     /**
10108      * Clears all invalid messages in this form.
10109      * @return {BasicForm} this
10110      */
10111     clearInvalid : function(){
10112         var items = this.getItems();
10113
10114         items.each(function(f){
10115            f.clearInvalid();
10116         });
10117
10118         return this;
10119     },
10120
10121     /**
10122      * Resets this form.
10123      * @return {BasicForm} this
10124      */
10125     reset : function(){
10126         var items = this.getItems();
10127         items.each(function(f){
10128             f.reset();
10129         });
10130
10131         Roo.each(this.childForms || [], function (f) {
10132             f.reset();
10133         });
10134
10135
10136         return this;
10137     },
10138     
10139     getItems : function()
10140     {
10141         var r=new Roo.util.MixedCollection(false, function(o){
10142             return o.id || (o.id = Roo.id());
10143         });
10144         var iter = function(el) {
10145             if (el.inputEl) {
10146                 r.add(el);
10147             }
10148             if (!el.items) {
10149                 return;
10150             }
10151             Roo.each(el.items,function(e) {
10152                 iter(e);
10153             });
10154         };
10155
10156         iter(this);
10157         return r;
10158     },
10159     
10160     hideFields : function(items)
10161     {
10162         Roo.each(items, function(i){
10163             
10164             var f = this.findField(i);
10165             
10166             if(!f){
10167                 return;
10168             }
10169             
10170             f.hide();
10171             
10172         }, this);
10173     },
10174     
10175     showFields : function(items)
10176     {
10177         Roo.each(items, function(i){
10178             
10179             var f = this.findField(i);
10180             
10181             if(!f){
10182                 return;
10183             }
10184             
10185             f.show();
10186             
10187         }, this);
10188     }
10189
10190 });
10191
10192 Roo.apply(Roo.bootstrap.Form, {
10193     
10194     popover : {
10195         
10196         padding : 5,
10197         
10198         isApplied : false,
10199         
10200         isMasked : false,
10201         
10202         form : false,
10203         
10204         target : false,
10205         
10206         toolTip : false,
10207         
10208         intervalID : false,
10209         
10210         maskEl : false,
10211         
10212         apply : function()
10213         {
10214             if(this.isApplied){
10215                 return;
10216             }
10217             
10218             this.maskEl = {
10219                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10220                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10221                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10222                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10223             };
10224             
10225             this.maskEl.top.enableDisplayMode("block");
10226             this.maskEl.left.enableDisplayMode("block");
10227             this.maskEl.bottom.enableDisplayMode("block");
10228             this.maskEl.right.enableDisplayMode("block");
10229             
10230             this.toolTip = new Roo.bootstrap.Tooltip({
10231                 cls : 'roo-form-error-popover',
10232                 alignment : {
10233                     'left' : ['r-l', [-2,0], 'right'],
10234                     'right' : ['l-r', [2,0], 'left'],
10235                     'bottom' : ['tl-bl', [0,2], 'top'],
10236                     'top' : [ 'bl-tl', [0,-2], 'bottom']
10237                 }
10238             });
10239             
10240             this.toolTip.render(Roo.get(document.body));
10241
10242             this.toolTip.el.enableDisplayMode("block");
10243             
10244             Roo.get(document.body).on('click', function(){
10245                 this.unmask();
10246             }, this);
10247             
10248             Roo.get(document.body).on('touchstart', function(){
10249                 this.unmask();
10250             }, this);
10251             
10252             this.isApplied = true
10253         },
10254         
10255         mask : function(form, target)
10256         {
10257             this.form = form;
10258             
10259             this.target = target;
10260             
10261             if(!this.form.errorMask || !target.el){
10262                 return;
10263             }
10264             
10265             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10266             
10267             Roo.log(scrollable);
10268             
10269             var ot = this.target.el.calcOffsetsTo(scrollable);
10270             
10271             var scrollTo = ot[1] - this.form.maskOffset;
10272             
10273             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10274             
10275             scrollable.scrollTo('top', scrollTo);
10276             
10277             var box = this.target.el.getBox();
10278             Roo.log(box);
10279             var zIndex = Roo.bootstrap.Modal.zIndex++;
10280
10281             
10282             this.maskEl.top.setStyle('position', 'absolute');
10283             this.maskEl.top.setStyle('z-index', zIndex);
10284             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10285             this.maskEl.top.setLeft(0);
10286             this.maskEl.top.setTop(0);
10287             this.maskEl.top.show();
10288             
10289             this.maskEl.left.setStyle('position', 'absolute');
10290             this.maskEl.left.setStyle('z-index', zIndex);
10291             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10292             this.maskEl.left.setLeft(0);
10293             this.maskEl.left.setTop(box.y - this.padding);
10294             this.maskEl.left.show();
10295
10296             this.maskEl.bottom.setStyle('position', 'absolute');
10297             this.maskEl.bottom.setStyle('z-index', zIndex);
10298             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10299             this.maskEl.bottom.setLeft(0);
10300             this.maskEl.bottom.setTop(box.bottom + this.padding);
10301             this.maskEl.bottom.show();
10302
10303             this.maskEl.right.setStyle('position', 'absolute');
10304             this.maskEl.right.setStyle('z-index', zIndex);
10305             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10306             this.maskEl.right.setLeft(box.right + this.padding);
10307             this.maskEl.right.setTop(box.y - this.padding);
10308             this.maskEl.right.show();
10309
10310             this.toolTip.bindEl = this.target.el;
10311
10312             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10313
10314             var tip = this.target.blankText;
10315
10316             if(this.target.getValue() !== '' ) {
10317                 
10318                 if (this.target.invalidText.length) {
10319                     tip = this.target.invalidText;
10320                 } else if (this.target.regexText.length){
10321                     tip = this.target.regexText;
10322                 }
10323             }
10324
10325             this.toolTip.show(tip);
10326
10327             this.intervalID = window.setInterval(function() {
10328                 Roo.bootstrap.Form.popover.unmask();
10329             }, 10000);
10330
10331             window.onwheel = function(){ return false;};
10332             
10333             (function(){ this.isMasked = true; }).defer(500, this);
10334             
10335         },
10336         
10337         unmask : function()
10338         {
10339             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10340                 return;
10341             }
10342             
10343             this.maskEl.top.setStyle('position', 'absolute');
10344             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10345             this.maskEl.top.hide();
10346
10347             this.maskEl.left.setStyle('position', 'absolute');
10348             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10349             this.maskEl.left.hide();
10350
10351             this.maskEl.bottom.setStyle('position', 'absolute');
10352             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10353             this.maskEl.bottom.hide();
10354
10355             this.maskEl.right.setStyle('position', 'absolute');
10356             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10357             this.maskEl.right.hide();
10358             
10359             this.toolTip.hide();
10360             
10361             this.toolTip.el.hide();
10362             
10363             window.onwheel = function(){ return true;};
10364             
10365             if(this.intervalID){
10366                 window.clearInterval(this.intervalID);
10367                 this.intervalID = false;
10368             }
10369             
10370             this.isMasked = false;
10371             
10372         }
10373         
10374     }
10375     
10376 });
10377
10378 /*
10379  * Based on:
10380  * Ext JS Library 1.1.1
10381  * Copyright(c) 2006-2007, Ext JS, LLC.
10382  *
10383  * Originally Released Under LGPL - original licence link has changed is not relivant.
10384  *
10385  * Fork - LGPL
10386  * <script type="text/javascript">
10387  */
10388 /**
10389  * @class Roo.form.VTypes
10390  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10391  * @singleton
10392  */
10393 Roo.form.VTypes = function(){
10394     // closure these in so they are only created once.
10395     var alpha = /^[a-zA-Z_]+$/;
10396     var alphanum = /^[a-zA-Z0-9_]+$/;
10397     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10398     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10399
10400     // All these messages and functions are configurable
10401     return {
10402         /**
10403          * The function used to validate email addresses
10404          * @param {String} value The email address
10405          */
10406         'email' : function(v){
10407             return email.test(v);
10408         },
10409         /**
10410          * The error text to display when the email validation function returns false
10411          * @type String
10412          */
10413         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10414         /**
10415          * The keystroke filter mask to be applied on email input
10416          * @type RegExp
10417          */
10418         'emailMask' : /[a-z0-9_\.\-@]/i,
10419
10420         /**
10421          * The function used to validate URLs
10422          * @param {String} value The URL
10423          */
10424         'url' : function(v){
10425             return url.test(v);
10426         },
10427         /**
10428          * The error text to display when the url validation function returns false
10429          * @type String
10430          */
10431         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10432         
10433         /**
10434          * The function used to validate alpha values
10435          * @param {String} value The value
10436          */
10437         'alpha' : function(v){
10438             return alpha.test(v);
10439         },
10440         /**
10441          * The error text to display when the alpha validation function returns false
10442          * @type String
10443          */
10444         'alphaText' : 'This field should only contain letters and _',
10445         /**
10446          * The keystroke filter mask to be applied on alpha input
10447          * @type RegExp
10448          */
10449         'alphaMask' : /[a-z_]/i,
10450
10451         /**
10452          * The function used to validate alphanumeric values
10453          * @param {String} value The value
10454          */
10455         'alphanum' : function(v){
10456             return alphanum.test(v);
10457         },
10458         /**
10459          * The error text to display when the alphanumeric validation function returns false
10460          * @type String
10461          */
10462         'alphanumText' : 'This field should only contain letters, numbers and _',
10463         /**
10464          * The keystroke filter mask to be applied on alphanumeric input
10465          * @type RegExp
10466          */
10467         'alphanumMask' : /[a-z0-9_]/i
10468     };
10469 }();/*
10470  * - LGPL
10471  *
10472  * Input
10473  * 
10474  */
10475
10476 /**
10477  * @class Roo.bootstrap.Input
10478  * @extends Roo.bootstrap.Component
10479  * Bootstrap Input class
10480  * @cfg {Boolean} disabled is it disabled
10481  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
10482  * @cfg {String} name name of the input
10483  * @cfg {string} fieldLabel - the label associated
10484  * @cfg {string} placeholder - placeholder to put in text.
10485  * @cfg {string}  before - input group add on before
10486  * @cfg {string} after - input group add on after
10487  * @cfg {string} size - (lg|sm) or leave empty..
10488  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10489  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10490  * @cfg {Number} md colspan out of 12 for computer-sized screens
10491  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10492  * @cfg {string} value default value of the input
10493  * @cfg {Number} labelWidth set the width of label 
10494  * @cfg {Number} labellg set the width of label (1-12)
10495  * @cfg {Number} labelmd set the width of label (1-12)
10496  * @cfg {Number} labelsm set the width of label (1-12)
10497  * @cfg {Number} labelxs set the width of label (1-12)
10498  * @cfg {String} labelAlign (top|left)
10499  * @cfg {Boolean} readOnly Specifies that the field should be read-only
10500  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10501  * @cfg {String} indicatorpos (left|right) default left
10502  * @cfg {String} capture (user|camera) use for file input only. (default empty)
10503  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10504  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10505
10506  * @cfg {String} align (left|center|right) Default left
10507  * @cfg {Boolean} forceFeedback (true|false) Default false
10508  * 
10509  * @constructor
10510  * Create a new Input
10511  * @param {Object} config The config object
10512  */
10513
10514 Roo.bootstrap.Input = function(config){
10515     
10516     Roo.bootstrap.Input.superclass.constructor.call(this, config);
10517     
10518     this.addEvents({
10519         /**
10520          * @event focus
10521          * Fires when this field receives input focus.
10522          * @param {Roo.form.Field} this
10523          */
10524         focus : true,
10525         /**
10526          * @event blur
10527          * Fires when this field loses input focus.
10528          * @param {Roo.form.Field} this
10529          */
10530         blur : true,
10531         /**
10532          * @event specialkey
10533          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
10534          * {@link Roo.EventObject#getKey} to determine which key was pressed.
10535          * @param {Roo.form.Field} this
10536          * @param {Roo.EventObject} e The event object
10537          */
10538         specialkey : true,
10539         /**
10540          * @event change
10541          * Fires just before the field blurs if the field value has changed.
10542          * @param {Roo.form.Field} this
10543          * @param {Mixed} newValue The new value
10544          * @param {Mixed} oldValue The original value
10545          */
10546         change : true,
10547         /**
10548          * @event invalid
10549          * Fires after the field has been marked as invalid.
10550          * @param {Roo.form.Field} this
10551          * @param {String} msg The validation message
10552          */
10553         invalid : true,
10554         /**
10555          * @event valid
10556          * Fires after the field has been validated with no errors.
10557          * @param {Roo.form.Field} this
10558          */
10559         valid : true,
10560          /**
10561          * @event keyup
10562          * Fires after the key up
10563          * @param {Roo.form.Field} this
10564          * @param {Roo.EventObject}  e The event Object
10565          */
10566         keyup : true
10567     });
10568 };
10569
10570 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10571      /**
10572      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10573       automatic validation (defaults to "keyup").
10574      */
10575     validationEvent : "keyup",
10576      /**
10577      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10578      */
10579     validateOnBlur : true,
10580     /**
10581      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10582      */
10583     validationDelay : 250,
10584      /**
10585      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10586      */
10587     focusClass : "x-form-focus",  // not needed???
10588     
10589        
10590     /**
10591      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10592      */
10593     invalidClass : "has-warning",
10594     
10595     /**
10596      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10597      */
10598     validClass : "has-success",
10599     
10600     /**
10601      * @cfg {Boolean} hasFeedback (true|false) default true
10602      */
10603     hasFeedback : true,
10604     
10605     /**
10606      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10607      */
10608     invalidFeedbackClass : "glyphicon-warning-sign",
10609     
10610     /**
10611      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10612      */
10613     validFeedbackClass : "glyphicon-ok",
10614     
10615     /**
10616      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10617      */
10618     selectOnFocus : false,
10619     
10620      /**
10621      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10622      */
10623     maskRe : null,
10624        /**
10625      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10626      */
10627     vtype : null,
10628     
10629       /**
10630      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10631      */
10632     disableKeyFilter : false,
10633     
10634        /**
10635      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10636      */
10637     disabled : false,
10638      /**
10639      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10640      */
10641     allowBlank : true,
10642     /**
10643      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10644      */
10645     blankText : "Please complete this mandatory field",
10646     
10647      /**
10648      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10649      */
10650     minLength : 0,
10651     /**
10652      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10653      */
10654     maxLength : Number.MAX_VALUE,
10655     /**
10656      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10657      */
10658     minLengthText : "The minimum length for this field is {0}",
10659     /**
10660      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10661      */
10662     maxLengthText : "The maximum length for this field is {0}",
10663   
10664     
10665     /**
10666      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10667      * If available, this function will be called only after the basic validators all return true, and will be passed the
10668      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10669      */
10670     validator : null,
10671     /**
10672      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10673      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10674      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10675      */
10676     regex : null,
10677     /**
10678      * @cfg {String} regexText -- Depricated - use Invalid Text
10679      */
10680     regexText : "",
10681     
10682     /**
10683      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10684      */
10685     invalidText : "",
10686     
10687     
10688     
10689     autocomplete: false,
10690     
10691     
10692     fieldLabel : '',
10693     inputType : 'text',
10694     
10695     name : false,
10696     placeholder: false,
10697     before : false,
10698     after : false,
10699     size : false,
10700     hasFocus : false,
10701     preventMark: false,
10702     isFormField : true,
10703     value : '',
10704     labelWidth : 2,
10705     labelAlign : false,
10706     readOnly : false,
10707     align : false,
10708     formatedValue : false,
10709     forceFeedback : false,
10710     
10711     indicatorpos : 'left',
10712     
10713     labellg : 0,
10714     labelmd : 0,
10715     labelsm : 0,
10716     labelxs : 0,
10717     
10718     capture : '',
10719     accept : '',
10720     
10721     parentLabelAlign : function()
10722     {
10723         var parent = this;
10724         while (parent.parent()) {
10725             parent = parent.parent();
10726             if (typeof(parent.labelAlign) !='undefined') {
10727                 return parent.labelAlign;
10728             }
10729         }
10730         return 'left';
10731         
10732     },
10733     
10734     getAutoCreate : function()
10735     {
10736         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10737         
10738         var id = Roo.id();
10739         
10740         var cfg = {};
10741         
10742         if(this.inputType != 'hidden'){
10743             cfg.cls = 'form-group' //input-group
10744         }
10745         
10746         var input =  {
10747             tag: 'input',
10748             id : id,
10749             type : this.inputType,
10750             value : this.value,
10751             cls : 'form-control',
10752             placeholder : this.placeholder || '',
10753             autocomplete : this.autocomplete || 'new-password'
10754         };
10755         
10756         if(this.capture.length){
10757             input.capture = this.capture;
10758         }
10759         
10760         if(this.accept.length){
10761             input.accept = this.accept + "/*";
10762         }
10763         
10764         if(this.align){
10765             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10766         }
10767         
10768         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10769             input.maxLength = this.maxLength;
10770         }
10771         
10772         if (this.disabled) {
10773             input.disabled=true;
10774         }
10775         
10776         if (this.readOnly) {
10777             input.readonly=true;
10778         }
10779         
10780         if (this.name) {
10781             input.name = this.name;
10782         }
10783         
10784         if (this.size) {
10785             input.cls += ' input-' + this.size;
10786         }
10787         
10788         var settings=this;
10789         ['xs','sm','md','lg'].map(function(size){
10790             if (settings[size]) {
10791                 cfg.cls += ' col-' + size + '-' + settings[size];
10792             }
10793         });
10794         
10795         var inputblock = input;
10796         
10797         var feedback = {
10798             tag: 'span',
10799             cls: 'glyphicon form-control-feedback'
10800         };
10801             
10802         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10803             
10804             inputblock = {
10805                 cls : 'has-feedback',
10806                 cn :  [
10807                     input,
10808                     feedback
10809                 ] 
10810             };  
10811         }
10812         
10813         if (this.before || this.after) {
10814             
10815             inputblock = {
10816                 cls : 'input-group',
10817                 cn :  [] 
10818             };
10819             
10820             if (this.before && typeof(this.before) == 'string') {
10821                 
10822                 inputblock.cn.push({
10823                     tag :'span',
10824                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10825                     html : this.before
10826                 });
10827             }
10828             if (this.before && typeof(this.before) == 'object') {
10829                 this.before = Roo.factory(this.before);
10830                 
10831                 inputblock.cn.push({
10832                     tag :'span',
10833                     cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
10834                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10835                 });
10836             }
10837             
10838             inputblock.cn.push(input);
10839             
10840             if (this.after && typeof(this.after) == 'string') {
10841                 inputblock.cn.push({
10842                     tag :'span',
10843                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10844                     html : this.after
10845                 });
10846             }
10847             if (this.after && typeof(this.after) == 'object') {
10848                 this.after = Roo.factory(this.after);
10849                 
10850                 inputblock.cn.push({
10851                     tag :'span',
10852                     cls : 'roo-input-after input-group-append input-group-text input-group-' +
10853                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10854                 });
10855             }
10856             
10857             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10858                 inputblock.cls += ' has-feedback';
10859                 inputblock.cn.push(feedback);
10860             }
10861         };
10862         var indicator = {
10863             tag : 'i',
10864             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10865             tooltip : 'This field is required'
10866         };
10867         if (Roo.bootstrap.version == 4) {
10868             indicator = {
10869                 tag : 'i',
10870                 style : 'display-none'
10871             };
10872         }
10873         if (align ==='left' && this.fieldLabel.length) {
10874             
10875             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10876             
10877             cfg.cn = [
10878                 indicator,
10879                 {
10880                     tag: 'label',
10881                     'for' :  id,
10882                     cls : 'control-label col-form-label',
10883                     html : this.fieldLabel
10884
10885                 },
10886                 {
10887                     cls : "", 
10888                     cn: [
10889                         inputblock
10890                     ]
10891                 }
10892             ];
10893             
10894             var labelCfg = cfg.cn[1];
10895             var contentCfg = cfg.cn[2];
10896             
10897             if(this.indicatorpos == 'right'){
10898                 cfg.cn = [
10899                     {
10900                         tag: 'label',
10901                         'for' :  id,
10902                         cls : 'control-label col-form-label',
10903                         cn : [
10904                             {
10905                                 tag : 'span',
10906                                 html : this.fieldLabel
10907                             },
10908                             indicator
10909                         ]
10910                     },
10911                     {
10912                         cls : "",
10913                         cn: [
10914                             inputblock
10915                         ]
10916                     }
10917
10918                 ];
10919                 
10920                 labelCfg = cfg.cn[0];
10921                 contentCfg = cfg.cn[1];
10922             
10923             }
10924             
10925             if(this.labelWidth > 12){
10926                 labelCfg.style = "width: " + this.labelWidth + 'px';
10927             }
10928             
10929             if(this.labelWidth < 13 && this.labelmd == 0){
10930                 this.labelmd = this.labelWidth;
10931             }
10932             
10933             if(this.labellg > 0){
10934                 labelCfg.cls += ' col-lg-' + this.labellg;
10935                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10936             }
10937             
10938             if(this.labelmd > 0){
10939                 labelCfg.cls += ' col-md-' + this.labelmd;
10940                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10941             }
10942             
10943             if(this.labelsm > 0){
10944                 labelCfg.cls += ' col-sm-' + this.labelsm;
10945                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10946             }
10947             
10948             if(this.labelxs > 0){
10949                 labelCfg.cls += ' col-xs-' + this.labelxs;
10950                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10951             }
10952             
10953             
10954         } else if ( this.fieldLabel.length) {
10955                 
10956             cfg.cn = [
10957                 {
10958                     tag : 'i',
10959                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10960                     tooltip : 'This field is required'
10961                 },
10962                 {
10963                     tag: 'label',
10964                    //cls : 'input-group-addon',
10965                     html : this.fieldLabel
10966
10967                 },
10968
10969                inputblock
10970
10971            ];
10972            
10973            if(this.indicatorpos == 'right'){
10974                 
10975                 cfg.cn = [
10976                     {
10977                         tag: 'label',
10978                        //cls : 'input-group-addon',
10979                         html : this.fieldLabel
10980
10981                     },
10982                     {
10983                         tag : 'i',
10984                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10985                         tooltip : 'This field is required'
10986                     },
10987
10988                    inputblock
10989
10990                ];
10991
10992             }
10993
10994         } else {
10995             
10996             cfg.cn = [
10997
10998                     inputblock
10999
11000             ];
11001                 
11002                 
11003         };
11004         
11005         if (this.parentType === 'Navbar' &&  this.parent().bar) {
11006            cfg.cls += ' navbar-form';
11007         }
11008         
11009         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
11010             // on BS4 we do this only if not form 
11011             cfg.cls += ' navbar-form';
11012             cfg.tag = 'li';
11013         }
11014         
11015         return cfg;
11016         
11017     },
11018     /**
11019      * return the real input element.
11020      */
11021     inputEl: function ()
11022     {
11023         return this.el.select('input.form-control',true).first();
11024     },
11025     
11026     tooltipEl : function()
11027     {
11028         return this.inputEl();
11029     },
11030     
11031     indicatorEl : function()
11032     {
11033         if (Roo.bootstrap.version == 4) {
11034             return false; // not enabled in v4 yet.
11035         }
11036         
11037         var indicator = this.el.select('i.roo-required-indicator',true).first();
11038         
11039         if(!indicator){
11040             return false;
11041         }
11042         
11043         return indicator;
11044         
11045     },
11046     
11047     setDisabled : function(v)
11048     {
11049         var i  = this.inputEl().dom;
11050         if (!v) {
11051             i.removeAttribute('disabled');
11052             return;
11053             
11054         }
11055         i.setAttribute('disabled','true');
11056     },
11057     initEvents : function()
11058     {
11059           
11060         this.inputEl().on("keydown" , this.fireKey,  this);
11061         this.inputEl().on("focus", this.onFocus,  this);
11062         this.inputEl().on("blur", this.onBlur,  this);
11063         
11064         this.inputEl().relayEvent('keyup', this);
11065         
11066         this.indicator = this.indicatorEl();
11067         
11068         if(this.indicator){
11069             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
11070         }
11071  
11072         // reference to original value for reset
11073         this.originalValue = this.getValue();
11074         //Roo.form.TextField.superclass.initEvents.call(this);
11075         if(this.validationEvent == 'keyup'){
11076             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11077             this.inputEl().on('keyup', this.filterValidation, this);
11078         }
11079         else if(this.validationEvent !== false){
11080             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11081         }
11082         
11083         if(this.selectOnFocus){
11084             this.on("focus", this.preFocus, this);
11085             
11086         }
11087         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11088             this.inputEl().on("keypress", this.filterKeys, this);
11089         } else {
11090             this.inputEl().relayEvent('keypress', this);
11091         }
11092        /* if(this.grow){
11093             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
11094             this.el.on("click", this.autoSize,  this);
11095         }
11096         */
11097         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11098             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11099         }
11100         
11101         if (typeof(this.before) == 'object') {
11102             this.before.render(this.el.select('.roo-input-before',true).first());
11103         }
11104         if (typeof(this.after) == 'object') {
11105             this.after.render(this.el.select('.roo-input-after',true).first());
11106         }
11107         
11108         this.inputEl().on('change', this.onChange, this);
11109         
11110     },
11111     filterValidation : function(e){
11112         if(!e.isNavKeyPress()){
11113             this.validationTask.delay(this.validationDelay);
11114         }
11115     },
11116      /**
11117      * Validates the field value
11118      * @return {Boolean} True if the value is valid, else false
11119      */
11120     validate : function(){
11121         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11122         if(this.disabled || this.validateValue(this.getRawValue())){
11123             this.markValid();
11124             return true;
11125         }
11126         
11127         this.markInvalid();
11128         return false;
11129     },
11130     
11131     
11132     /**
11133      * Validates a value according to the field's validation rules and marks the field as invalid
11134      * if the validation fails
11135      * @param {Mixed} value The value to validate
11136      * @return {Boolean} True if the value is valid, else false
11137      */
11138     validateValue : function(value)
11139     {
11140         if(this.getVisibilityEl().hasClass('hidden')){
11141             return true;
11142         }
11143         
11144         if(value.length < 1)  { // if it's blank
11145             if(this.allowBlank){
11146                 return true;
11147             }
11148             return false;
11149         }
11150         
11151         if(value.length < this.minLength){
11152             return false;
11153         }
11154         if(value.length > this.maxLength){
11155             return false;
11156         }
11157         if(this.vtype){
11158             var vt = Roo.form.VTypes;
11159             if(!vt[this.vtype](value, this)){
11160                 return false;
11161             }
11162         }
11163         if(typeof this.validator == "function"){
11164             var msg = this.validator(value);
11165             if(msg !== true){
11166                 return false;
11167             }
11168             if (typeof(msg) == 'string') {
11169                 this.invalidText = msg;
11170             }
11171         }
11172         
11173         if(this.regex && !this.regex.test(value)){
11174             return false;
11175         }
11176         
11177         return true;
11178     },
11179     
11180      // private
11181     fireKey : function(e){
11182         //Roo.log('field ' + e.getKey());
11183         if(e.isNavKeyPress()){
11184             this.fireEvent("specialkey", this, e);
11185         }
11186     },
11187     focus : function (selectText){
11188         if(this.rendered){
11189             this.inputEl().focus();
11190             if(selectText === true){
11191                 this.inputEl().dom.select();
11192             }
11193         }
11194         return this;
11195     } ,
11196     
11197     onFocus : function(){
11198         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11199            // this.el.addClass(this.focusClass);
11200         }
11201         if(!this.hasFocus){
11202             this.hasFocus = true;
11203             this.startValue = this.getValue();
11204             this.fireEvent("focus", this);
11205         }
11206     },
11207     
11208     beforeBlur : Roo.emptyFn,
11209
11210     
11211     // private
11212     onBlur : function(){
11213         this.beforeBlur();
11214         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11215             //this.el.removeClass(this.focusClass);
11216         }
11217         this.hasFocus = false;
11218         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11219             this.validate();
11220         }
11221         var v = this.getValue();
11222         if(String(v) !== String(this.startValue)){
11223             this.fireEvent('change', this, v, this.startValue);
11224         }
11225         this.fireEvent("blur", this);
11226     },
11227     
11228     onChange : function(e)
11229     {
11230         var v = this.getValue();
11231         if(String(v) !== String(this.startValue)){
11232             this.fireEvent('change', this, v, this.startValue);
11233         }
11234         
11235     },
11236     
11237     /**
11238      * Resets the current field value to the originally loaded value and clears any validation messages
11239      */
11240     reset : function(){
11241         this.setValue(this.originalValue);
11242         this.validate();
11243     },
11244      /**
11245      * Returns the name of the field
11246      * @return {Mixed} name The name field
11247      */
11248     getName: function(){
11249         return this.name;
11250     },
11251      /**
11252      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
11253      * @return {Mixed} value The field value
11254      */
11255     getValue : function(){
11256         
11257         var v = this.inputEl().getValue();
11258         
11259         return v;
11260     },
11261     /**
11262      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
11263      * @return {Mixed} value The field value
11264      */
11265     getRawValue : function(){
11266         var v = this.inputEl().getValue();
11267         
11268         return v;
11269     },
11270     
11271     /**
11272      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
11273      * @param {Mixed} value The value to set
11274      */
11275     setRawValue : function(v){
11276         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11277     },
11278     
11279     selectText : function(start, end){
11280         var v = this.getRawValue();
11281         if(v.length > 0){
11282             start = start === undefined ? 0 : start;
11283             end = end === undefined ? v.length : end;
11284             var d = this.inputEl().dom;
11285             if(d.setSelectionRange){
11286                 d.setSelectionRange(start, end);
11287             }else if(d.createTextRange){
11288                 var range = d.createTextRange();
11289                 range.moveStart("character", start);
11290                 range.moveEnd("character", v.length-end);
11291                 range.select();
11292             }
11293         }
11294     },
11295     
11296     /**
11297      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
11298      * @param {Mixed} value The value to set
11299      */
11300     setValue : function(v){
11301         this.value = v;
11302         if(this.rendered){
11303             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11304             this.validate();
11305         }
11306     },
11307     
11308     /*
11309     processValue : function(value){
11310         if(this.stripCharsRe){
11311             var newValue = value.replace(this.stripCharsRe, '');
11312             if(newValue !== value){
11313                 this.setRawValue(newValue);
11314                 return newValue;
11315             }
11316         }
11317         return value;
11318     },
11319   */
11320     preFocus : function(){
11321         
11322         if(this.selectOnFocus){
11323             this.inputEl().dom.select();
11324         }
11325     },
11326     filterKeys : function(e){
11327         var k = e.getKey();
11328         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11329             return;
11330         }
11331         var c = e.getCharCode(), cc = String.fromCharCode(c);
11332         if(Roo.isIE && (e.isSpecialKey() || !cc)){
11333             return;
11334         }
11335         if(!this.maskRe.test(cc)){
11336             e.stopEvent();
11337         }
11338     },
11339      /**
11340      * Clear any invalid styles/messages for this field
11341      */
11342     clearInvalid : function(){
11343         
11344         if(!this.el || this.preventMark){ // not rendered
11345             return;
11346         }
11347         
11348         
11349         this.el.removeClass([this.invalidClass, 'is-invalid']);
11350         
11351         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11352             
11353             var feedback = this.el.select('.form-control-feedback', true).first();
11354             
11355             if(feedback){
11356                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11357             }
11358             
11359         }
11360         
11361         if(this.indicator){
11362             this.indicator.removeClass('visible');
11363             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11364         }
11365         
11366         this.fireEvent('valid', this);
11367     },
11368     
11369      /**
11370      * Mark this field as valid
11371      */
11372     markValid : function()
11373     {
11374         if(!this.el  || this.preventMark){ // not rendered...
11375             return;
11376         }
11377         
11378         this.el.removeClass([this.invalidClass, this.validClass]);
11379         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11380
11381         var feedback = this.el.select('.form-control-feedback', true).first();
11382             
11383         if(feedback){
11384             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11385         }
11386         
11387         if(this.indicator){
11388             this.indicator.removeClass('visible');
11389             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11390         }
11391         
11392         if(this.disabled){
11393             return;
11394         }
11395         
11396            
11397         if(this.allowBlank && !this.getRawValue().length){
11398             return;
11399         }
11400         if (Roo.bootstrap.version == 3) {
11401             this.el.addClass(this.validClass);
11402         } else {
11403             this.inputEl().addClass('is-valid');
11404         }
11405
11406         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11407             
11408             var feedback = this.el.select('.form-control-feedback', true).first();
11409             
11410             if(feedback){
11411                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11412                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11413             }
11414             
11415         }
11416         
11417         this.fireEvent('valid', this);
11418     },
11419     
11420      /**
11421      * Mark this field as invalid
11422      * @param {String} msg The validation message
11423      */
11424     markInvalid : function(msg)
11425     {
11426         if(!this.el  || this.preventMark){ // not rendered
11427             return;
11428         }
11429         
11430         this.el.removeClass([this.invalidClass, this.validClass]);
11431         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11432         
11433         var feedback = this.el.select('.form-control-feedback', true).first();
11434             
11435         if(feedback){
11436             this.el.select('.form-control-feedback', true).first().removeClass(
11437                     [this.invalidFeedbackClass, this.validFeedbackClass]);
11438         }
11439
11440         if(this.disabled){
11441             return;
11442         }
11443         
11444         if(this.allowBlank && !this.getRawValue().length){
11445             return;
11446         }
11447         
11448         if(this.indicator){
11449             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11450             this.indicator.addClass('visible');
11451         }
11452         if (Roo.bootstrap.version == 3) {
11453             this.el.addClass(this.invalidClass);
11454         } else {
11455             this.inputEl().addClass('is-invalid');
11456         }
11457         
11458         
11459         
11460         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11461             
11462             var feedback = this.el.select('.form-control-feedback', true).first();
11463             
11464             if(feedback){
11465                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11466                 
11467                 if(this.getValue().length || this.forceFeedback){
11468                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11469                 }
11470                 
11471             }
11472             
11473         }
11474         
11475         this.fireEvent('invalid', this, msg);
11476     },
11477     // private
11478     SafariOnKeyDown : function(event)
11479     {
11480         // this is a workaround for a password hang bug on chrome/ webkit.
11481         if (this.inputEl().dom.type != 'password') {
11482             return;
11483         }
11484         
11485         var isSelectAll = false;
11486         
11487         if(this.inputEl().dom.selectionEnd > 0){
11488             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11489         }
11490         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11491             event.preventDefault();
11492             this.setValue('');
11493             return;
11494         }
11495         
11496         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11497             
11498             event.preventDefault();
11499             // this is very hacky as keydown always get's upper case.
11500             //
11501             var cc = String.fromCharCode(event.getCharCode());
11502             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
11503             
11504         }
11505     },
11506     adjustWidth : function(tag, w){
11507         tag = tag.toLowerCase();
11508         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11509             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11510                 if(tag == 'input'){
11511                     return w + 2;
11512                 }
11513                 if(tag == 'textarea'){
11514                     return w-2;
11515                 }
11516             }else if(Roo.isOpera){
11517                 if(tag == 'input'){
11518                     return w + 2;
11519                 }
11520                 if(tag == 'textarea'){
11521                     return w-2;
11522                 }
11523             }
11524         }
11525         return w;
11526     },
11527     
11528     setFieldLabel : function(v)
11529     {
11530         if(!this.rendered){
11531             return;
11532         }
11533         
11534         if(this.indicatorEl()){
11535             var ar = this.el.select('label > span',true);
11536             
11537             if (ar.elements.length) {
11538                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11539                 this.fieldLabel = v;
11540                 return;
11541             }
11542             
11543             var br = this.el.select('label',true);
11544             
11545             if(br.elements.length) {
11546                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11547                 this.fieldLabel = v;
11548                 return;
11549             }
11550             
11551             Roo.log('Cannot Found any of label > span || label in input');
11552             return;
11553         }
11554         
11555         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11556         this.fieldLabel = v;
11557         
11558         
11559     }
11560 });
11561
11562  
11563 /*
11564  * - LGPL
11565  *
11566  * Input
11567  * 
11568  */
11569
11570 /**
11571  * @class Roo.bootstrap.TextArea
11572  * @extends Roo.bootstrap.Input
11573  * Bootstrap TextArea class
11574  * @cfg {Number} cols Specifies the visible width of a text area
11575  * @cfg {Number} rows Specifies the visible number of lines in a text area
11576  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11577  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11578  * @cfg {string} html text
11579  * 
11580  * @constructor
11581  * Create a new TextArea
11582  * @param {Object} config The config object
11583  */
11584
11585 Roo.bootstrap.TextArea = function(config){
11586     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11587    
11588 };
11589
11590 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11591      
11592     cols : false,
11593     rows : 5,
11594     readOnly : false,
11595     warp : 'soft',
11596     resize : false,
11597     value: false,
11598     html: false,
11599     
11600     getAutoCreate : function(){
11601         
11602         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11603         
11604         var id = Roo.id();
11605         
11606         var cfg = {};
11607         
11608         if(this.inputType != 'hidden'){
11609             cfg.cls = 'form-group' //input-group
11610         }
11611         
11612         var input =  {
11613             tag: 'textarea',
11614             id : id,
11615             warp : this.warp,
11616             rows : this.rows,
11617             value : this.value || '',
11618             html: this.html || '',
11619             cls : 'form-control',
11620             placeholder : this.placeholder || '' 
11621             
11622         };
11623         
11624         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11625             input.maxLength = this.maxLength;
11626         }
11627         
11628         if(this.resize){
11629             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11630         }
11631         
11632         if(this.cols){
11633             input.cols = this.cols;
11634         }
11635         
11636         if (this.readOnly) {
11637             input.readonly = true;
11638         }
11639         
11640         if (this.name) {
11641             input.name = this.name;
11642         }
11643         
11644         if (this.size) {
11645             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11646         }
11647         
11648         var settings=this;
11649         ['xs','sm','md','lg'].map(function(size){
11650             if (settings[size]) {
11651                 cfg.cls += ' col-' + size + '-' + settings[size];
11652             }
11653         });
11654         
11655         var inputblock = input;
11656         
11657         if(this.hasFeedback && !this.allowBlank){
11658             
11659             var feedback = {
11660                 tag: 'span',
11661                 cls: 'glyphicon form-control-feedback'
11662             };
11663
11664             inputblock = {
11665                 cls : 'has-feedback',
11666                 cn :  [
11667                     input,
11668                     feedback
11669                 ] 
11670             };  
11671         }
11672         
11673         
11674         if (this.before || this.after) {
11675             
11676             inputblock = {
11677                 cls : 'input-group',
11678                 cn :  [] 
11679             };
11680             if (this.before) {
11681                 inputblock.cn.push({
11682                     tag :'span',
11683                     cls : 'input-group-addon',
11684                     html : this.before
11685                 });
11686             }
11687             
11688             inputblock.cn.push(input);
11689             
11690             if(this.hasFeedback && !this.allowBlank){
11691                 inputblock.cls += ' has-feedback';
11692                 inputblock.cn.push(feedback);
11693             }
11694             
11695             if (this.after) {
11696                 inputblock.cn.push({
11697                     tag :'span',
11698                     cls : 'input-group-addon',
11699                     html : this.after
11700                 });
11701             }
11702             
11703         }
11704         
11705         if (align ==='left' && this.fieldLabel.length) {
11706             cfg.cn = [
11707                 {
11708                     tag: 'label',
11709                     'for' :  id,
11710                     cls : 'control-label',
11711                     html : this.fieldLabel
11712                 },
11713                 {
11714                     cls : "",
11715                     cn: [
11716                         inputblock
11717                     ]
11718                 }
11719
11720             ];
11721             
11722             if(this.labelWidth > 12){
11723                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11724             }
11725
11726             if(this.labelWidth < 13 && this.labelmd == 0){
11727                 this.labelmd = this.labelWidth;
11728             }
11729
11730             if(this.labellg > 0){
11731                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11732                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11733             }
11734
11735             if(this.labelmd > 0){
11736                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11737                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11738             }
11739
11740             if(this.labelsm > 0){
11741                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11742                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11743             }
11744
11745             if(this.labelxs > 0){
11746                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11747                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11748             }
11749             
11750         } else if ( this.fieldLabel.length) {
11751             cfg.cn = [
11752
11753                {
11754                    tag: 'label',
11755                    //cls : 'input-group-addon',
11756                    html : this.fieldLabel
11757
11758                },
11759
11760                inputblock
11761
11762            ];
11763
11764         } else {
11765
11766             cfg.cn = [
11767
11768                 inputblock
11769
11770             ];
11771                 
11772         }
11773         
11774         if (this.disabled) {
11775             input.disabled=true;
11776         }
11777         
11778         return cfg;
11779         
11780     },
11781     /**
11782      * return the real textarea element.
11783      */
11784     inputEl: function ()
11785     {
11786         return this.el.select('textarea.form-control',true).first();
11787     },
11788     
11789     /**
11790      * Clear any invalid styles/messages for this field
11791      */
11792     clearInvalid : function()
11793     {
11794         
11795         if(!this.el || this.preventMark){ // not rendered
11796             return;
11797         }
11798         
11799         var label = this.el.select('label', true).first();
11800         var icon = this.el.select('i.fa-star', true).first();
11801         
11802         if(label && icon){
11803             icon.remove();
11804         }
11805         this.el.removeClass( this.validClass);
11806         this.inputEl().removeClass('is-invalid');
11807          
11808         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11809             
11810             var feedback = this.el.select('.form-control-feedback', true).first();
11811             
11812             if(feedback){
11813                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11814             }
11815             
11816         }
11817         
11818         this.fireEvent('valid', this);
11819     },
11820     
11821      /**
11822      * Mark this field as valid
11823      */
11824     markValid : function()
11825     {
11826         if(!this.el  || this.preventMark){ // not rendered
11827             return;
11828         }
11829         
11830         this.el.removeClass([this.invalidClass, this.validClass]);
11831         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11832         
11833         var feedback = this.el.select('.form-control-feedback', true).first();
11834             
11835         if(feedback){
11836             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11837         }
11838
11839         if(this.disabled || this.allowBlank){
11840             return;
11841         }
11842         
11843         var label = this.el.select('label', true).first();
11844         var icon = this.el.select('i.fa-star', true).first();
11845         
11846         if(label && icon){
11847             icon.remove();
11848         }
11849         if (Roo.bootstrap.version == 3) {
11850             this.el.addClass(this.validClass);
11851         } else {
11852             this.inputEl().addClass('is-valid');
11853         }
11854         
11855         
11856         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11857             
11858             var feedback = this.el.select('.form-control-feedback', true).first();
11859             
11860             if(feedback){
11861                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11862                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11863             }
11864             
11865         }
11866         
11867         this.fireEvent('valid', this);
11868     },
11869     
11870      /**
11871      * Mark this field as invalid
11872      * @param {String} msg The validation message
11873      */
11874     markInvalid : function(msg)
11875     {
11876         if(!this.el  || this.preventMark){ // not rendered
11877             return;
11878         }
11879         
11880         this.el.removeClass([this.invalidClass, this.validClass]);
11881         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11882         
11883         var feedback = this.el.select('.form-control-feedback', true).first();
11884             
11885         if(feedback){
11886             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11887         }
11888
11889         if(this.disabled || this.allowBlank){
11890             return;
11891         }
11892         
11893         var label = this.el.select('label', true).first();
11894         var icon = this.el.select('i.fa-star', true).first();
11895         
11896         if(!this.getValue().length && label && !icon){
11897             this.el.createChild({
11898                 tag : 'i',
11899                 cls : 'text-danger fa fa-lg fa-star',
11900                 tooltip : 'This field is required',
11901                 style : 'margin-right:5px;'
11902             }, label, true);
11903         }
11904         
11905         if (Roo.bootstrap.version == 3) {
11906             this.el.addClass(this.invalidClass);
11907         } else {
11908             this.inputEl().addClass('is-invalid');
11909         }
11910         
11911         // fixme ... this may be depricated need to test..
11912         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11913             
11914             var feedback = this.el.select('.form-control-feedback', true).first();
11915             
11916             if(feedback){
11917                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11918                 
11919                 if(this.getValue().length || this.forceFeedback){
11920                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11921                 }
11922                 
11923             }
11924             
11925         }
11926         
11927         this.fireEvent('invalid', this, msg);
11928     }
11929 });
11930
11931  
11932 /*
11933  * - LGPL
11934  *
11935  * trigger field - base class for combo..
11936  * 
11937  */
11938  
11939 /**
11940  * @class Roo.bootstrap.TriggerField
11941  * @extends Roo.bootstrap.Input
11942  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11943  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11944  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11945  * for which you can provide a custom implementation.  For example:
11946  * <pre><code>
11947 var trigger = new Roo.bootstrap.TriggerField();
11948 trigger.onTriggerClick = myTriggerFn;
11949 trigger.applyTo('my-field');
11950 </code></pre>
11951  *
11952  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11953  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11954  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
11955  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11956  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11957
11958  * @constructor
11959  * Create a new TriggerField.
11960  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11961  * to the base TextField)
11962  */
11963 Roo.bootstrap.TriggerField = function(config){
11964     this.mimicing = false;
11965     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11966 };
11967
11968 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
11969     /**
11970      * @cfg {String} triggerClass A CSS class to apply to the trigger
11971      */
11972      /**
11973      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11974      */
11975     hideTrigger:false,
11976
11977     /**
11978      * @cfg {Boolean} removable (true|false) special filter default false
11979      */
11980     removable : false,
11981     
11982     /** @cfg {Boolean} grow @hide */
11983     /** @cfg {Number} growMin @hide */
11984     /** @cfg {Number} growMax @hide */
11985
11986     /**
11987      * @hide 
11988      * @method
11989      */
11990     autoSize: Roo.emptyFn,
11991     // private
11992     monitorTab : true,
11993     // private
11994     deferHeight : true,
11995
11996     
11997     actionMode : 'wrap',
11998     
11999     caret : false,
12000     
12001     
12002     getAutoCreate : function(){
12003        
12004         var align = this.labelAlign || this.parentLabelAlign();
12005         
12006         var id = Roo.id();
12007         
12008         var cfg = {
12009             cls: 'form-group' //input-group
12010         };
12011         
12012         
12013         var input =  {
12014             tag: 'input',
12015             id : id,
12016             type : this.inputType,
12017             cls : 'form-control',
12018             autocomplete: 'new-password',
12019             placeholder : this.placeholder || '' 
12020             
12021         };
12022         if (this.name) {
12023             input.name = this.name;
12024         }
12025         if (this.size) {
12026             input.cls += ' input-' + this.size;
12027         }
12028         
12029         if (this.disabled) {
12030             input.disabled=true;
12031         }
12032         
12033         var inputblock = input;
12034         
12035         if(this.hasFeedback && !this.allowBlank){
12036             
12037             var feedback = {
12038                 tag: 'span',
12039                 cls: 'glyphicon form-control-feedback'
12040             };
12041             
12042             if(this.removable && !this.editable  ){
12043                 inputblock = {
12044                     cls : 'has-feedback',
12045                     cn :  [
12046                         inputblock,
12047                         {
12048                             tag: 'button',
12049                             html : 'x',
12050                             cls : 'roo-combo-removable-btn close'
12051                         },
12052                         feedback
12053                     ] 
12054                 };
12055             } else {
12056                 inputblock = {
12057                     cls : 'has-feedback',
12058                     cn :  [
12059                         inputblock,
12060                         feedback
12061                     ] 
12062                 };
12063             }
12064
12065         } else {
12066             if(this.removable && !this.editable ){
12067                 inputblock = {
12068                     cls : 'roo-removable',
12069                     cn :  [
12070                         inputblock,
12071                         {
12072                             tag: 'button',
12073                             html : 'x',
12074                             cls : 'roo-combo-removable-btn close'
12075                         }
12076                     ] 
12077                 };
12078             }
12079         }
12080         
12081         if (this.before || this.after) {
12082             
12083             inputblock = {
12084                 cls : 'input-group',
12085                 cn :  [] 
12086             };
12087             if (this.before) {
12088                 inputblock.cn.push({
12089                     tag :'span',
12090                     cls : 'input-group-addon input-group-prepend input-group-text',
12091                     html : this.before
12092                 });
12093             }
12094             
12095             inputblock.cn.push(input);
12096             
12097             if(this.hasFeedback && !this.allowBlank){
12098                 inputblock.cls += ' has-feedback';
12099                 inputblock.cn.push(feedback);
12100             }
12101             
12102             if (this.after) {
12103                 inputblock.cn.push({
12104                     tag :'span',
12105                     cls : 'input-group-addon input-group-append input-group-text',
12106                     html : this.after
12107                 });
12108             }
12109             
12110         };
12111         
12112       
12113         
12114         var ibwrap = inputblock;
12115         
12116         if(this.multiple){
12117             ibwrap = {
12118                 tag: 'ul',
12119                 cls: 'roo-select2-choices',
12120                 cn:[
12121                     {
12122                         tag: 'li',
12123                         cls: 'roo-select2-search-field',
12124                         cn: [
12125
12126                             inputblock
12127                         ]
12128                     }
12129                 ]
12130             };
12131                 
12132         }
12133         
12134         var combobox = {
12135             cls: 'roo-select2-container input-group',
12136             cn: [
12137                  {
12138                     tag: 'input',
12139                     type : 'hidden',
12140                     cls: 'form-hidden-field'
12141                 },
12142                 ibwrap
12143             ]
12144         };
12145         
12146         if(!this.multiple && this.showToggleBtn){
12147             
12148             var caret = {
12149                         tag: 'span',
12150                         cls: 'caret'
12151              };
12152             if (this.caret != false) {
12153                 caret = {
12154                      tag: 'i',
12155                      cls: 'fa fa-' + this.caret
12156                 };
12157                 
12158             }
12159             
12160             combobox.cn.push({
12161                 tag :'span',
12162                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12163                 cn : [
12164                     Roo.bootstrap.version == 3 ? caret : '',
12165                     {
12166                         tag: 'span',
12167                         cls: 'combobox-clear',
12168                         cn  : [
12169                             {
12170                                 tag : 'i',
12171                                 cls: 'icon-remove'
12172                             }
12173                         ]
12174                     }
12175                 ]
12176
12177             })
12178         }
12179         
12180         if(this.multiple){
12181             combobox.cls += ' roo-select2-container-multi';
12182         }
12183          var indicator = {
12184             tag : 'i',
12185             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12186             tooltip : 'This field is required'
12187         };
12188         if (Roo.bootstrap.version == 4) {
12189             indicator = {
12190                 tag : 'i',
12191                 style : 'display:none'
12192             };
12193         }
12194         
12195         
12196         if (align ==='left' && this.fieldLabel.length) {
12197             
12198             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12199
12200             cfg.cn = [
12201                 indicator,
12202                 {
12203                     tag: 'label',
12204                     'for' :  id,
12205                     cls : 'control-label',
12206                     html : this.fieldLabel
12207
12208                 },
12209                 {
12210                     cls : "", 
12211                     cn: [
12212                         combobox
12213                     ]
12214                 }
12215
12216             ];
12217             
12218             var labelCfg = cfg.cn[1];
12219             var contentCfg = cfg.cn[2];
12220             
12221             if(this.indicatorpos == 'right'){
12222                 cfg.cn = [
12223                     {
12224                         tag: 'label',
12225                         'for' :  id,
12226                         cls : 'control-label',
12227                         cn : [
12228                             {
12229                                 tag : 'span',
12230                                 html : this.fieldLabel
12231                             },
12232                             indicator
12233                         ]
12234                     },
12235                     {
12236                         cls : "", 
12237                         cn: [
12238                             combobox
12239                         ]
12240                     }
12241
12242                 ];
12243                 
12244                 labelCfg = cfg.cn[0];
12245                 contentCfg = cfg.cn[1];
12246             }
12247             
12248             if(this.labelWidth > 12){
12249                 labelCfg.style = "width: " + this.labelWidth + 'px';
12250             }
12251             
12252             if(this.labelWidth < 13 && this.labelmd == 0){
12253                 this.labelmd = this.labelWidth;
12254             }
12255             
12256             if(this.labellg > 0){
12257                 labelCfg.cls += ' col-lg-' + this.labellg;
12258                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12259             }
12260             
12261             if(this.labelmd > 0){
12262                 labelCfg.cls += ' col-md-' + this.labelmd;
12263                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12264             }
12265             
12266             if(this.labelsm > 0){
12267                 labelCfg.cls += ' col-sm-' + this.labelsm;
12268                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12269             }
12270             
12271             if(this.labelxs > 0){
12272                 labelCfg.cls += ' col-xs-' + this.labelxs;
12273                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12274             }
12275             
12276         } else if ( this.fieldLabel.length) {
12277 //                Roo.log(" label");
12278             cfg.cn = [
12279                 indicator,
12280                {
12281                    tag: 'label',
12282                    //cls : 'input-group-addon',
12283                    html : this.fieldLabel
12284
12285                },
12286
12287                combobox
12288
12289             ];
12290             
12291             if(this.indicatorpos == 'right'){
12292                 
12293                 cfg.cn = [
12294                     {
12295                        tag: 'label',
12296                        cn : [
12297                            {
12298                                tag : 'span',
12299                                html : this.fieldLabel
12300                            },
12301                            indicator
12302                        ]
12303
12304                     },
12305                     combobox
12306
12307                 ];
12308
12309             }
12310
12311         } else {
12312             
12313 //                Roo.log(" no label && no align");
12314                 cfg = combobox
12315                      
12316                 
12317         }
12318         
12319         var settings=this;
12320         ['xs','sm','md','lg'].map(function(size){
12321             if (settings[size]) {
12322                 cfg.cls += ' col-' + size + '-' + settings[size];
12323             }
12324         });
12325         
12326         return cfg;
12327         
12328     },
12329     
12330     
12331     
12332     // private
12333     onResize : function(w, h){
12334 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12335 //        if(typeof w == 'number'){
12336 //            var x = w - this.trigger.getWidth();
12337 //            this.inputEl().setWidth(this.adjustWidth('input', x));
12338 //            this.trigger.setStyle('left', x+'px');
12339 //        }
12340     },
12341
12342     // private
12343     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12344
12345     // private
12346     getResizeEl : function(){
12347         return this.inputEl();
12348     },
12349
12350     // private
12351     getPositionEl : function(){
12352         return this.inputEl();
12353     },
12354
12355     // private
12356     alignErrorIcon : function(){
12357         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12358     },
12359
12360     // private
12361     initEvents : function(){
12362         
12363         this.createList();
12364         
12365         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12366         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12367         if(!this.multiple && this.showToggleBtn){
12368             this.trigger = this.el.select('span.dropdown-toggle',true).first();
12369             if(this.hideTrigger){
12370                 this.trigger.setDisplayed(false);
12371             }
12372             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12373         }
12374         
12375         if(this.multiple){
12376             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12377         }
12378         
12379         if(this.removable && !this.editable && !this.tickable){
12380             var close = this.closeTriggerEl();
12381             
12382             if(close){
12383                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12384                 close.on('click', this.removeBtnClick, this, close);
12385             }
12386         }
12387         
12388         //this.trigger.addClassOnOver('x-form-trigger-over');
12389         //this.trigger.addClassOnClick('x-form-trigger-click');
12390         
12391         //if(!this.width){
12392         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12393         //}
12394     },
12395     
12396     closeTriggerEl : function()
12397     {
12398         var close = this.el.select('.roo-combo-removable-btn', true).first();
12399         return close ? close : false;
12400     },
12401     
12402     removeBtnClick : function(e, h, el)
12403     {
12404         e.preventDefault();
12405         
12406         if(this.fireEvent("remove", this) !== false){
12407             this.reset();
12408             this.fireEvent("afterremove", this)
12409         }
12410     },
12411     
12412     createList : function()
12413     {
12414         this.list = Roo.get(document.body).createChild({
12415             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12416             cls: 'typeahead typeahead-long dropdown-menu',
12417             style: 'display:none'
12418         });
12419         
12420         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12421         
12422     },
12423
12424     // private
12425     initTrigger : function(){
12426        
12427     },
12428
12429     // private
12430     onDestroy : function(){
12431         if(this.trigger){
12432             this.trigger.removeAllListeners();
12433           //  this.trigger.remove();
12434         }
12435         //if(this.wrap){
12436         //    this.wrap.remove();
12437         //}
12438         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12439     },
12440
12441     // private
12442     onFocus : function(){
12443         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12444         /*
12445         if(!this.mimicing){
12446             this.wrap.addClass('x-trigger-wrap-focus');
12447             this.mimicing = true;
12448             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12449             if(this.monitorTab){
12450                 this.el.on("keydown", this.checkTab, this);
12451             }
12452         }
12453         */
12454     },
12455
12456     // private
12457     checkTab : function(e){
12458         if(e.getKey() == e.TAB){
12459             this.triggerBlur();
12460         }
12461     },
12462
12463     // private
12464     onBlur : function(){
12465         // do nothing
12466     },
12467
12468     // private
12469     mimicBlur : function(e, t){
12470         /*
12471         if(!this.wrap.contains(t) && this.validateBlur()){
12472             this.triggerBlur();
12473         }
12474         */
12475     },
12476
12477     // private
12478     triggerBlur : function(){
12479         this.mimicing = false;
12480         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12481         if(this.monitorTab){
12482             this.el.un("keydown", this.checkTab, this);
12483         }
12484         //this.wrap.removeClass('x-trigger-wrap-focus');
12485         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12486     },
12487
12488     // private
12489     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12490     validateBlur : function(e, t){
12491         return true;
12492     },
12493
12494     // private
12495     onDisable : function(){
12496         this.inputEl().dom.disabled = true;
12497         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12498         //if(this.wrap){
12499         //    this.wrap.addClass('x-item-disabled');
12500         //}
12501     },
12502
12503     // private
12504     onEnable : function(){
12505         this.inputEl().dom.disabled = false;
12506         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12507         //if(this.wrap){
12508         //    this.el.removeClass('x-item-disabled');
12509         //}
12510     },
12511
12512     // private
12513     onShow : function(){
12514         var ae = this.getActionEl();
12515         
12516         if(ae){
12517             ae.dom.style.display = '';
12518             ae.dom.style.visibility = 'visible';
12519         }
12520     },
12521
12522     // private
12523     
12524     onHide : function(){
12525         var ae = this.getActionEl();
12526         ae.dom.style.display = 'none';
12527     },
12528
12529     /**
12530      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
12531      * by an implementing function.
12532      * @method
12533      * @param {EventObject} e
12534      */
12535     onTriggerClick : Roo.emptyFn
12536 });
12537  /*
12538  * Based on:
12539  * Ext JS Library 1.1.1
12540  * Copyright(c) 2006-2007, Ext JS, LLC.
12541  *
12542  * Originally Released Under LGPL - original licence link has changed is not relivant.
12543  *
12544  * Fork - LGPL
12545  * <script type="text/javascript">
12546  */
12547
12548
12549 /**
12550  * @class Roo.data.SortTypes
12551  * @singleton
12552  * Defines the default sorting (casting?) comparison functions used when sorting data.
12553  */
12554 Roo.data.SortTypes = {
12555     /**
12556      * Default sort that does nothing
12557      * @param {Mixed} s The value being converted
12558      * @return {Mixed} The comparison value
12559      */
12560     none : function(s){
12561         return s;
12562     },
12563     
12564     /**
12565      * The regular expression used to strip tags
12566      * @type {RegExp}
12567      * @property
12568      */
12569     stripTagsRE : /<\/?[^>]+>/gi,
12570     
12571     /**
12572      * Strips all HTML tags to sort on text only
12573      * @param {Mixed} s The value being converted
12574      * @return {String} The comparison value
12575      */
12576     asText : function(s){
12577         return String(s).replace(this.stripTagsRE, "");
12578     },
12579     
12580     /**
12581      * Strips all HTML tags to sort on text only - Case insensitive
12582      * @param {Mixed} s The value being converted
12583      * @return {String} The comparison value
12584      */
12585     asUCText : function(s){
12586         return String(s).toUpperCase().replace(this.stripTagsRE, "");
12587     },
12588     
12589     /**
12590      * Case insensitive string
12591      * @param {Mixed} s The value being converted
12592      * @return {String} The comparison value
12593      */
12594     asUCString : function(s) {
12595         return String(s).toUpperCase();
12596     },
12597     
12598     /**
12599      * Date sorting
12600      * @param {Mixed} s The value being converted
12601      * @return {Number} The comparison value
12602      */
12603     asDate : function(s) {
12604         if(!s){
12605             return 0;
12606         }
12607         if(s instanceof Date){
12608             return s.getTime();
12609         }
12610         return Date.parse(String(s));
12611     },
12612     
12613     /**
12614      * Float sorting
12615      * @param {Mixed} s The value being converted
12616      * @return {Float} The comparison value
12617      */
12618     asFloat : function(s) {
12619         var val = parseFloat(String(s).replace(/,/g, ""));
12620         if(isNaN(val)) {
12621             val = 0;
12622         }
12623         return val;
12624     },
12625     
12626     /**
12627      * Integer sorting
12628      * @param {Mixed} s The value being converted
12629      * @return {Number} The comparison value
12630      */
12631     asInt : function(s) {
12632         var val = parseInt(String(s).replace(/,/g, ""));
12633         if(isNaN(val)) {
12634             val = 0;
12635         }
12636         return val;
12637     }
12638 };/*
12639  * Based on:
12640  * Ext JS Library 1.1.1
12641  * Copyright(c) 2006-2007, Ext JS, LLC.
12642  *
12643  * Originally Released Under LGPL - original licence link has changed is not relivant.
12644  *
12645  * Fork - LGPL
12646  * <script type="text/javascript">
12647  */
12648
12649 /**
12650 * @class Roo.data.Record
12651  * Instances of this class encapsulate both record <em>definition</em> information, and record
12652  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12653  * to access Records cached in an {@link Roo.data.Store} object.<br>
12654  * <p>
12655  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12656  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12657  * objects.<br>
12658  * <p>
12659  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12660  * @constructor
12661  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12662  * {@link #create}. The parameters are the same.
12663  * @param {Array} data An associative Array of data values keyed by the field name.
12664  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12665  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12666  * not specified an integer id is generated.
12667  */
12668 Roo.data.Record = function(data, id){
12669     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12670     this.data = data;
12671 };
12672
12673 /**
12674  * Generate a constructor for a specific record layout.
12675  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12676  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12677  * Each field definition object may contain the following properties: <ul>
12678  * <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,
12679  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12680  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12681  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12682  * is being used, then this is a string containing the javascript expression to reference the data relative to 
12683  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12684  * to the data item relative to the record element. If the mapping expression is the same as the field name,
12685  * this may be omitted.</p></li>
12686  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12687  * <ul><li>auto (Default, implies no conversion)</li>
12688  * <li>string</li>
12689  * <li>int</li>
12690  * <li>float</li>
12691  * <li>boolean</li>
12692  * <li>date</li></ul></p></li>
12693  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12694  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12695  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12696  * by the Reader into an object that will be stored in the Record. It is passed the
12697  * following parameters:<ul>
12698  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12699  * </ul></p></li>
12700  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12701  * </ul>
12702  * <br>usage:<br><pre><code>
12703 var TopicRecord = Roo.data.Record.create(
12704     {name: 'title', mapping: 'topic_title'},
12705     {name: 'author', mapping: 'username'},
12706     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12707     {name: 'lastPost', mapping: 'post_time', type: 'date'},
12708     {name: 'lastPoster', mapping: 'user2'},
12709     {name: 'excerpt', mapping: 'post_text'}
12710 );
12711
12712 var myNewRecord = new TopicRecord({
12713     title: 'Do my job please',
12714     author: 'noobie',
12715     totalPosts: 1,
12716     lastPost: new Date(),
12717     lastPoster: 'Animal',
12718     excerpt: 'No way dude!'
12719 });
12720 myStore.add(myNewRecord);
12721 </code></pre>
12722  * @method create
12723  * @static
12724  */
12725 Roo.data.Record.create = function(o){
12726     var f = function(){
12727         f.superclass.constructor.apply(this, arguments);
12728     };
12729     Roo.extend(f, Roo.data.Record);
12730     var p = f.prototype;
12731     p.fields = new Roo.util.MixedCollection(false, function(field){
12732         return field.name;
12733     });
12734     for(var i = 0, len = o.length; i < len; i++){
12735         p.fields.add(new Roo.data.Field(o[i]));
12736     }
12737     f.getField = function(name){
12738         return p.fields.get(name);  
12739     };
12740     return f;
12741 };
12742
12743 Roo.data.Record.AUTO_ID = 1000;
12744 Roo.data.Record.EDIT = 'edit';
12745 Roo.data.Record.REJECT = 'reject';
12746 Roo.data.Record.COMMIT = 'commit';
12747
12748 Roo.data.Record.prototype = {
12749     /**
12750      * Readonly flag - true if this record has been modified.
12751      * @type Boolean
12752      */
12753     dirty : false,
12754     editing : false,
12755     error: null,
12756     modified: null,
12757
12758     // private
12759     join : function(store){
12760         this.store = store;
12761     },
12762
12763     /**
12764      * Set the named field to the specified value.
12765      * @param {String} name The name of the field to set.
12766      * @param {Object} value The value to set the field to.
12767      */
12768     set : function(name, value){
12769         if(this.data[name] == value){
12770             return;
12771         }
12772         this.dirty = true;
12773         if(!this.modified){
12774             this.modified = {};
12775         }
12776         if(typeof this.modified[name] == 'undefined'){
12777             this.modified[name] = this.data[name];
12778         }
12779         this.data[name] = value;
12780         if(!this.editing && this.store){
12781             this.store.afterEdit(this);
12782         }       
12783     },
12784
12785     /**
12786      * Get the value of the named field.
12787      * @param {String} name The name of the field to get the value of.
12788      * @return {Object} The value of the field.
12789      */
12790     get : function(name){
12791         return this.data[name]; 
12792     },
12793
12794     // private
12795     beginEdit : function(){
12796         this.editing = true;
12797         this.modified = {}; 
12798     },
12799
12800     // private
12801     cancelEdit : function(){
12802         this.editing = false;
12803         delete this.modified;
12804     },
12805
12806     // private
12807     endEdit : function(){
12808         this.editing = false;
12809         if(this.dirty && this.store){
12810             this.store.afterEdit(this);
12811         }
12812     },
12813
12814     /**
12815      * Usually called by the {@link Roo.data.Store} which owns the Record.
12816      * Rejects all changes made to the Record since either creation, or the last commit operation.
12817      * Modified fields are reverted to their original values.
12818      * <p>
12819      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
12820      * of reject operations.
12821      */
12822     reject : function(){
12823         var m = this.modified;
12824         for(var n in m){
12825             if(typeof m[n] != "function"){
12826                 this.data[n] = m[n];
12827             }
12828         }
12829         this.dirty = false;
12830         delete this.modified;
12831         this.editing = false;
12832         if(this.store){
12833             this.store.afterReject(this);
12834         }
12835     },
12836
12837     /**
12838      * Usually called by the {@link Roo.data.Store} which owns the Record.
12839      * Commits all changes made to the Record since either creation, or the last commit operation.
12840      * <p>
12841      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
12842      * of commit operations.
12843      */
12844     commit : function(){
12845         this.dirty = false;
12846         delete this.modified;
12847         this.editing = false;
12848         if(this.store){
12849             this.store.afterCommit(this);
12850         }
12851     },
12852
12853     // private
12854     hasError : function(){
12855         return this.error != null;
12856     },
12857
12858     // private
12859     clearError : function(){
12860         this.error = null;
12861     },
12862
12863     /**
12864      * Creates a copy of this record.
12865      * @param {String} id (optional) A new record id if you don't want to use this record's id
12866      * @return {Record}
12867      */
12868     copy : function(newId) {
12869         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
12870     }
12871 };/*
12872  * Based on:
12873  * Ext JS Library 1.1.1
12874  * Copyright(c) 2006-2007, Ext JS, LLC.
12875  *
12876  * Originally Released Under LGPL - original licence link has changed is not relivant.
12877  *
12878  * Fork - LGPL
12879  * <script type="text/javascript">
12880  */
12881
12882
12883
12884 /**
12885  * @class Roo.data.Store
12886  * @extends Roo.util.Observable
12887  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
12888  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
12889  * <p>
12890  * 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
12891  * has no knowledge of the format of the data returned by the Proxy.<br>
12892  * <p>
12893  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
12894  * instances from the data object. These records are cached and made available through accessor functions.
12895  * @constructor
12896  * Creates a new Store.
12897  * @param {Object} config A config object containing the objects needed for the Store to access data,
12898  * and read the data into Records.
12899  */
12900 Roo.data.Store = function(config){
12901     this.data = new Roo.util.MixedCollection(false);
12902     this.data.getKey = function(o){
12903         return o.id;
12904     };
12905     this.baseParams = {};
12906     // private
12907     this.paramNames = {
12908         "start" : "start",
12909         "limit" : "limit",
12910         "sort" : "sort",
12911         "dir" : "dir",
12912         "multisort" : "_multisort"
12913     };
12914
12915     if(config && config.data){
12916         this.inlineData = config.data;
12917         delete config.data;
12918     }
12919
12920     Roo.apply(this, config);
12921     
12922     if(this.reader){ // reader passed
12923         this.reader = Roo.factory(this.reader, Roo.data);
12924         this.reader.xmodule = this.xmodule || false;
12925         if(!this.recordType){
12926             this.recordType = this.reader.recordType;
12927         }
12928         if(this.reader.onMetaChange){
12929             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
12930         }
12931     }
12932
12933     if(this.recordType){
12934         this.fields = this.recordType.prototype.fields;
12935     }
12936     this.modified = [];
12937
12938     this.addEvents({
12939         /**
12940          * @event datachanged
12941          * Fires when the data cache has changed, and a widget which is using this Store
12942          * as a Record cache should refresh its view.
12943          * @param {Store} this
12944          */
12945         datachanged : true,
12946         /**
12947          * @event metachange
12948          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
12949          * @param {Store} this
12950          * @param {Object} meta The JSON metadata
12951          */
12952         metachange : true,
12953         /**
12954          * @event add
12955          * Fires when Records have been added to the Store
12956          * @param {Store} this
12957          * @param {Roo.data.Record[]} records The array of Records added
12958          * @param {Number} index The index at which the record(s) were added
12959          */
12960         add : true,
12961         /**
12962          * @event remove
12963          * Fires when a Record has been removed from the Store
12964          * @param {Store} this
12965          * @param {Roo.data.Record} record The Record that was removed
12966          * @param {Number} index The index at which the record was removed
12967          */
12968         remove : true,
12969         /**
12970          * @event update
12971          * Fires when a Record has been updated
12972          * @param {Store} this
12973          * @param {Roo.data.Record} record The Record that was updated
12974          * @param {String} operation The update operation being performed.  Value may be one of:
12975          * <pre><code>
12976  Roo.data.Record.EDIT
12977  Roo.data.Record.REJECT
12978  Roo.data.Record.COMMIT
12979          * </code></pre>
12980          */
12981         update : true,
12982         /**
12983          * @event clear
12984          * Fires when the data cache has been cleared.
12985          * @param {Store} this
12986          */
12987         clear : true,
12988         /**
12989          * @event beforeload
12990          * Fires before a request is made for a new data object.  If the beforeload handler returns false
12991          * the load action will be canceled.
12992          * @param {Store} this
12993          * @param {Object} options The loading options that were specified (see {@link #load} for details)
12994          */
12995         beforeload : true,
12996         /**
12997          * @event beforeloadadd
12998          * Fires after a new set of Records has been loaded.
12999          * @param {Store} this
13000          * @param {Roo.data.Record[]} records The Records that were loaded
13001          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13002          */
13003         beforeloadadd : true,
13004         /**
13005          * @event load
13006          * Fires after a new set of Records has been loaded, before they are added to the store.
13007          * @param {Store} this
13008          * @param {Roo.data.Record[]} records The Records that were loaded
13009          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13010          * @params {Object} return from reader
13011          */
13012         load : true,
13013         /**
13014          * @event loadexception
13015          * Fires if an exception occurs in the Proxy during loading.
13016          * Called with the signature of the Proxy's "loadexception" event.
13017          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13018          * 
13019          * @param {Proxy} 
13020          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13021          * @param {Object} load options 
13022          * @param {Object} jsonData from your request (normally this contains the Exception)
13023          */
13024         loadexception : true
13025     });
13026     
13027     if(this.proxy){
13028         this.proxy = Roo.factory(this.proxy, Roo.data);
13029         this.proxy.xmodule = this.xmodule || false;
13030         this.relayEvents(this.proxy,  ["loadexception"]);
13031     }
13032     this.sortToggle = {};
13033     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13034
13035     Roo.data.Store.superclass.constructor.call(this);
13036
13037     if(this.inlineData){
13038         this.loadData(this.inlineData);
13039         delete this.inlineData;
13040     }
13041 };
13042
13043 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13044      /**
13045     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13046     * without a remote query - used by combo/forms at present.
13047     */
13048     
13049     /**
13050     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13051     */
13052     /**
13053     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13054     */
13055     /**
13056     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13057     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13058     */
13059     /**
13060     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13061     * on any HTTP request
13062     */
13063     /**
13064     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13065     */
13066     /**
13067     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13068     */
13069     multiSort: false,
13070     /**
13071     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13072     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13073     */
13074     remoteSort : false,
13075
13076     /**
13077     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13078      * loaded or when a record is removed. (defaults to false).
13079     */
13080     pruneModifiedRecords : false,
13081
13082     // private
13083     lastOptions : null,
13084
13085     /**
13086      * Add Records to the Store and fires the add event.
13087      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13088      */
13089     add : function(records){
13090         records = [].concat(records);
13091         for(var i = 0, len = records.length; i < len; i++){
13092             records[i].join(this);
13093         }
13094         var index = this.data.length;
13095         this.data.addAll(records);
13096         this.fireEvent("add", this, records, index);
13097     },
13098
13099     /**
13100      * Remove a Record from the Store and fires the remove event.
13101      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13102      */
13103     remove : function(record){
13104         var index = this.data.indexOf(record);
13105         this.data.removeAt(index);
13106  
13107         if(this.pruneModifiedRecords){
13108             this.modified.remove(record);
13109         }
13110         this.fireEvent("remove", this, record, index);
13111     },
13112
13113     /**
13114      * Remove all Records from the Store and fires the clear event.
13115      */
13116     removeAll : function(){
13117         this.data.clear();
13118         if(this.pruneModifiedRecords){
13119             this.modified = [];
13120         }
13121         this.fireEvent("clear", this);
13122     },
13123
13124     /**
13125      * Inserts Records to the Store at the given index and fires the add event.
13126      * @param {Number} index The start index at which to insert the passed Records.
13127      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13128      */
13129     insert : function(index, records){
13130         records = [].concat(records);
13131         for(var i = 0, len = records.length; i < len; i++){
13132             this.data.insert(index, records[i]);
13133             records[i].join(this);
13134         }
13135         this.fireEvent("add", this, records, index);
13136     },
13137
13138     /**
13139      * Get the index within the cache of the passed Record.
13140      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13141      * @return {Number} The index of the passed Record. Returns -1 if not found.
13142      */
13143     indexOf : function(record){
13144         return this.data.indexOf(record);
13145     },
13146
13147     /**
13148      * Get the index within the cache of the Record with the passed id.
13149      * @param {String} id The id of the Record to find.
13150      * @return {Number} The index of the Record. Returns -1 if not found.
13151      */
13152     indexOfId : function(id){
13153         return this.data.indexOfKey(id);
13154     },
13155
13156     /**
13157      * Get the Record with the specified id.
13158      * @param {String} id The id of the Record to find.
13159      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13160      */
13161     getById : function(id){
13162         return this.data.key(id);
13163     },
13164
13165     /**
13166      * Get the Record at the specified index.
13167      * @param {Number} index The index of the Record to find.
13168      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13169      */
13170     getAt : function(index){
13171         return this.data.itemAt(index);
13172     },
13173
13174     /**
13175      * Returns a range of Records between specified indices.
13176      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13177      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13178      * @return {Roo.data.Record[]} An array of Records
13179      */
13180     getRange : function(start, end){
13181         return this.data.getRange(start, end);
13182     },
13183
13184     // private
13185     storeOptions : function(o){
13186         o = Roo.apply({}, o);
13187         delete o.callback;
13188         delete o.scope;
13189         this.lastOptions = o;
13190     },
13191
13192     /**
13193      * Loads the Record cache from the configured Proxy using the configured Reader.
13194      * <p>
13195      * If using remote paging, then the first load call must specify the <em>start</em>
13196      * and <em>limit</em> properties in the options.params property to establish the initial
13197      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13198      * <p>
13199      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13200      * and this call will return before the new data has been loaded. Perform any post-processing
13201      * in a callback function, or in a "load" event handler.</strong>
13202      * <p>
13203      * @param {Object} options An object containing properties which control loading options:<ul>
13204      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13205      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13206      * passed the following arguments:<ul>
13207      * <li>r : Roo.data.Record[]</li>
13208      * <li>options: Options object from the load call</li>
13209      * <li>success: Boolean success indicator</li></ul></li>
13210      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13211      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13212      * </ul>
13213      */
13214     load : function(options){
13215         options = options || {};
13216         if(this.fireEvent("beforeload", this, options) !== false){
13217             this.storeOptions(options);
13218             var p = Roo.apply(options.params || {}, this.baseParams);
13219             // if meta was not loaded from remote source.. try requesting it.
13220             if (!this.reader.metaFromRemote) {
13221                 p._requestMeta = 1;
13222             }
13223             if(this.sortInfo && this.remoteSort){
13224                 var pn = this.paramNames;
13225                 p[pn["sort"]] = this.sortInfo.field;
13226                 p[pn["dir"]] = this.sortInfo.direction;
13227             }
13228             if (this.multiSort) {
13229                 var pn = this.paramNames;
13230                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13231             }
13232             
13233             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13234         }
13235     },
13236
13237     /**
13238      * Reloads the Record cache from the configured Proxy using the configured Reader and
13239      * the options from the last load operation performed.
13240      * @param {Object} options (optional) An object containing properties which may override the options
13241      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13242      * the most recently used options are reused).
13243      */
13244     reload : function(options){
13245         this.load(Roo.applyIf(options||{}, this.lastOptions));
13246     },
13247
13248     // private
13249     // Called as a callback by the Reader during a load operation.
13250     loadRecords : function(o, options, success){
13251         if(!o || success === false){
13252             if(success !== false){
13253                 this.fireEvent("load", this, [], options, o);
13254             }
13255             if(options.callback){
13256                 options.callback.call(options.scope || this, [], options, false);
13257             }
13258             return;
13259         }
13260         // if data returned failure - throw an exception.
13261         if (o.success === false) {
13262             // show a message if no listener is registered.
13263             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13264                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13265             }
13266             // loadmask wil be hooked into this..
13267             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13268             return;
13269         }
13270         var r = o.records, t = o.totalRecords || r.length;
13271         
13272         this.fireEvent("beforeloadadd", this, r, options, o);
13273         
13274         if(!options || options.add !== true){
13275             if(this.pruneModifiedRecords){
13276                 this.modified = [];
13277             }
13278             for(var i = 0, len = r.length; i < len; i++){
13279                 r[i].join(this);
13280             }
13281             if(this.snapshot){
13282                 this.data = this.snapshot;
13283                 delete this.snapshot;
13284             }
13285             this.data.clear();
13286             this.data.addAll(r);
13287             this.totalLength = t;
13288             this.applySort();
13289             this.fireEvent("datachanged", this);
13290         }else{
13291             this.totalLength = Math.max(t, this.data.length+r.length);
13292             this.add(r);
13293         }
13294         
13295         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13296                 
13297             var e = new Roo.data.Record({});
13298
13299             e.set(this.parent.displayField, this.parent.emptyTitle);
13300             e.set(this.parent.valueField, '');
13301
13302             this.insert(0, e);
13303         }
13304             
13305         this.fireEvent("load", this, r, options, o);
13306         if(options.callback){
13307             options.callback.call(options.scope || this, r, options, true);
13308         }
13309     },
13310
13311
13312     /**
13313      * Loads data from a passed data block. A Reader which understands the format of the data
13314      * must have been configured in the constructor.
13315      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13316      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13317      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13318      */
13319     loadData : function(o, append){
13320         var r = this.reader.readRecords(o);
13321         this.loadRecords(r, {add: append}, true);
13322     },
13323     
13324      /**
13325      * using 'cn' the nested child reader read the child array into it's child stores.
13326      * @param {Object} rec The record with a 'children array
13327      */
13328     loadDataFromChildren : function(rec)
13329     {
13330         this.loadData(this.reader.toLoadData(rec));
13331     },
13332     
13333
13334     /**
13335      * Gets the number of cached records.
13336      * <p>
13337      * <em>If using paging, this may not be the total size of the dataset. If the data object
13338      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13339      * the data set size</em>
13340      */
13341     getCount : function(){
13342         return this.data.length || 0;
13343     },
13344
13345     /**
13346      * Gets the total number of records in the dataset as returned by the server.
13347      * <p>
13348      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13349      * the dataset size</em>
13350      */
13351     getTotalCount : function(){
13352         return this.totalLength || 0;
13353     },
13354
13355     /**
13356      * Returns the sort state of the Store as an object with two properties:
13357      * <pre><code>
13358  field {String} The name of the field by which the Records are sorted
13359  direction {String} The sort order, "ASC" or "DESC"
13360      * </code></pre>
13361      */
13362     getSortState : function(){
13363         return this.sortInfo;
13364     },
13365
13366     // private
13367     applySort : function(){
13368         if(this.sortInfo && !this.remoteSort){
13369             var s = this.sortInfo, f = s.field;
13370             var st = this.fields.get(f).sortType;
13371             var fn = function(r1, r2){
13372                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13373                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13374             };
13375             this.data.sort(s.direction, fn);
13376             if(this.snapshot && this.snapshot != this.data){
13377                 this.snapshot.sort(s.direction, fn);
13378             }
13379         }
13380     },
13381
13382     /**
13383      * Sets the default sort column and order to be used by the next load operation.
13384      * @param {String} fieldName The name of the field to sort by.
13385      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13386      */
13387     setDefaultSort : function(field, dir){
13388         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13389     },
13390
13391     /**
13392      * Sort the Records.
13393      * If remote sorting is used, the sort is performed on the server, and the cache is
13394      * reloaded. If local sorting is used, the cache is sorted internally.
13395      * @param {String} fieldName The name of the field to sort by.
13396      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13397      */
13398     sort : function(fieldName, dir){
13399         var f = this.fields.get(fieldName);
13400         if(!dir){
13401             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13402             
13403             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13404                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13405             }else{
13406                 dir = f.sortDir;
13407             }
13408         }
13409         this.sortToggle[f.name] = dir;
13410         this.sortInfo = {field: f.name, direction: dir};
13411         if(!this.remoteSort){
13412             this.applySort();
13413             this.fireEvent("datachanged", this);
13414         }else{
13415             this.load(this.lastOptions);
13416         }
13417     },
13418
13419     /**
13420      * Calls the specified function for each of the Records in the cache.
13421      * @param {Function} fn The function to call. The Record is passed as the first parameter.
13422      * Returning <em>false</em> aborts and exits the iteration.
13423      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13424      */
13425     each : function(fn, scope){
13426         this.data.each(fn, scope);
13427     },
13428
13429     /**
13430      * Gets all records modified since the last commit.  Modified records are persisted across load operations
13431      * (e.g., during paging).
13432      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13433      */
13434     getModifiedRecords : function(){
13435         return this.modified;
13436     },
13437
13438     // private
13439     createFilterFn : function(property, value, anyMatch){
13440         if(!value.exec){ // not a regex
13441             value = String(value);
13442             if(value.length == 0){
13443                 return false;
13444             }
13445             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13446         }
13447         return function(r){
13448             return value.test(r.data[property]);
13449         };
13450     },
13451
13452     /**
13453      * Sums the value of <i>property</i> for each record between start and end and returns the result.
13454      * @param {String} property A field on your records
13455      * @param {Number} start The record index to start at (defaults to 0)
13456      * @param {Number} end The last record index to include (defaults to length - 1)
13457      * @return {Number} The sum
13458      */
13459     sum : function(property, start, end){
13460         var rs = this.data.items, v = 0;
13461         start = start || 0;
13462         end = (end || end === 0) ? end : rs.length-1;
13463
13464         for(var i = start; i <= end; i++){
13465             v += (rs[i].data[property] || 0);
13466         }
13467         return v;
13468     },
13469
13470     /**
13471      * Filter the records by a specified property.
13472      * @param {String} field A field on your records
13473      * @param {String/RegExp} value Either a string that the field
13474      * should start with or a RegExp to test against the field
13475      * @param {Boolean} anyMatch True to match any part not just the beginning
13476      */
13477     filter : function(property, value, anyMatch){
13478         var fn = this.createFilterFn(property, value, anyMatch);
13479         return fn ? this.filterBy(fn) : this.clearFilter();
13480     },
13481
13482     /**
13483      * Filter by a function. The specified function will be called with each
13484      * record in this data source. If the function returns true the record is included,
13485      * otherwise it is filtered.
13486      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13487      * @param {Object} scope (optional) The scope of the function (defaults to this)
13488      */
13489     filterBy : function(fn, scope){
13490         this.snapshot = this.snapshot || this.data;
13491         this.data = this.queryBy(fn, scope||this);
13492         this.fireEvent("datachanged", this);
13493     },
13494
13495     /**
13496      * Query the records by a specified property.
13497      * @param {String} field A field on your records
13498      * @param {String/RegExp} value Either a string that the field
13499      * should start with or a RegExp to test against the field
13500      * @param {Boolean} anyMatch True to match any part not just the beginning
13501      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13502      */
13503     query : function(property, value, anyMatch){
13504         var fn = this.createFilterFn(property, value, anyMatch);
13505         return fn ? this.queryBy(fn) : this.data.clone();
13506     },
13507
13508     /**
13509      * Query by a function. The specified function will be called with each
13510      * record in this data source. If the function returns true the record is included
13511      * in the results.
13512      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13513      * @param {Object} scope (optional) The scope of the function (defaults to this)
13514       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13515      **/
13516     queryBy : function(fn, scope){
13517         var data = this.snapshot || this.data;
13518         return data.filterBy(fn, scope||this);
13519     },
13520
13521     /**
13522      * Collects unique values for a particular dataIndex from this store.
13523      * @param {String} dataIndex The property to collect
13524      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13525      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13526      * @return {Array} An array of the unique values
13527      **/
13528     collect : function(dataIndex, allowNull, bypassFilter){
13529         var d = (bypassFilter === true && this.snapshot) ?
13530                 this.snapshot.items : this.data.items;
13531         var v, sv, r = [], l = {};
13532         for(var i = 0, len = d.length; i < len; i++){
13533             v = d[i].data[dataIndex];
13534             sv = String(v);
13535             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13536                 l[sv] = true;
13537                 r[r.length] = v;
13538             }
13539         }
13540         return r;
13541     },
13542
13543     /**
13544      * Revert to a view of the Record cache with no filtering applied.
13545      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13546      */
13547     clearFilter : function(suppressEvent){
13548         if(this.snapshot && this.snapshot != this.data){
13549             this.data = this.snapshot;
13550             delete this.snapshot;
13551             if(suppressEvent !== true){
13552                 this.fireEvent("datachanged", this);
13553             }
13554         }
13555     },
13556
13557     // private
13558     afterEdit : function(record){
13559         if(this.modified.indexOf(record) == -1){
13560             this.modified.push(record);
13561         }
13562         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13563     },
13564     
13565     // private
13566     afterReject : function(record){
13567         this.modified.remove(record);
13568         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13569     },
13570
13571     // private
13572     afterCommit : function(record){
13573         this.modified.remove(record);
13574         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13575     },
13576
13577     /**
13578      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13579      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13580      */
13581     commitChanges : function(){
13582         var m = this.modified.slice(0);
13583         this.modified = [];
13584         for(var i = 0, len = m.length; i < len; i++){
13585             m[i].commit();
13586         }
13587     },
13588
13589     /**
13590      * Cancel outstanding changes on all changed records.
13591      */
13592     rejectChanges : function(){
13593         var m = this.modified.slice(0);
13594         this.modified = [];
13595         for(var i = 0, len = m.length; i < len; i++){
13596             m[i].reject();
13597         }
13598     },
13599
13600     onMetaChange : function(meta, rtype, o){
13601         this.recordType = rtype;
13602         this.fields = rtype.prototype.fields;
13603         delete this.snapshot;
13604         this.sortInfo = meta.sortInfo || this.sortInfo;
13605         this.modified = [];
13606         this.fireEvent('metachange', this, this.reader.meta);
13607     },
13608     
13609     moveIndex : function(data, type)
13610     {
13611         var index = this.indexOf(data);
13612         
13613         var newIndex = index + type;
13614         
13615         this.remove(data);
13616         
13617         this.insert(newIndex, data);
13618         
13619     }
13620 });/*
13621  * Based on:
13622  * Ext JS Library 1.1.1
13623  * Copyright(c) 2006-2007, Ext JS, LLC.
13624  *
13625  * Originally Released Under LGPL - original licence link has changed is not relivant.
13626  *
13627  * Fork - LGPL
13628  * <script type="text/javascript">
13629  */
13630
13631 /**
13632  * @class Roo.data.SimpleStore
13633  * @extends Roo.data.Store
13634  * Small helper class to make creating Stores from Array data easier.
13635  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13636  * @cfg {Array} fields An array of field definition objects, or field name strings.
13637  * @cfg {Object} an existing reader (eg. copied from another store)
13638  * @cfg {Array} data The multi-dimensional array of data
13639  * @constructor
13640  * @param {Object} config
13641  */
13642 Roo.data.SimpleStore = function(config)
13643 {
13644     Roo.data.SimpleStore.superclass.constructor.call(this, {
13645         isLocal : true,
13646         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13647                 id: config.id
13648             },
13649             Roo.data.Record.create(config.fields)
13650         ),
13651         proxy : new Roo.data.MemoryProxy(config.data)
13652     });
13653     this.load();
13654 };
13655 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13656  * Based on:
13657  * Ext JS Library 1.1.1
13658  * Copyright(c) 2006-2007, Ext JS, LLC.
13659  *
13660  * Originally Released Under LGPL - original licence link has changed is not relivant.
13661  *
13662  * Fork - LGPL
13663  * <script type="text/javascript">
13664  */
13665
13666 /**
13667 /**
13668  * @extends Roo.data.Store
13669  * @class Roo.data.JsonStore
13670  * Small helper class to make creating Stores for JSON data easier. <br/>
13671 <pre><code>
13672 var store = new Roo.data.JsonStore({
13673     url: 'get-images.php',
13674     root: 'images',
13675     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13676 });
13677 </code></pre>
13678  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13679  * JsonReader and HttpProxy (unless inline data is provided).</b>
13680  * @cfg {Array} fields An array of field definition objects, or field name strings.
13681  * @constructor
13682  * @param {Object} config
13683  */
13684 Roo.data.JsonStore = function(c){
13685     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13686         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13687         reader: new Roo.data.JsonReader(c, c.fields)
13688     }));
13689 };
13690 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13691  * Based on:
13692  * Ext JS Library 1.1.1
13693  * Copyright(c) 2006-2007, Ext JS, LLC.
13694  *
13695  * Originally Released Under LGPL - original licence link has changed is not relivant.
13696  *
13697  * Fork - LGPL
13698  * <script type="text/javascript">
13699  */
13700
13701  
13702 Roo.data.Field = function(config){
13703     if(typeof config == "string"){
13704         config = {name: config};
13705     }
13706     Roo.apply(this, config);
13707     
13708     if(!this.type){
13709         this.type = "auto";
13710     }
13711     
13712     var st = Roo.data.SortTypes;
13713     // named sortTypes are supported, here we look them up
13714     if(typeof this.sortType == "string"){
13715         this.sortType = st[this.sortType];
13716     }
13717     
13718     // set default sortType for strings and dates
13719     if(!this.sortType){
13720         switch(this.type){
13721             case "string":
13722                 this.sortType = st.asUCString;
13723                 break;
13724             case "date":
13725                 this.sortType = st.asDate;
13726                 break;
13727             default:
13728                 this.sortType = st.none;
13729         }
13730     }
13731
13732     // define once
13733     var stripRe = /[\$,%]/g;
13734
13735     // prebuilt conversion function for this field, instead of
13736     // switching every time we're reading a value
13737     if(!this.convert){
13738         var cv, dateFormat = this.dateFormat;
13739         switch(this.type){
13740             case "":
13741             case "auto":
13742             case undefined:
13743                 cv = function(v){ return v; };
13744                 break;
13745             case "string":
13746                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
13747                 break;
13748             case "int":
13749                 cv = function(v){
13750                     return v !== undefined && v !== null && v !== '' ?
13751                            parseInt(String(v).replace(stripRe, ""), 10) : '';
13752                     };
13753                 break;
13754             case "float":
13755                 cv = function(v){
13756                     return v !== undefined && v !== null && v !== '' ?
13757                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
13758                     };
13759                 break;
13760             case "bool":
13761             case "boolean":
13762                 cv = function(v){ return v === true || v === "true" || v == 1; };
13763                 break;
13764             case "date":
13765                 cv = function(v){
13766                     if(!v){
13767                         return '';
13768                     }
13769                     if(v instanceof Date){
13770                         return v;
13771                     }
13772                     if(dateFormat){
13773                         if(dateFormat == "timestamp"){
13774                             return new Date(v*1000);
13775                         }
13776                         return Date.parseDate(v, dateFormat);
13777                     }
13778                     var parsed = Date.parse(v);
13779                     return parsed ? new Date(parsed) : null;
13780                 };
13781              break;
13782             
13783         }
13784         this.convert = cv;
13785     }
13786 };
13787
13788 Roo.data.Field.prototype = {
13789     dateFormat: null,
13790     defaultValue: "",
13791     mapping: null,
13792     sortType : null,
13793     sortDir : "ASC"
13794 };/*
13795  * Based on:
13796  * Ext JS Library 1.1.1
13797  * Copyright(c) 2006-2007, Ext JS, LLC.
13798  *
13799  * Originally Released Under LGPL - original licence link has changed is not relivant.
13800  *
13801  * Fork - LGPL
13802  * <script type="text/javascript">
13803  */
13804  
13805 // Base class for reading structured data from a data source.  This class is intended to be
13806 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
13807
13808 /**
13809  * @class Roo.data.DataReader
13810  * Base class for reading structured data from a data source.  This class is intended to be
13811  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
13812  */
13813
13814 Roo.data.DataReader = function(meta, recordType){
13815     
13816     this.meta = meta;
13817     
13818     this.recordType = recordType instanceof Array ? 
13819         Roo.data.Record.create(recordType) : recordType;
13820 };
13821
13822 Roo.data.DataReader.prototype = {
13823     
13824     
13825     readerType : 'Data',
13826      /**
13827      * Create an empty record
13828      * @param {Object} data (optional) - overlay some values
13829      * @return {Roo.data.Record} record created.
13830      */
13831     newRow :  function(d) {
13832         var da =  {};
13833         this.recordType.prototype.fields.each(function(c) {
13834             switch( c.type) {
13835                 case 'int' : da[c.name] = 0; break;
13836                 case 'date' : da[c.name] = new Date(); break;
13837                 case 'float' : da[c.name] = 0.0; break;
13838                 case 'boolean' : da[c.name] = false; break;
13839                 default : da[c.name] = ""; break;
13840             }
13841             
13842         });
13843         return new this.recordType(Roo.apply(da, d));
13844     }
13845     
13846     
13847 };/*
13848  * Based on:
13849  * Ext JS Library 1.1.1
13850  * Copyright(c) 2006-2007, Ext JS, LLC.
13851  *
13852  * Originally Released Under LGPL - original licence link has changed is not relivant.
13853  *
13854  * Fork - LGPL
13855  * <script type="text/javascript">
13856  */
13857
13858 /**
13859  * @class Roo.data.DataProxy
13860  * @extends Roo.data.Observable
13861  * This class is an abstract base class for implementations which provide retrieval of
13862  * unformatted data objects.<br>
13863  * <p>
13864  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
13865  * (of the appropriate type which knows how to parse the data object) to provide a block of
13866  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
13867  * <p>
13868  * Custom implementations must implement the load method as described in
13869  * {@link Roo.data.HttpProxy#load}.
13870  */
13871 Roo.data.DataProxy = function(){
13872     this.addEvents({
13873         /**
13874          * @event beforeload
13875          * Fires before a network request is made to retrieve a data object.
13876          * @param {Object} This DataProxy object.
13877          * @param {Object} params The params parameter to the load function.
13878          */
13879         beforeload : true,
13880         /**
13881          * @event load
13882          * Fires before the load method's callback is called.
13883          * @param {Object} This DataProxy object.
13884          * @param {Object} o The data object.
13885          * @param {Object} arg The callback argument object passed to the load function.
13886          */
13887         load : true,
13888         /**
13889          * @event loadexception
13890          * Fires if an Exception occurs during data retrieval.
13891          * @param {Object} This DataProxy object.
13892          * @param {Object} o The data object.
13893          * @param {Object} arg The callback argument object passed to the load function.
13894          * @param {Object} e The Exception.
13895          */
13896         loadexception : true
13897     });
13898     Roo.data.DataProxy.superclass.constructor.call(this);
13899 };
13900
13901 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
13902
13903     /**
13904      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
13905      */
13906 /*
13907  * Based on:
13908  * Ext JS Library 1.1.1
13909  * Copyright(c) 2006-2007, Ext JS, LLC.
13910  *
13911  * Originally Released Under LGPL - original licence link has changed is not relivant.
13912  *
13913  * Fork - LGPL
13914  * <script type="text/javascript">
13915  */
13916 /**
13917  * @class Roo.data.MemoryProxy
13918  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
13919  * to the Reader when its load method is called.
13920  * @constructor
13921  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
13922  */
13923 Roo.data.MemoryProxy = function(data){
13924     if (data.data) {
13925         data = data.data;
13926     }
13927     Roo.data.MemoryProxy.superclass.constructor.call(this);
13928     this.data = data;
13929 };
13930
13931 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
13932     
13933     /**
13934      * Load data from the requested source (in this case an in-memory
13935      * data object passed to the constructor), read the data object into
13936      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
13937      * process that block using the passed callback.
13938      * @param {Object} params This parameter is not used by the MemoryProxy class.
13939      * @param {Roo.data.DataReader} reader The Reader object which converts the data
13940      * object into a block of Roo.data.Records.
13941      * @param {Function} callback The function into which to pass the block of Roo.data.records.
13942      * The function must be passed <ul>
13943      * <li>The Record block object</li>
13944      * <li>The "arg" argument from the load function</li>
13945      * <li>A boolean success indicator</li>
13946      * </ul>
13947      * @param {Object} scope The scope in which to call the callback
13948      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
13949      */
13950     load : function(params, reader, callback, scope, arg){
13951         params = params || {};
13952         var result;
13953         try {
13954             result = reader.readRecords(params.data ? params.data :this.data);
13955         }catch(e){
13956             this.fireEvent("loadexception", this, arg, null, e);
13957             callback.call(scope, null, arg, false);
13958             return;
13959         }
13960         callback.call(scope, result, arg, true);
13961     },
13962     
13963     // private
13964     update : function(params, records){
13965         
13966     }
13967 });/*
13968  * Based on:
13969  * Ext JS Library 1.1.1
13970  * Copyright(c) 2006-2007, Ext JS, LLC.
13971  *
13972  * Originally Released Under LGPL - original licence link has changed is not relivant.
13973  *
13974  * Fork - LGPL
13975  * <script type="text/javascript">
13976  */
13977 /**
13978  * @class Roo.data.HttpProxy
13979  * @extends Roo.data.DataProxy
13980  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
13981  * configured to reference a certain URL.<br><br>
13982  * <p>
13983  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
13984  * from which the running page was served.<br><br>
13985  * <p>
13986  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
13987  * <p>
13988  * Be aware that to enable the browser to parse an XML document, the server must set
13989  * the Content-Type header in the HTTP response to "text/xml".
13990  * @constructor
13991  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
13992  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
13993  * will be used to make the request.
13994  */
13995 Roo.data.HttpProxy = function(conn){
13996     Roo.data.HttpProxy.superclass.constructor.call(this);
13997     // is conn a conn config or a real conn?
13998     this.conn = conn;
13999     this.useAjax = !conn || !conn.events;
14000   
14001 };
14002
14003 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14004     // thse are take from connection...
14005     
14006     /**
14007      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14008      */
14009     /**
14010      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14011      * extra parameters to each request made by this object. (defaults to undefined)
14012      */
14013     /**
14014      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14015      *  to each request made by this object. (defaults to undefined)
14016      */
14017     /**
14018      * @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)
14019      */
14020     /**
14021      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14022      */
14023      /**
14024      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14025      * @type Boolean
14026      */
14027   
14028
14029     /**
14030      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14031      * @type Boolean
14032      */
14033     /**
14034      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14035      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14036      * a finer-grained basis than the DataProxy events.
14037      */
14038     getConnection : function(){
14039         return this.useAjax ? Roo.Ajax : this.conn;
14040     },
14041
14042     /**
14043      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14044      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14045      * process that block using the passed callback.
14046      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14047      * for the request to the remote server.
14048      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14049      * object into a block of Roo.data.Records.
14050      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14051      * The function must be passed <ul>
14052      * <li>The Record block object</li>
14053      * <li>The "arg" argument from the load function</li>
14054      * <li>A boolean success indicator</li>
14055      * </ul>
14056      * @param {Object} scope The scope in which to call the callback
14057      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14058      */
14059     load : function(params, reader, callback, scope, arg){
14060         if(this.fireEvent("beforeload", this, params) !== false){
14061             var  o = {
14062                 params : params || {},
14063                 request: {
14064                     callback : callback,
14065                     scope : scope,
14066                     arg : arg
14067                 },
14068                 reader: reader,
14069                 callback : this.loadResponse,
14070                 scope: this
14071             };
14072             if(this.useAjax){
14073                 Roo.applyIf(o, this.conn);
14074                 if(this.activeRequest){
14075                     Roo.Ajax.abort(this.activeRequest);
14076                 }
14077                 this.activeRequest = Roo.Ajax.request(o);
14078             }else{
14079                 this.conn.request(o);
14080             }
14081         }else{
14082             callback.call(scope||this, null, arg, false);
14083         }
14084     },
14085
14086     // private
14087     loadResponse : function(o, success, response){
14088         delete this.activeRequest;
14089         if(!success){
14090             this.fireEvent("loadexception", this, o, response);
14091             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14092             return;
14093         }
14094         var result;
14095         try {
14096             result = o.reader.read(response);
14097         }catch(e){
14098             this.fireEvent("loadexception", this, o, response, e);
14099             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14100             return;
14101         }
14102         
14103         this.fireEvent("load", this, o, o.request.arg);
14104         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14105     },
14106
14107     // private
14108     update : function(dataSet){
14109
14110     },
14111
14112     // private
14113     updateResponse : function(dataSet){
14114
14115     }
14116 });/*
14117  * Based on:
14118  * Ext JS Library 1.1.1
14119  * Copyright(c) 2006-2007, Ext JS, LLC.
14120  *
14121  * Originally Released Under LGPL - original licence link has changed is not relivant.
14122  *
14123  * Fork - LGPL
14124  * <script type="text/javascript">
14125  */
14126
14127 /**
14128  * @class Roo.data.ScriptTagProxy
14129  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14130  * other than the originating domain of the running page.<br><br>
14131  * <p>
14132  * <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
14133  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14134  * <p>
14135  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14136  * source code that is used as the source inside a &lt;script> tag.<br><br>
14137  * <p>
14138  * In order for the browser to process the returned data, the server must wrap the data object
14139  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14140  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14141  * depending on whether the callback name was passed:
14142  * <p>
14143  * <pre><code>
14144 boolean scriptTag = false;
14145 String cb = request.getParameter("callback");
14146 if (cb != null) {
14147     scriptTag = true;
14148     response.setContentType("text/javascript");
14149 } else {
14150     response.setContentType("application/x-json");
14151 }
14152 Writer out = response.getWriter();
14153 if (scriptTag) {
14154     out.write(cb + "(");
14155 }
14156 out.print(dataBlock.toJsonString());
14157 if (scriptTag) {
14158     out.write(");");
14159 }
14160 </pre></code>
14161  *
14162  * @constructor
14163  * @param {Object} config A configuration object.
14164  */
14165 Roo.data.ScriptTagProxy = function(config){
14166     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14167     Roo.apply(this, config);
14168     this.head = document.getElementsByTagName("head")[0];
14169 };
14170
14171 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14172
14173 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14174     /**
14175      * @cfg {String} url The URL from which to request the data object.
14176      */
14177     /**
14178      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14179      */
14180     timeout : 30000,
14181     /**
14182      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14183      * the server the name of the callback function set up by the load call to process the returned data object.
14184      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14185      * javascript output which calls this named function passing the data object as its only parameter.
14186      */
14187     callbackParam : "callback",
14188     /**
14189      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14190      * name to the request.
14191      */
14192     nocache : true,
14193
14194     /**
14195      * Load data from the configured URL, read the data object into
14196      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14197      * process that block using the passed callback.
14198      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14199      * for the request to the remote server.
14200      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14201      * object into a block of Roo.data.Records.
14202      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14203      * The function must be passed <ul>
14204      * <li>The Record block object</li>
14205      * <li>The "arg" argument from the load function</li>
14206      * <li>A boolean success indicator</li>
14207      * </ul>
14208      * @param {Object} scope The scope in which to call the callback
14209      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14210      */
14211     load : function(params, reader, callback, scope, arg){
14212         if(this.fireEvent("beforeload", this, params) !== false){
14213
14214             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14215
14216             var url = this.url;
14217             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14218             if(this.nocache){
14219                 url += "&_dc=" + (new Date().getTime());
14220             }
14221             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14222             var trans = {
14223                 id : transId,
14224                 cb : "stcCallback"+transId,
14225                 scriptId : "stcScript"+transId,
14226                 params : params,
14227                 arg : arg,
14228                 url : url,
14229                 callback : callback,
14230                 scope : scope,
14231                 reader : reader
14232             };
14233             var conn = this;
14234
14235             window[trans.cb] = function(o){
14236                 conn.handleResponse(o, trans);
14237             };
14238
14239             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14240
14241             if(this.autoAbort !== false){
14242                 this.abort();
14243             }
14244
14245             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14246
14247             var script = document.createElement("script");
14248             script.setAttribute("src", url);
14249             script.setAttribute("type", "text/javascript");
14250             script.setAttribute("id", trans.scriptId);
14251             this.head.appendChild(script);
14252
14253             this.trans = trans;
14254         }else{
14255             callback.call(scope||this, null, arg, false);
14256         }
14257     },
14258
14259     // private
14260     isLoading : function(){
14261         return this.trans ? true : false;
14262     },
14263
14264     /**
14265      * Abort the current server request.
14266      */
14267     abort : function(){
14268         if(this.isLoading()){
14269             this.destroyTrans(this.trans);
14270         }
14271     },
14272
14273     // private
14274     destroyTrans : function(trans, isLoaded){
14275         this.head.removeChild(document.getElementById(trans.scriptId));
14276         clearTimeout(trans.timeoutId);
14277         if(isLoaded){
14278             window[trans.cb] = undefined;
14279             try{
14280                 delete window[trans.cb];
14281             }catch(e){}
14282         }else{
14283             // if hasn't been loaded, wait for load to remove it to prevent script error
14284             window[trans.cb] = function(){
14285                 window[trans.cb] = undefined;
14286                 try{
14287                     delete window[trans.cb];
14288                 }catch(e){}
14289             };
14290         }
14291     },
14292
14293     // private
14294     handleResponse : function(o, trans){
14295         this.trans = false;
14296         this.destroyTrans(trans, true);
14297         var result;
14298         try {
14299             result = trans.reader.readRecords(o);
14300         }catch(e){
14301             this.fireEvent("loadexception", this, o, trans.arg, e);
14302             trans.callback.call(trans.scope||window, null, trans.arg, false);
14303             return;
14304         }
14305         this.fireEvent("load", this, o, trans.arg);
14306         trans.callback.call(trans.scope||window, result, trans.arg, true);
14307     },
14308
14309     // private
14310     handleFailure : function(trans){
14311         this.trans = false;
14312         this.destroyTrans(trans, false);
14313         this.fireEvent("loadexception", this, null, trans.arg);
14314         trans.callback.call(trans.scope||window, null, trans.arg, false);
14315     }
14316 });/*
14317  * Based on:
14318  * Ext JS Library 1.1.1
14319  * Copyright(c) 2006-2007, Ext JS, LLC.
14320  *
14321  * Originally Released Under LGPL - original licence link has changed is not relivant.
14322  *
14323  * Fork - LGPL
14324  * <script type="text/javascript">
14325  */
14326
14327 /**
14328  * @class Roo.data.JsonReader
14329  * @extends Roo.data.DataReader
14330  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14331  * based on mappings in a provided Roo.data.Record constructor.
14332  * 
14333  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14334  * in the reply previously. 
14335  * 
14336  * <p>
14337  * Example code:
14338  * <pre><code>
14339 var RecordDef = Roo.data.Record.create([
14340     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14341     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14342 ]);
14343 var myReader = new Roo.data.JsonReader({
14344     totalProperty: "results",    // The property which contains the total dataset size (optional)
14345     root: "rows",                // The property which contains an Array of row objects
14346     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14347 }, RecordDef);
14348 </code></pre>
14349  * <p>
14350  * This would consume a JSON file like this:
14351  * <pre><code>
14352 { 'results': 2, 'rows': [
14353     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14354     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14355 }
14356 </code></pre>
14357  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14358  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14359  * paged from the remote server.
14360  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14361  * @cfg {String} root name of the property which contains the Array of row objects.
14362  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14363  * @cfg {Array} fields Array of field definition objects
14364  * @constructor
14365  * Create a new JsonReader
14366  * @param {Object} meta Metadata configuration options
14367  * @param {Object} recordType Either an Array of field definition objects,
14368  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14369  */
14370 Roo.data.JsonReader = function(meta, recordType){
14371     
14372     meta = meta || {};
14373     // set some defaults:
14374     Roo.applyIf(meta, {
14375         totalProperty: 'total',
14376         successProperty : 'success',
14377         root : 'data',
14378         id : 'id'
14379     });
14380     
14381     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14382 };
14383 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14384     
14385     readerType : 'Json',
14386     
14387     /**
14388      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
14389      * Used by Store query builder to append _requestMeta to params.
14390      * 
14391      */
14392     metaFromRemote : false,
14393     /**
14394      * This method is only used by a DataProxy which has retrieved data from a remote server.
14395      * @param {Object} response The XHR object which contains the JSON data in its responseText.
14396      * @return {Object} data A data block which is used by an Roo.data.Store object as
14397      * a cache of Roo.data.Records.
14398      */
14399     read : function(response){
14400         var json = response.responseText;
14401        
14402         var o = /* eval:var:o */ eval("("+json+")");
14403         if(!o) {
14404             throw {message: "JsonReader.read: Json object not found"};
14405         }
14406         
14407         if(o.metaData){
14408             
14409             delete this.ef;
14410             this.metaFromRemote = true;
14411             this.meta = o.metaData;
14412             this.recordType = Roo.data.Record.create(o.metaData.fields);
14413             this.onMetaChange(this.meta, this.recordType, o);
14414         }
14415         return this.readRecords(o);
14416     },
14417
14418     // private function a store will implement
14419     onMetaChange : function(meta, recordType, o){
14420
14421     },
14422
14423     /**
14424          * @ignore
14425          */
14426     simpleAccess: function(obj, subsc) {
14427         return obj[subsc];
14428     },
14429
14430         /**
14431          * @ignore
14432          */
14433     getJsonAccessor: function(){
14434         var re = /[\[\.]/;
14435         return function(expr) {
14436             try {
14437                 return(re.test(expr))
14438                     ? new Function("obj", "return obj." + expr)
14439                     : function(obj){
14440                         return obj[expr];
14441                     };
14442             } catch(e){}
14443             return Roo.emptyFn;
14444         };
14445     }(),
14446
14447     /**
14448      * Create a data block containing Roo.data.Records from an XML document.
14449      * @param {Object} o An object which contains an Array of row objects in the property specified
14450      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14451      * which contains the total size of the dataset.
14452      * @return {Object} data A data block which is used by an Roo.data.Store object as
14453      * a cache of Roo.data.Records.
14454      */
14455     readRecords : function(o){
14456         /**
14457          * After any data loads, the raw JSON data is available for further custom processing.
14458          * @type Object
14459          */
14460         this.o = o;
14461         var s = this.meta, Record = this.recordType,
14462             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14463
14464 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
14465         if (!this.ef) {
14466             if(s.totalProperty) {
14467                     this.getTotal = this.getJsonAccessor(s.totalProperty);
14468                 }
14469                 if(s.successProperty) {
14470                     this.getSuccess = this.getJsonAccessor(s.successProperty);
14471                 }
14472                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14473                 if (s.id) {
14474                         var g = this.getJsonAccessor(s.id);
14475                         this.getId = function(rec) {
14476                                 var r = g(rec);  
14477                                 return (r === undefined || r === "") ? null : r;
14478                         };
14479                 } else {
14480                         this.getId = function(){return null;};
14481                 }
14482             this.ef = [];
14483             for(var jj = 0; jj < fl; jj++){
14484                 f = fi[jj];
14485                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14486                 this.ef[jj] = this.getJsonAccessor(map);
14487             }
14488         }
14489
14490         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14491         if(s.totalProperty){
14492             var vt = parseInt(this.getTotal(o), 10);
14493             if(!isNaN(vt)){
14494                 totalRecords = vt;
14495             }
14496         }
14497         if(s.successProperty){
14498             var vs = this.getSuccess(o);
14499             if(vs === false || vs === 'false'){
14500                 success = false;
14501             }
14502         }
14503         var records = [];
14504         for(var i = 0; i < c; i++){
14505                 var n = root[i];
14506             var values = {};
14507             var id = this.getId(n);
14508             for(var j = 0; j < fl; j++){
14509                 f = fi[j];
14510             var v = this.ef[j](n);
14511             if (!f.convert) {
14512                 Roo.log('missing convert for ' + f.name);
14513                 Roo.log(f);
14514                 continue;
14515             }
14516             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14517             }
14518             var record = new Record(values, id);
14519             record.json = n;
14520             records[i] = record;
14521         }
14522         return {
14523             raw : o,
14524             success : success,
14525             records : records,
14526             totalRecords : totalRecords
14527         };
14528     },
14529     // used when loading children.. @see loadDataFromChildren
14530     toLoadData: function(rec)
14531     {
14532         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14533         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14534         return { data : data, total : data.length };
14535         
14536     }
14537 });/*
14538  * Based on:
14539  * Ext JS Library 1.1.1
14540  * Copyright(c) 2006-2007, Ext JS, LLC.
14541  *
14542  * Originally Released Under LGPL - original licence link has changed is not relivant.
14543  *
14544  * Fork - LGPL
14545  * <script type="text/javascript">
14546  */
14547
14548 /**
14549  * @class Roo.data.ArrayReader
14550  * @extends Roo.data.DataReader
14551  * Data reader class to create an Array of Roo.data.Record objects from an Array.
14552  * Each element of that Array represents a row of data fields. The
14553  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14554  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14555  * <p>
14556  * Example code:.
14557  * <pre><code>
14558 var RecordDef = Roo.data.Record.create([
14559     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
14560     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
14561 ]);
14562 var myReader = new Roo.data.ArrayReader({
14563     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
14564 }, RecordDef);
14565 </code></pre>
14566  * <p>
14567  * This would consume an Array like this:
14568  * <pre><code>
14569 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14570   </code></pre>
14571  
14572  * @constructor
14573  * Create a new JsonReader
14574  * @param {Object} meta Metadata configuration options.
14575  * @param {Object|Array} recordType Either an Array of field definition objects
14576  * 
14577  * @cfg {Array} fields Array of field definition objects
14578  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14579  * as specified to {@link Roo.data.Record#create},
14580  * or an {@link Roo.data.Record} object
14581  *
14582  * 
14583  * created using {@link Roo.data.Record#create}.
14584  */
14585 Roo.data.ArrayReader = function(meta, recordType)
14586 {    
14587     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14588 };
14589
14590 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14591     
14592       /**
14593      * Create a data block containing Roo.data.Records from an XML document.
14594      * @param {Object} o An Array of row objects which represents the dataset.
14595      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14596      * a cache of Roo.data.Records.
14597      */
14598     readRecords : function(o)
14599     {
14600         var sid = this.meta ? this.meta.id : null;
14601         var recordType = this.recordType, fields = recordType.prototype.fields;
14602         var records = [];
14603         var root = o;
14604         for(var i = 0; i < root.length; i++){
14605                 var n = root[i];
14606             var values = {};
14607             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14608             for(var j = 0, jlen = fields.length; j < jlen; j++){
14609                 var f = fields.items[j];
14610                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14611                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14612                 v = f.convert(v);
14613                 values[f.name] = v;
14614             }
14615             var record = new recordType(values, id);
14616             record.json = n;
14617             records[records.length] = record;
14618         }
14619         return {
14620             records : records,
14621             totalRecords : records.length
14622         };
14623     },
14624     // used when loading children.. @see loadDataFromChildren
14625     toLoadData: function(rec)
14626     {
14627         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14628         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14629         
14630     }
14631     
14632     
14633 });/*
14634  * - LGPL
14635  * * 
14636  */
14637
14638 /**
14639  * @class Roo.bootstrap.ComboBox
14640  * @extends Roo.bootstrap.TriggerField
14641  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14642  * @cfg {Boolean} append (true|false) default false
14643  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14644  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14645  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14646  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14647  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14648  * @cfg {Boolean} animate default true
14649  * @cfg {Boolean} emptyResultText only for touch device
14650  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14651  * @cfg {String} emptyTitle default ''
14652  * @constructor
14653  * Create a new ComboBox.
14654  * @param {Object} config Configuration options
14655  */
14656 Roo.bootstrap.ComboBox = function(config){
14657     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14658     this.addEvents({
14659         /**
14660          * @event expand
14661          * Fires when the dropdown list is expanded
14662         * @param {Roo.bootstrap.ComboBox} combo This combo box
14663         */
14664         'expand' : true,
14665         /**
14666          * @event collapse
14667          * Fires when the dropdown list is collapsed
14668         * @param {Roo.bootstrap.ComboBox} combo This combo box
14669         */
14670         'collapse' : true,
14671         /**
14672          * @event beforeselect
14673          * Fires before a list item is selected. Return false to cancel the selection.
14674         * @param {Roo.bootstrap.ComboBox} combo This combo box
14675         * @param {Roo.data.Record} record The data record returned from the underlying store
14676         * @param {Number} index The index of the selected item in the dropdown list
14677         */
14678         'beforeselect' : true,
14679         /**
14680          * @event select
14681          * Fires when a list item is selected
14682         * @param {Roo.bootstrap.ComboBox} combo This combo box
14683         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14684         * @param {Number} index The index of the selected item in the dropdown list
14685         */
14686         'select' : true,
14687         /**
14688          * @event beforequery
14689          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14690          * The event object passed has these properties:
14691         * @param {Roo.bootstrap.ComboBox} combo This combo box
14692         * @param {String} query The query
14693         * @param {Boolean} forceAll true to force "all" query
14694         * @param {Boolean} cancel true to cancel the query
14695         * @param {Object} e The query event object
14696         */
14697         'beforequery': true,
14698          /**
14699          * @event add
14700          * Fires when the 'add' icon is pressed (add a listener to enable add button)
14701         * @param {Roo.bootstrap.ComboBox} combo This combo box
14702         */
14703         'add' : true,
14704         /**
14705          * @event edit
14706          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14707         * @param {Roo.bootstrap.ComboBox} combo This combo box
14708         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14709         */
14710         'edit' : true,
14711         /**
14712          * @event remove
14713          * Fires when the remove value from the combobox array
14714         * @param {Roo.bootstrap.ComboBox} combo This combo box
14715         */
14716         'remove' : true,
14717         /**
14718          * @event afterremove
14719          * Fires when the remove value from the combobox array
14720         * @param {Roo.bootstrap.ComboBox} combo This combo box
14721         */
14722         'afterremove' : true,
14723         /**
14724          * @event specialfilter
14725          * Fires when specialfilter
14726             * @param {Roo.bootstrap.ComboBox} combo This combo box
14727             */
14728         'specialfilter' : true,
14729         /**
14730          * @event tick
14731          * Fires when tick the element
14732             * @param {Roo.bootstrap.ComboBox} combo This combo box
14733             */
14734         'tick' : true,
14735         /**
14736          * @event touchviewdisplay
14737          * Fires when touch view require special display (default is using displayField)
14738             * @param {Roo.bootstrap.ComboBox} combo This combo box
14739             * @param {Object} cfg set html .
14740             */
14741         'touchviewdisplay' : true
14742         
14743     });
14744     
14745     this.item = [];
14746     this.tickItems = [];
14747     
14748     this.selectedIndex = -1;
14749     if(this.mode == 'local'){
14750         if(config.queryDelay === undefined){
14751             this.queryDelay = 10;
14752         }
14753         if(config.minChars === undefined){
14754             this.minChars = 0;
14755         }
14756     }
14757 };
14758
14759 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
14760      
14761     /**
14762      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
14763      * rendering into an Roo.Editor, defaults to false)
14764      */
14765     /**
14766      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
14767      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
14768      */
14769     /**
14770      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
14771      */
14772     /**
14773      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
14774      * the dropdown list (defaults to undefined, with no header element)
14775      */
14776
14777      /**
14778      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
14779      */
14780      
14781      /**
14782      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
14783      */
14784     listWidth: undefined,
14785     /**
14786      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
14787      * mode = 'remote' or 'text' if mode = 'local')
14788      */
14789     displayField: undefined,
14790     
14791     /**
14792      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
14793      * mode = 'remote' or 'value' if mode = 'local'). 
14794      * Note: use of a valueField requires the user make a selection
14795      * in order for a value to be mapped.
14796      */
14797     valueField: undefined,
14798     /**
14799      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
14800      */
14801     modalTitle : '',
14802     
14803     /**
14804      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
14805      * field's data value (defaults to the underlying DOM element's name)
14806      */
14807     hiddenName: undefined,
14808     /**
14809      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
14810      */
14811     listClass: '',
14812     /**
14813      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
14814      */
14815     selectedClass: 'active',
14816     
14817     /**
14818      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14819      */
14820     shadow:'sides',
14821     /**
14822      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
14823      * anchor positions (defaults to 'tl-bl')
14824      */
14825     listAlign: 'tl-bl?',
14826     /**
14827      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
14828      */
14829     maxHeight: 300,
14830     /**
14831      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
14832      * query specified by the allQuery config option (defaults to 'query')
14833      */
14834     triggerAction: 'query',
14835     /**
14836      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
14837      * (defaults to 4, does not apply if editable = false)
14838      */
14839     minChars : 4,
14840     /**
14841      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
14842      * delay (typeAheadDelay) if it matches a known value (defaults to false)
14843      */
14844     typeAhead: false,
14845     /**
14846      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
14847      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
14848      */
14849     queryDelay: 500,
14850     /**
14851      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
14852      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
14853      */
14854     pageSize: 0,
14855     /**
14856      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
14857      * when editable = true (defaults to false)
14858      */
14859     selectOnFocus:false,
14860     /**
14861      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
14862      */
14863     queryParam: 'query',
14864     /**
14865      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
14866      * when mode = 'remote' (defaults to 'Loading...')
14867      */
14868     loadingText: 'Loading...',
14869     /**
14870      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
14871      */
14872     resizable: false,
14873     /**
14874      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
14875      */
14876     handleHeight : 8,
14877     /**
14878      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
14879      * traditional select (defaults to true)
14880      */
14881     editable: true,
14882     /**
14883      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
14884      */
14885     allQuery: '',
14886     /**
14887      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
14888      */
14889     mode: 'remote',
14890     /**
14891      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
14892      * listWidth has a higher value)
14893      */
14894     minListWidth : 70,
14895     /**
14896      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
14897      * allow the user to set arbitrary text into the field (defaults to false)
14898      */
14899     forceSelection:false,
14900     /**
14901      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
14902      * if typeAhead = true (defaults to 250)
14903      */
14904     typeAheadDelay : 250,
14905     /**
14906      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
14907      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
14908      */
14909     valueNotFoundText : undefined,
14910     /**
14911      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
14912      */
14913     blockFocus : false,
14914     
14915     /**
14916      * @cfg {Boolean} disableClear Disable showing of clear button.
14917      */
14918     disableClear : false,
14919     /**
14920      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
14921      */
14922     alwaysQuery : false,
14923     
14924     /**
14925      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
14926      */
14927     multiple : false,
14928     
14929     /**
14930      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
14931      */
14932     invalidClass : "has-warning",
14933     
14934     /**
14935      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
14936      */
14937     validClass : "has-success",
14938     
14939     /**
14940      * @cfg {Boolean} specialFilter (true|false) special filter default false
14941      */
14942     specialFilter : false,
14943     
14944     /**
14945      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
14946      */
14947     mobileTouchView : true,
14948     
14949     /**
14950      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
14951      */
14952     useNativeIOS : false,
14953     
14954     /**
14955      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
14956      */
14957     mobile_restrict_height : false,
14958     
14959     ios_options : false,
14960     
14961     //private
14962     addicon : false,
14963     editicon: false,
14964     
14965     page: 0,
14966     hasQuery: false,
14967     append: false,
14968     loadNext: false,
14969     autoFocus : true,
14970     tickable : false,
14971     btnPosition : 'right',
14972     triggerList : true,
14973     showToggleBtn : true,
14974     animate : true,
14975     emptyResultText: 'Empty',
14976     triggerText : 'Select',
14977     emptyTitle : '',
14978     
14979     // element that contains real text value.. (when hidden is used..)
14980     
14981     getAutoCreate : function()
14982     {   
14983         var cfg = false;
14984         //render
14985         /*
14986          * Render classic select for iso
14987          */
14988         
14989         if(Roo.isIOS && this.useNativeIOS){
14990             cfg = this.getAutoCreateNativeIOS();
14991             return cfg;
14992         }
14993         
14994         /*
14995          * Touch Devices
14996          */
14997         
14998         if(Roo.isTouch && this.mobileTouchView){
14999             cfg = this.getAutoCreateTouchView();
15000             return cfg;;
15001         }
15002         
15003         /*
15004          *  Normal ComboBox
15005          */
15006         if(!this.tickable){
15007             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15008             return cfg;
15009         }
15010         
15011         /*
15012          *  ComboBox with tickable selections
15013          */
15014              
15015         var align = this.labelAlign || this.parentLabelAlign();
15016         
15017         cfg = {
15018             cls : 'form-group roo-combobox-tickable' //input-group
15019         };
15020         
15021         var btn_text_select = '';
15022         var btn_text_done = '';
15023         var btn_text_cancel = '';
15024         
15025         if (this.btn_text_show) {
15026             btn_text_select = 'Select';
15027             btn_text_done = 'Done';
15028             btn_text_cancel = 'Cancel'; 
15029         }
15030         
15031         var buttons = {
15032             tag : 'div',
15033             cls : 'tickable-buttons',
15034             cn : [
15035                 {
15036                     tag : 'button',
15037                     type : 'button',
15038                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15039                     //html : this.triggerText
15040                     html: btn_text_select
15041                 },
15042                 {
15043                     tag : 'button',
15044                     type : 'button',
15045                     name : 'ok',
15046                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15047                     //html : 'Done'
15048                     html: btn_text_done
15049                 },
15050                 {
15051                     tag : 'button',
15052                     type : 'button',
15053                     name : 'cancel',
15054                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15055                     //html : 'Cancel'
15056                     html: btn_text_cancel
15057                 }
15058             ]
15059         };
15060         
15061         if(this.editable){
15062             buttons.cn.unshift({
15063                 tag: 'input',
15064                 cls: 'roo-select2-search-field-input'
15065             });
15066         }
15067         
15068         var _this = this;
15069         
15070         Roo.each(buttons.cn, function(c){
15071             if (_this.size) {
15072                 c.cls += ' btn-' + _this.size;
15073             }
15074
15075             if (_this.disabled) {
15076                 c.disabled = true;
15077             }
15078         });
15079         
15080         var box = {
15081             tag: 'div',
15082             style : 'display: contents',
15083             cn: [
15084                 {
15085                     tag: 'input',
15086                     type : 'hidden',
15087                     cls: 'form-hidden-field'
15088                 },
15089                 {
15090                     tag: 'ul',
15091                     cls: 'roo-select2-choices',
15092                     cn:[
15093                         {
15094                             tag: 'li',
15095                             cls: 'roo-select2-search-field',
15096                             cn: [
15097                                 buttons
15098                             ]
15099                         }
15100                     ]
15101                 }
15102             ]
15103         };
15104         
15105         var combobox = {
15106             cls: 'roo-select2-container input-group roo-select2-container-multi',
15107             cn: [
15108                 
15109                 box
15110 //                {
15111 //                    tag: 'ul',
15112 //                    cls: 'typeahead typeahead-long dropdown-menu',
15113 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15114 //                }
15115             ]
15116         };
15117         
15118         if(this.hasFeedback && !this.allowBlank){
15119             
15120             var feedback = {
15121                 tag: 'span',
15122                 cls: 'glyphicon form-control-feedback'
15123             };
15124
15125             combobox.cn.push(feedback);
15126         }
15127         
15128         
15129         
15130         var indicator = {
15131             tag : 'i',
15132             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15133             tooltip : 'This field is required'
15134         };
15135         if (Roo.bootstrap.version == 4) {
15136             indicator = {
15137                 tag : 'i',
15138                 style : 'display:none'
15139             };
15140         }
15141         if (align ==='left' && this.fieldLabel.length) {
15142             
15143             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15144             
15145             cfg.cn = [
15146                 indicator,
15147                 {
15148                     tag: 'label',
15149                     'for' :  id,
15150                     cls : 'control-label col-form-label',
15151                     html : this.fieldLabel
15152
15153                 },
15154                 {
15155                     cls : "", 
15156                     cn: [
15157                         combobox
15158                     ]
15159                 }
15160
15161             ];
15162             
15163             var labelCfg = cfg.cn[1];
15164             var contentCfg = cfg.cn[2];
15165             
15166
15167             if(this.indicatorpos == 'right'){
15168                 
15169                 cfg.cn = [
15170                     {
15171                         tag: 'label',
15172                         'for' :  id,
15173                         cls : 'control-label col-form-label',
15174                         cn : [
15175                             {
15176                                 tag : 'span',
15177                                 html : this.fieldLabel
15178                             },
15179                             indicator
15180                         ]
15181                     },
15182                     {
15183                         cls : "",
15184                         cn: [
15185                             combobox
15186                         ]
15187                     }
15188
15189                 ];
15190                 
15191                 
15192                 
15193                 labelCfg = cfg.cn[0];
15194                 contentCfg = cfg.cn[1];
15195             
15196             }
15197             
15198             if(this.labelWidth > 12){
15199                 labelCfg.style = "width: " + this.labelWidth + 'px';
15200             }
15201             
15202             if(this.labelWidth < 13 && this.labelmd == 0){
15203                 this.labelmd = this.labelWidth;
15204             }
15205             
15206             if(this.labellg > 0){
15207                 labelCfg.cls += ' col-lg-' + this.labellg;
15208                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15209             }
15210             
15211             if(this.labelmd > 0){
15212                 labelCfg.cls += ' col-md-' + this.labelmd;
15213                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15214             }
15215             
15216             if(this.labelsm > 0){
15217                 labelCfg.cls += ' col-sm-' + this.labelsm;
15218                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15219             }
15220             
15221             if(this.labelxs > 0){
15222                 labelCfg.cls += ' col-xs-' + this.labelxs;
15223                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15224             }
15225                 
15226                 
15227         } else if ( this.fieldLabel.length) {
15228 //                Roo.log(" label");
15229                  cfg.cn = [
15230                    indicator,
15231                     {
15232                         tag: 'label',
15233                         //cls : 'input-group-addon',
15234                         html : this.fieldLabel
15235                     },
15236                     combobox
15237                 ];
15238                 
15239                 if(this.indicatorpos == 'right'){
15240                     cfg.cn = [
15241                         {
15242                             tag: 'label',
15243                             //cls : 'input-group-addon',
15244                             html : this.fieldLabel
15245                         },
15246                         indicator,
15247                         combobox
15248                     ];
15249                     
15250                 }
15251
15252         } else {
15253             
15254 //                Roo.log(" no label && no align");
15255                 cfg = combobox
15256                      
15257                 
15258         }
15259          
15260         var settings=this;
15261         ['xs','sm','md','lg'].map(function(size){
15262             if (settings[size]) {
15263                 cfg.cls += ' col-' + size + '-' + settings[size];
15264             }
15265         });
15266         
15267         return cfg;
15268         
15269     },
15270     
15271     _initEventsCalled : false,
15272     
15273     // private
15274     initEvents: function()
15275     {   
15276         if (this._initEventsCalled) { // as we call render... prevent looping...
15277             return;
15278         }
15279         this._initEventsCalled = true;
15280         
15281         if (!this.store) {
15282             throw "can not find store for combo";
15283         }
15284         
15285         this.indicator = this.indicatorEl();
15286         
15287         this.store = Roo.factory(this.store, Roo.data);
15288         this.store.parent = this;
15289         
15290         // if we are building from html. then this element is so complex, that we can not really
15291         // use the rendered HTML.
15292         // so we have to trash and replace the previous code.
15293         if (Roo.XComponent.build_from_html) {
15294             // remove this element....
15295             var e = this.el.dom, k=0;
15296             while (e ) { e = e.previousSibling;  ++k;}
15297
15298             this.el.remove();
15299             
15300             this.el=false;
15301             this.rendered = false;
15302             
15303             this.render(this.parent().getChildContainer(true), k);
15304         }
15305         
15306         if(Roo.isIOS && this.useNativeIOS){
15307             this.initIOSView();
15308             return;
15309         }
15310         
15311         /*
15312          * Touch Devices
15313          */
15314         
15315         if(Roo.isTouch && this.mobileTouchView){
15316             this.initTouchView();
15317             return;
15318         }
15319         
15320         if(this.tickable){
15321             this.initTickableEvents();
15322             return;
15323         }
15324         
15325         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15326         
15327         if(this.hiddenName){
15328             
15329             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15330             
15331             this.hiddenField.dom.value =
15332                 this.hiddenValue !== undefined ? this.hiddenValue :
15333                 this.value !== undefined ? this.value : '';
15334
15335             // prevent input submission
15336             this.el.dom.removeAttribute('name');
15337             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15338              
15339              
15340         }
15341         //if(Roo.isGecko){
15342         //    this.el.dom.setAttribute('autocomplete', 'off');
15343         //}
15344         
15345         var cls = 'x-combo-list';
15346         
15347         //this.list = new Roo.Layer({
15348         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15349         //});
15350         
15351         var _this = this;
15352         
15353         (function(){
15354             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15355             _this.list.setWidth(lw);
15356         }).defer(100);
15357         
15358         this.list.on('mouseover', this.onViewOver, this);
15359         this.list.on('mousemove', this.onViewMove, this);
15360         this.list.on('scroll', this.onViewScroll, this);
15361         
15362         /*
15363         this.list.swallowEvent('mousewheel');
15364         this.assetHeight = 0;
15365
15366         if(this.title){
15367             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15368             this.assetHeight += this.header.getHeight();
15369         }
15370
15371         this.innerList = this.list.createChild({cls:cls+'-inner'});
15372         this.innerList.on('mouseover', this.onViewOver, this);
15373         this.innerList.on('mousemove', this.onViewMove, this);
15374         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15375         
15376         if(this.allowBlank && !this.pageSize && !this.disableClear){
15377             this.footer = this.list.createChild({cls:cls+'-ft'});
15378             this.pageTb = new Roo.Toolbar(this.footer);
15379            
15380         }
15381         if(this.pageSize){
15382             this.footer = this.list.createChild({cls:cls+'-ft'});
15383             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15384                     {pageSize: this.pageSize});
15385             
15386         }
15387         
15388         if (this.pageTb && this.allowBlank && !this.disableClear) {
15389             var _this = this;
15390             this.pageTb.add(new Roo.Toolbar.Fill(), {
15391                 cls: 'x-btn-icon x-btn-clear',
15392                 text: '&#160;',
15393                 handler: function()
15394                 {
15395                     _this.collapse();
15396                     _this.clearValue();
15397                     _this.onSelect(false, -1);
15398                 }
15399             });
15400         }
15401         if (this.footer) {
15402             this.assetHeight += this.footer.getHeight();
15403         }
15404         */
15405             
15406         if(!this.tpl){
15407             this.tpl = Roo.bootstrap.version == 4 ?
15408                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
15409                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15410         }
15411
15412         this.view = new Roo.View(this.list, this.tpl, {
15413             singleSelect:true, store: this.store, selectedClass: this.selectedClass
15414         });
15415         //this.view.wrapEl.setDisplayed(false);
15416         this.view.on('click', this.onViewClick, this);
15417         
15418         
15419         this.store.on('beforeload', this.onBeforeLoad, this);
15420         this.store.on('load', this.onLoad, this);
15421         this.store.on('loadexception', this.onLoadException, this);
15422         /*
15423         if(this.resizable){
15424             this.resizer = new Roo.Resizable(this.list,  {
15425                pinned:true, handles:'se'
15426             });
15427             this.resizer.on('resize', function(r, w, h){
15428                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15429                 this.listWidth = w;
15430                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15431                 this.restrictHeight();
15432             }, this);
15433             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15434         }
15435         */
15436         if(!this.editable){
15437             this.editable = true;
15438             this.setEditable(false);
15439         }
15440         
15441         /*
15442         
15443         if (typeof(this.events.add.listeners) != 'undefined') {
15444             
15445             this.addicon = this.wrap.createChild(
15446                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
15447        
15448             this.addicon.on('click', function(e) {
15449                 this.fireEvent('add', this);
15450             }, this);
15451         }
15452         if (typeof(this.events.edit.listeners) != 'undefined') {
15453             
15454             this.editicon = this.wrap.createChild(
15455                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
15456             if (this.addicon) {
15457                 this.editicon.setStyle('margin-left', '40px');
15458             }
15459             this.editicon.on('click', function(e) {
15460                 
15461                 // we fire even  if inothing is selected..
15462                 this.fireEvent('edit', this, this.lastData );
15463                 
15464             }, this);
15465         }
15466         */
15467         
15468         this.keyNav = new Roo.KeyNav(this.inputEl(), {
15469             "up" : function(e){
15470                 this.inKeyMode = true;
15471                 this.selectPrev();
15472             },
15473
15474             "down" : function(e){
15475                 if(!this.isExpanded()){
15476                     this.onTriggerClick();
15477                 }else{
15478                     this.inKeyMode = true;
15479                     this.selectNext();
15480                 }
15481             },
15482
15483             "enter" : function(e){
15484 //                this.onViewClick();
15485                 //return true;
15486                 this.collapse();
15487                 
15488                 if(this.fireEvent("specialkey", this, e)){
15489                     this.onViewClick(false);
15490                 }
15491                 
15492                 return true;
15493             },
15494
15495             "esc" : function(e){
15496                 this.collapse();
15497             },
15498
15499             "tab" : function(e){
15500                 this.collapse();
15501                 
15502                 if(this.fireEvent("specialkey", this, e)){
15503                     this.onViewClick(false);
15504                 }
15505                 
15506                 return true;
15507             },
15508
15509             scope : this,
15510
15511             doRelay : function(foo, bar, hname){
15512                 if(hname == 'down' || this.scope.isExpanded()){
15513                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15514                 }
15515                 return true;
15516             },
15517
15518             forceKeyDown: true
15519         });
15520         
15521         
15522         this.queryDelay = Math.max(this.queryDelay || 10,
15523                 this.mode == 'local' ? 10 : 250);
15524         
15525         
15526         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15527         
15528         if(this.typeAhead){
15529             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15530         }
15531         if(this.editable !== false){
15532             this.inputEl().on("keyup", this.onKeyUp, this);
15533         }
15534         if(this.forceSelection){
15535             this.inputEl().on('blur', this.doForce, this);
15536         }
15537         
15538         if(this.multiple){
15539             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15540             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15541         }
15542     },
15543     
15544     initTickableEvents: function()
15545     {   
15546         this.createList();
15547         
15548         if(this.hiddenName){
15549             
15550             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15551             
15552             this.hiddenField.dom.value =
15553                 this.hiddenValue !== undefined ? this.hiddenValue :
15554                 this.value !== undefined ? this.value : '';
15555
15556             // prevent input submission
15557             this.el.dom.removeAttribute('name');
15558             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15559              
15560              
15561         }
15562         
15563 //        this.list = this.el.select('ul.dropdown-menu',true).first();
15564         
15565         this.choices = this.el.select('ul.roo-select2-choices', true).first();
15566         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15567         if(this.triggerList){
15568             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15569         }
15570          
15571         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15572         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15573         
15574         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15575         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15576         
15577         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15578         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15579         
15580         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15581         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15582         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15583         
15584         this.okBtn.hide();
15585         this.cancelBtn.hide();
15586         
15587         var _this = this;
15588         
15589         (function(){
15590             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15591             _this.list.setWidth(lw);
15592         }).defer(100);
15593         
15594         this.list.on('mouseover', this.onViewOver, this);
15595         this.list.on('mousemove', this.onViewMove, this);
15596         
15597         this.list.on('scroll', this.onViewScroll, this);
15598         
15599         if(!this.tpl){
15600             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
15601                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15602         }
15603
15604         this.view = new Roo.View(this.list, this.tpl, {
15605             singleSelect:true,
15606             tickable:true,
15607             parent:this,
15608             store: this.store,
15609             selectedClass: this.selectedClass
15610         });
15611         
15612         //this.view.wrapEl.setDisplayed(false);
15613         this.view.on('click', this.onViewClick, this);
15614         
15615         
15616         
15617         this.store.on('beforeload', this.onBeforeLoad, this);
15618         this.store.on('load', this.onLoad, this);
15619         this.store.on('loadexception', this.onLoadException, this);
15620         
15621         if(this.editable){
15622             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15623                 "up" : function(e){
15624                     this.inKeyMode = true;
15625                     this.selectPrev();
15626                 },
15627
15628                 "down" : function(e){
15629                     this.inKeyMode = true;
15630                     this.selectNext();
15631                 },
15632
15633                 "enter" : function(e){
15634                     if(this.fireEvent("specialkey", this, e)){
15635                         this.onViewClick(false);
15636                     }
15637                     
15638                     return true;
15639                 },
15640
15641                 "esc" : function(e){
15642                     this.onTickableFooterButtonClick(e, false, false);
15643                 },
15644
15645                 "tab" : function(e){
15646                     this.fireEvent("specialkey", this, e);
15647                     
15648                     this.onTickableFooterButtonClick(e, false, false);
15649                     
15650                     return true;
15651                 },
15652
15653                 scope : this,
15654
15655                 doRelay : function(e, fn, key){
15656                     if(this.scope.isExpanded()){
15657                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15658                     }
15659                     return true;
15660                 },
15661
15662                 forceKeyDown: true
15663             });
15664         }
15665         
15666         this.queryDelay = Math.max(this.queryDelay || 10,
15667                 this.mode == 'local' ? 10 : 250);
15668         
15669         
15670         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15671         
15672         if(this.typeAhead){
15673             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15674         }
15675         
15676         if(this.editable !== false){
15677             this.tickableInputEl().on("keyup", this.onKeyUp, this);
15678         }
15679         
15680         this.indicator = this.indicatorEl();
15681         
15682         if(this.indicator){
15683             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15684             this.indicator.hide();
15685         }
15686         
15687     },
15688
15689     onDestroy : function(){
15690         if(this.view){
15691             this.view.setStore(null);
15692             this.view.el.removeAllListeners();
15693             this.view.el.remove();
15694             this.view.purgeListeners();
15695         }
15696         if(this.list){
15697             this.list.dom.innerHTML  = '';
15698         }
15699         
15700         if(this.store){
15701             this.store.un('beforeload', this.onBeforeLoad, this);
15702             this.store.un('load', this.onLoad, this);
15703             this.store.un('loadexception', this.onLoadException, this);
15704         }
15705         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15706     },
15707
15708     // private
15709     fireKey : function(e){
15710         if(e.isNavKeyPress() && !this.list.isVisible()){
15711             this.fireEvent("specialkey", this, e);
15712         }
15713     },
15714
15715     // private
15716     onResize: function(w, h){
15717 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
15718 //        
15719 //        if(typeof w != 'number'){
15720 //            // we do not handle it!?!?
15721 //            return;
15722 //        }
15723 //        var tw = this.trigger.getWidth();
15724 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
15725 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
15726 //        var x = w - tw;
15727 //        this.inputEl().setWidth( this.adjustWidth('input', x));
15728 //            
15729 //        //this.trigger.setStyle('left', x+'px');
15730 //        
15731 //        if(this.list && this.listWidth === undefined){
15732 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
15733 //            this.list.setWidth(lw);
15734 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15735 //        }
15736         
15737     
15738         
15739     },
15740
15741     /**
15742      * Allow or prevent the user from directly editing the field text.  If false is passed,
15743      * the user will only be able to select from the items defined in the dropdown list.  This method
15744      * is the runtime equivalent of setting the 'editable' config option at config time.
15745      * @param {Boolean} value True to allow the user to directly edit the field text
15746      */
15747     setEditable : function(value){
15748         if(value == this.editable){
15749             return;
15750         }
15751         this.editable = value;
15752         if(!value){
15753             this.inputEl().dom.setAttribute('readOnly', true);
15754             this.inputEl().on('mousedown', this.onTriggerClick,  this);
15755             this.inputEl().addClass('x-combo-noedit');
15756         }else{
15757             this.inputEl().dom.setAttribute('readOnly', false);
15758             this.inputEl().un('mousedown', this.onTriggerClick,  this);
15759             this.inputEl().removeClass('x-combo-noedit');
15760         }
15761     },
15762
15763     // private
15764     
15765     onBeforeLoad : function(combo,opts){
15766         if(!this.hasFocus){
15767             return;
15768         }
15769          if (!opts.add) {
15770             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
15771          }
15772         this.restrictHeight();
15773         this.selectedIndex = -1;
15774     },
15775
15776     // private
15777     onLoad : function(){
15778         
15779         this.hasQuery = false;
15780         
15781         if(!this.hasFocus){
15782             return;
15783         }
15784         
15785         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
15786             this.loading.hide();
15787         }
15788         
15789         if(this.store.getCount() > 0){
15790             
15791             this.expand();
15792             this.restrictHeight();
15793             if(this.lastQuery == this.allQuery){
15794                 if(this.editable && !this.tickable){
15795                     this.inputEl().dom.select();
15796                 }
15797                 
15798                 if(
15799                     !this.selectByValue(this.value, true) &&
15800                     this.autoFocus && 
15801                     (
15802                         !this.store.lastOptions ||
15803                         typeof(this.store.lastOptions.add) == 'undefined' || 
15804                         this.store.lastOptions.add != true
15805                     )
15806                 ){
15807                     this.select(0, true);
15808                 }
15809             }else{
15810                 if(this.autoFocus){
15811                     this.selectNext();
15812                 }
15813                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
15814                     this.taTask.delay(this.typeAheadDelay);
15815                 }
15816             }
15817         }else{
15818             this.onEmptyResults();
15819         }
15820         
15821         //this.el.focus();
15822     },
15823     // private
15824     onLoadException : function()
15825     {
15826         this.hasQuery = false;
15827         
15828         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
15829             this.loading.hide();
15830         }
15831         
15832         if(this.tickable && this.editable){
15833             return;
15834         }
15835         
15836         this.collapse();
15837         // only causes errors at present
15838         //Roo.log(this.store.reader.jsonData);
15839         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
15840             // fixme
15841             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
15842         //}
15843         
15844         
15845     },
15846     // private
15847     onTypeAhead : function(){
15848         if(this.store.getCount() > 0){
15849             var r = this.store.getAt(0);
15850             var newValue = r.data[this.displayField];
15851             var len = newValue.length;
15852             var selStart = this.getRawValue().length;
15853             
15854             if(selStart != len){
15855                 this.setRawValue(newValue);
15856                 this.selectText(selStart, newValue.length);
15857             }
15858         }
15859     },
15860
15861     // private
15862     onSelect : function(record, index){
15863         
15864         if(this.fireEvent('beforeselect', this, record, index) !== false){
15865         
15866             this.setFromData(index > -1 ? record.data : false);
15867             
15868             this.collapse();
15869             this.fireEvent('select', this, record, index);
15870         }
15871     },
15872
15873     /**
15874      * Returns the currently selected field value or empty string if no value is set.
15875      * @return {String} value The selected value
15876      */
15877     getValue : function()
15878     {
15879         if(Roo.isIOS && this.useNativeIOS){
15880             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
15881         }
15882         
15883         if(this.multiple){
15884             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
15885         }
15886         
15887         if(this.valueField){
15888             return typeof this.value != 'undefined' ? this.value : '';
15889         }else{
15890             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
15891         }
15892     },
15893     
15894     getRawValue : function()
15895     {
15896         if(Roo.isIOS && this.useNativeIOS){
15897             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
15898         }
15899         
15900         var v = this.inputEl().getValue();
15901         
15902         return v;
15903     },
15904
15905     /**
15906      * Clears any text/value currently set in the field
15907      */
15908     clearValue : function(){
15909         
15910         if(this.hiddenField){
15911             this.hiddenField.dom.value = '';
15912         }
15913         this.value = '';
15914         this.setRawValue('');
15915         this.lastSelectionText = '';
15916         this.lastData = false;
15917         
15918         var close = this.closeTriggerEl();
15919         
15920         if(close){
15921             close.hide();
15922         }
15923         
15924         this.validate();
15925         
15926     },
15927
15928     /**
15929      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
15930      * will be displayed in the field.  If the value does not match the data value of an existing item,
15931      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
15932      * Otherwise the field will be blank (although the value will still be set).
15933      * @param {String} value The value to match
15934      */
15935     setValue : function(v)
15936     {
15937         if(Roo.isIOS && this.useNativeIOS){
15938             this.setIOSValue(v);
15939             return;
15940         }
15941         
15942         if(this.multiple){
15943             this.syncValue();
15944             return;
15945         }
15946         
15947         var text = v;
15948         if(this.valueField){
15949             var r = this.findRecord(this.valueField, v);
15950             if(r){
15951                 text = r.data[this.displayField];
15952             }else if(this.valueNotFoundText !== undefined){
15953                 text = this.valueNotFoundText;
15954             }
15955         }
15956         this.lastSelectionText = text;
15957         if(this.hiddenField){
15958             this.hiddenField.dom.value = v;
15959         }
15960         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
15961         this.value = v;
15962         
15963         var close = this.closeTriggerEl();
15964         
15965         if(close){
15966             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
15967         }
15968         
15969         this.validate();
15970     },
15971     /**
15972      * @property {Object} the last set data for the element
15973      */
15974     
15975     lastData : false,
15976     /**
15977      * Sets the value of the field based on a object which is related to the record format for the store.
15978      * @param {Object} value the value to set as. or false on reset?
15979      */
15980     setFromData : function(o){
15981         
15982         if(this.multiple){
15983             this.addItem(o);
15984             return;
15985         }
15986             
15987         var dv = ''; // display value
15988         var vv = ''; // value value..
15989         this.lastData = o;
15990         if (this.displayField) {
15991             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
15992         } else {
15993             // this is an error condition!!!
15994             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
15995         }
15996         
15997         if(this.valueField){
15998             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
15999         }
16000         
16001         var close = this.closeTriggerEl();
16002         
16003         if(close){
16004             if(dv.length || vv * 1 > 0){
16005                 close.show() ;
16006                 this.blockFocus=true;
16007             } else {
16008                 close.hide();
16009             }             
16010         }
16011         
16012         if(this.hiddenField){
16013             this.hiddenField.dom.value = vv;
16014             
16015             this.lastSelectionText = dv;
16016             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16017             this.value = vv;
16018             return;
16019         }
16020         // no hidden field.. - we store the value in 'value', but still display
16021         // display field!!!!
16022         this.lastSelectionText = dv;
16023         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16024         this.value = vv;
16025         
16026         
16027         
16028     },
16029     // private
16030     reset : function(){
16031         // overridden so that last data is reset..
16032         
16033         if(this.multiple){
16034             this.clearItem();
16035             return;
16036         }
16037         
16038         this.setValue(this.originalValue);
16039         //this.clearInvalid();
16040         this.lastData = false;
16041         if (this.view) {
16042             this.view.clearSelections();
16043         }
16044         
16045         this.validate();
16046     },
16047     // private
16048     findRecord : function(prop, value){
16049         var record;
16050         if(this.store.getCount() > 0){
16051             this.store.each(function(r){
16052                 if(r.data[prop] == value){
16053                     record = r;
16054                     return false;
16055                 }
16056                 return true;
16057             });
16058         }
16059         return record;
16060     },
16061     
16062     getName: function()
16063     {
16064         // returns hidden if it's set..
16065         if (!this.rendered) {return ''};
16066         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16067         
16068     },
16069     // private
16070     onViewMove : function(e, t){
16071         this.inKeyMode = false;
16072     },
16073
16074     // private
16075     onViewOver : function(e, t){
16076         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16077             return;
16078         }
16079         var item = this.view.findItemFromChild(t);
16080         
16081         if(item){
16082             var index = this.view.indexOf(item);
16083             this.select(index, false);
16084         }
16085     },
16086
16087     // private
16088     onViewClick : function(view, doFocus, el, e)
16089     {
16090         var index = this.view.getSelectedIndexes()[0];
16091         
16092         var r = this.store.getAt(index);
16093         
16094         if(this.tickable){
16095             
16096             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16097                 return;
16098             }
16099             
16100             var rm = false;
16101             var _this = this;
16102             
16103             Roo.each(this.tickItems, function(v,k){
16104                 
16105                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16106                     Roo.log(v);
16107                     _this.tickItems.splice(k, 1);
16108                     
16109                     if(typeof(e) == 'undefined' && view == false){
16110                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16111                     }
16112                     
16113                     rm = true;
16114                     return;
16115                 }
16116             });
16117             
16118             if(rm){
16119                 return;
16120             }
16121             
16122             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16123                 this.tickItems.push(r.data);
16124             }
16125             
16126             if(typeof(e) == 'undefined' && view == false){
16127                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16128             }
16129                     
16130             return;
16131         }
16132         
16133         if(r){
16134             this.onSelect(r, index);
16135         }
16136         if(doFocus !== false && !this.blockFocus){
16137             this.inputEl().focus();
16138         }
16139     },
16140
16141     // private
16142     restrictHeight : function(){
16143         //this.innerList.dom.style.height = '';
16144         //var inner = this.innerList.dom;
16145         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16146         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16147         //this.list.beginUpdate();
16148         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16149         this.list.alignTo(this.inputEl(), this.listAlign);
16150         this.list.alignTo(this.inputEl(), this.listAlign);
16151         //this.list.endUpdate();
16152     },
16153
16154     // private
16155     onEmptyResults : function(){
16156         
16157         if(this.tickable && this.editable){
16158             this.hasFocus = false;
16159             this.restrictHeight();
16160             return;
16161         }
16162         
16163         this.collapse();
16164     },
16165
16166     /**
16167      * Returns true if the dropdown list is expanded, else false.
16168      */
16169     isExpanded : function(){
16170         return this.list.isVisible();
16171     },
16172
16173     /**
16174      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16175      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16176      * @param {String} value The data value of the item to select
16177      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16178      * selected item if it is not currently in view (defaults to true)
16179      * @return {Boolean} True if the value matched an item in the list, else false
16180      */
16181     selectByValue : function(v, scrollIntoView){
16182         if(v !== undefined && v !== null){
16183             var r = this.findRecord(this.valueField || this.displayField, v);
16184             if(r){
16185                 this.select(this.store.indexOf(r), scrollIntoView);
16186                 return true;
16187             }
16188         }
16189         return false;
16190     },
16191
16192     /**
16193      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16194      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16195      * @param {Number} index The zero-based index of the list item to select
16196      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16197      * selected item if it is not currently in view (defaults to true)
16198      */
16199     select : function(index, scrollIntoView){
16200         this.selectedIndex = index;
16201         this.view.select(index);
16202         if(scrollIntoView !== false){
16203             var el = this.view.getNode(index);
16204             /*
16205              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16206              */
16207             if(el){
16208                 this.list.scrollChildIntoView(el, false);
16209             }
16210         }
16211     },
16212
16213     // private
16214     selectNext : function(){
16215         var ct = this.store.getCount();
16216         if(ct > 0){
16217             if(this.selectedIndex == -1){
16218                 this.select(0);
16219             }else if(this.selectedIndex < ct-1){
16220                 this.select(this.selectedIndex+1);
16221             }
16222         }
16223     },
16224
16225     // private
16226     selectPrev : function(){
16227         var ct = this.store.getCount();
16228         if(ct > 0){
16229             if(this.selectedIndex == -1){
16230                 this.select(0);
16231             }else if(this.selectedIndex != 0){
16232                 this.select(this.selectedIndex-1);
16233             }
16234         }
16235     },
16236
16237     // private
16238     onKeyUp : function(e){
16239         if(this.editable !== false && !e.isSpecialKey()){
16240             this.lastKey = e.getKey();
16241             this.dqTask.delay(this.queryDelay);
16242         }
16243     },
16244
16245     // private
16246     validateBlur : function(){
16247         return !this.list || !this.list.isVisible();   
16248     },
16249
16250     // private
16251     initQuery : function(){
16252         
16253         var v = this.getRawValue();
16254         
16255         if(this.tickable && this.editable){
16256             v = this.tickableInputEl().getValue();
16257         }
16258         
16259         this.doQuery(v);
16260     },
16261
16262     // private
16263     doForce : function(){
16264         if(this.inputEl().dom.value.length > 0){
16265             this.inputEl().dom.value =
16266                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16267              
16268         }
16269     },
16270
16271     /**
16272      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16273      * query allowing the query action to be canceled if needed.
16274      * @param {String} query The SQL query to execute
16275      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16276      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16277      * saved in the current store (defaults to false)
16278      */
16279     doQuery : function(q, forceAll){
16280         
16281         if(q === undefined || q === null){
16282             q = '';
16283         }
16284         var qe = {
16285             query: q,
16286             forceAll: forceAll,
16287             combo: this,
16288             cancel:false
16289         };
16290         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16291             return false;
16292         }
16293         q = qe.query;
16294         
16295         forceAll = qe.forceAll;
16296         if(forceAll === true || (q.length >= this.minChars)){
16297             
16298             this.hasQuery = true;
16299             
16300             if(this.lastQuery != q || this.alwaysQuery){
16301                 this.lastQuery = q;
16302                 if(this.mode == 'local'){
16303                     this.selectedIndex = -1;
16304                     if(forceAll){
16305                         this.store.clearFilter();
16306                     }else{
16307                         
16308                         if(this.specialFilter){
16309                             this.fireEvent('specialfilter', this);
16310                             this.onLoad();
16311                             return;
16312                         }
16313                         
16314                         this.store.filter(this.displayField, q);
16315                     }
16316                     
16317                     this.store.fireEvent("datachanged", this.store);
16318                     
16319                     this.onLoad();
16320                     
16321                     
16322                 }else{
16323                     
16324                     this.store.baseParams[this.queryParam] = q;
16325                     
16326                     var options = {params : this.getParams(q)};
16327                     
16328                     if(this.loadNext){
16329                         options.add = true;
16330                         options.params.start = this.page * this.pageSize;
16331                     }
16332                     
16333                     this.store.load(options);
16334                     
16335                     /*
16336                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16337                      *  we should expand the list on onLoad
16338                      *  so command out it
16339                      */
16340 //                    this.expand();
16341                 }
16342             }else{
16343                 this.selectedIndex = -1;
16344                 this.onLoad();   
16345             }
16346         }
16347         
16348         this.loadNext = false;
16349     },
16350     
16351     // private
16352     getParams : function(q){
16353         var p = {};
16354         //p[this.queryParam] = q;
16355         
16356         if(this.pageSize){
16357             p.start = 0;
16358             p.limit = this.pageSize;
16359         }
16360         return p;
16361     },
16362
16363     /**
16364      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16365      */
16366     collapse : function(){
16367         if(!this.isExpanded()){
16368             return;
16369         }
16370         
16371         this.list.hide();
16372         
16373         this.hasFocus = false;
16374         
16375         if(this.tickable){
16376             this.okBtn.hide();
16377             this.cancelBtn.hide();
16378             this.trigger.show();
16379             
16380             if(this.editable){
16381                 this.tickableInputEl().dom.value = '';
16382                 this.tickableInputEl().blur();
16383             }
16384             
16385         }
16386         
16387         Roo.get(document).un('mousedown', this.collapseIf, this);
16388         Roo.get(document).un('mousewheel', this.collapseIf, this);
16389         if (!this.editable) {
16390             Roo.get(document).un('keydown', this.listKeyPress, this);
16391         }
16392         this.fireEvent('collapse', this);
16393         
16394         this.validate();
16395     },
16396
16397     // private
16398     collapseIf : function(e){
16399         var in_combo  = e.within(this.el);
16400         var in_list =  e.within(this.list);
16401         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16402         
16403         if (in_combo || in_list || is_list) {
16404             //e.stopPropagation();
16405             return;
16406         }
16407         
16408         if(this.tickable){
16409             this.onTickableFooterButtonClick(e, false, false);
16410         }
16411
16412         this.collapse();
16413         
16414     },
16415
16416     /**
16417      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16418      */
16419     expand : function(){
16420        
16421         if(this.isExpanded() || !this.hasFocus){
16422             return;
16423         }
16424         
16425         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16426         this.list.setWidth(lw);
16427         
16428         Roo.log('expand');
16429         
16430         this.list.show();
16431         
16432         this.restrictHeight();
16433         
16434         if(this.tickable){
16435             
16436             this.tickItems = Roo.apply([], this.item);
16437             
16438             this.okBtn.show();
16439             this.cancelBtn.show();
16440             this.trigger.hide();
16441             
16442             if(this.editable){
16443                 this.tickableInputEl().focus();
16444             }
16445             
16446         }
16447         
16448         Roo.get(document).on('mousedown', this.collapseIf, this);
16449         Roo.get(document).on('mousewheel', this.collapseIf, this);
16450         if (!this.editable) {
16451             Roo.get(document).on('keydown', this.listKeyPress, this);
16452         }
16453         
16454         this.fireEvent('expand', this);
16455     },
16456
16457     // private
16458     // Implements the default empty TriggerField.onTriggerClick function
16459     onTriggerClick : function(e)
16460     {
16461         Roo.log('trigger click');
16462         
16463         if(this.disabled || !this.triggerList){
16464             return;
16465         }
16466         
16467         this.page = 0;
16468         this.loadNext = false;
16469         
16470         if(this.isExpanded()){
16471             this.collapse();
16472             if (!this.blockFocus) {
16473                 this.inputEl().focus();
16474             }
16475             
16476         }else {
16477             this.hasFocus = true;
16478             if(this.triggerAction == 'all') {
16479                 this.doQuery(this.allQuery, true);
16480             } else {
16481                 this.doQuery(this.getRawValue());
16482             }
16483             if (!this.blockFocus) {
16484                 this.inputEl().focus();
16485             }
16486         }
16487     },
16488     
16489     onTickableTriggerClick : function(e)
16490     {
16491         if(this.disabled){
16492             return;
16493         }
16494         
16495         this.page = 0;
16496         this.loadNext = false;
16497         this.hasFocus = true;
16498         
16499         if(this.triggerAction == 'all') {
16500             this.doQuery(this.allQuery, true);
16501         } else {
16502             this.doQuery(this.getRawValue());
16503         }
16504     },
16505     
16506     onSearchFieldClick : function(e)
16507     {
16508         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16509             this.onTickableFooterButtonClick(e, false, false);
16510             return;
16511         }
16512         
16513         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16514             return;
16515         }
16516         
16517         this.page = 0;
16518         this.loadNext = false;
16519         this.hasFocus = true;
16520         
16521         if(this.triggerAction == 'all') {
16522             this.doQuery(this.allQuery, true);
16523         } else {
16524             this.doQuery(this.getRawValue());
16525         }
16526     },
16527     
16528     listKeyPress : function(e)
16529     {
16530         //Roo.log('listkeypress');
16531         // scroll to first matching element based on key pres..
16532         if (e.isSpecialKey()) {
16533             return false;
16534         }
16535         var k = String.fromCharCode(e.getKey()).toUpperCase();
16536         //Roo.log(k);
16537         var match  = false;
16538         var csel = this.view.getSelectedNodes();
16539         var cselitem = false;
16540         if (csel.length) {
16541             var ix = this.view.indexOf(csel[0]);
16542             cselitem  = this.store.getAt(ix);
16543             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16544                 cselitem = false;
16545             }
16546             
16547         }
16548         
16549         this.store.each(function(v) { 
16550             if (cselitem) {
16551                 // start at existing selection.
16552                 if (cselitem.id == v.id) {
16553                     cselitem = false;
16554                 }
16555                 return true;
16556             }
16557                 
16558             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16559                 match = this.store.indexOf(v);
16560                 return false;
16561             }
16562             return true;
16563         }, this);
16564         
16565         if (match === false) {
16566             return true; // no more action?
16567         }
16568         // scroll to?
16569         this.view.select(match);
16570         var sn = Roo.get(this.view.getSelectedNodes()[0]);
16571         sn.scrollIntoView(sn.dom.parentNode, false);
16572     },
16573     
16574     onViewScroll : function(e, t){
16575         
16576         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){
16577             return;
16578         }
16579         
16580         this.hasQuery = true;
16581         
16582         this.loading = this.list.select('.loading', true).first();
16583         
16584         if(this.loading === null){
16585             this.list.createChild({
16586                 tag: 'div',
16587                 cls: 'loading roo-select2-more-results roo-select2-active',
16588                 html: 'Loading more results...'
16589             });
16590             
16591             this.loading = this.list.select('.loading', true).first();
16592             
16593             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16594             
16595             this.loading.hide();
16596         }
16597         
16598         this.loading.show();
16599         
16600         var _combo = this;
16601         
16602         this.page++;
16603         this.loadNext = true;
16604         
16605         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16606         
16607         return;
16608     },
16609     
16610     addItem : function(o)
16611     {   
16612         var dv = ''; // display value
16613         
16614         if (this.displayField) {
16615             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16616         } else {
16617             // this is an error condition!!!
16618             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16619         }
16620         
16621         if(!dv.length){
16622             return;
16623         }
16624         
16625         var choice = this.choices.createChild({
16626             tag: 'li',
16627             cls: 'roo-select2-search-choice',
16628             cn: [
16629                 {
16630                     tag: 'div',
16631                     html: dv
16632                 },
16633                 {
16634                     tag: 'a',
16635                     href: '#',
16636                     cls: 'roo-select2-search-choice-close fa fa-times',
16637                     tabindex: '-1'
16638                 }
16639             ]
16640             
16641         }, this.searchField);
16642         
16643         var close = choice.select('a.roo-select2-search-choice-close', true).first();
16644         
16645         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16646         
16647         this.item.push(o);
16648         
16649         this.lastData = o;
16650         
16651         this.syncValue();
16652         
16653         this.inputEl().dom.value = '';
16654         
16655         this.validate();
16656     },
16657     
16658     onRemoveItem : function(e, _self, o)
16659     {
16660         e.preventDefault();
16661         
16662         this.lastItem = Roo.apply([], this.item);
16663         
16664         var index = this.item.indexOf(o.data) * 1;
16665         
16666         if( index < 0){
16667             Roo.log('not this item?!');
16668             return;
16669         }
16670         
16671         this.item.splice(index, 1);
16672         o.item.remove();
16673         
16674         this.syncValue();
16675         
16676         this.fireEvent('remove', this, e);
16677         
16678         this.validate();
16679         
16680     },
16681     
16682     syncValue : function()
16683     {
16684         if(!this.item.length){
16685             this.clearValue();
16686             return;
16687         }
16688             
16689         var value = [];
16690         var _this = this;
16691         Roo.each(this.item, function(i){
16692             if(_this.valueField){
16693                 value.push(i[_this.valueField]);
16694                 return;
16695             }
16696
16697             value.push(i);
16698         });
16699
16700         this.value = value.join(',');
16701
16702         if(this.hiddenField){
16703             this.hiddenField.dom.value = this.value;
16704         }
16705         
16706         this.store.fireEvent("datachanged", this.store);
16707         
16708         this.validate();
16709     },
16710     
16711     clearItem : function()
16712     {
16713         if(!this.multiple){
16714             return;
16715         }
16716         
16717         this.item = [];
16718         
16719         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
16720            c.remove();
16721         });
16722         
16723         this.syncValue();
16724         
16725         this.validate();
16726         
16727         if(this.tickable && !Roo.isTouch){
16728             this.view.refresh();
16729         }
16730     },
16731     
16732     inputEl: function ()
16733     {
16734         if(Roo.isIOS && this.useNativeIOS){
16735             return this.el.select('select.roo-ios-select', true).first();
16736         }
16737         
16738         if(Roo.isTouch && this.mobileTouchView){
16739             return this.el.select('input.form-control',true).first();
16740         }
16741         
16742         if(this.tickable){
16743             return this.searchField;
16744         }
16745         
16746         return this.el.select('input.form-control',true).first();
16747     },
16748     
16749     onTickableFooterButtonClick : function(e, btn, el)
16750     {
16751         e.preventDefault();
16752         
16753         this.lastItem = Roo.apply([], this.item);
16754         
16755         if(btn && btn.name == 'cancel'){
16756             this.tickItems = Roo.apply([], this.item);
16757             this.collapse();
16758             return;
16759         }
16760         
16761         this.clearItem();
16762         
16763         var _this = this;
16764         
16765         Roo.each(this.tickItems, function(o){
16766             _this.addItem(o);
16767         });
16768         
16769         this.collapse();
16770         
16771     },
16772     
16773     validate : function()
16774     {
16775         if(this.getVisibilityEl().hasClass('hidden')){
16776             return true;
16777         }
16778         
16779         var v = this.getRawValue();
16780         
16781         if(this.multiple){
16782             v = this.getValue();
16783         }
16784         
16785         if(this.disabled || this.allowBlank || v.length){
16786             this.markValid();
16787             return true;
16788         }
16789         
16790         this.markInvalid();
16791         return false;
16792     },
16793     
16794     tickableInputEl : function()
16795     {
16796         if(!this.tickable || !this.editable){
16797             return this.inputEl();
16798         }
16799         
16800         return this.inputEl().select('.roo-select2-search-field-input', true).first();
16801     },
16802     
16803     
16804     getAutoCreateTouchView : function()
16805     {
16806         var id = Roo.id();
16807         
16808         var cfg = {
16809             cls: 'form-group' //input-group
16810         };
16811         
16812         var input =  {
16813             tag: 'input',
16814             id : id,
16815             type : this.inputType,
16816             cls : 'form-control x-combo-noedit',
16817             autocomplete: 'new-password',
16818             placeholder : this.placeholder || '',
16819             readonly : true
16820         };
16821         
16822         if (this.name) {
16823             input.name = this.name;
16824         }
16825         
16826         if (this.size) {
16827             input.cls += ' input-' + this.size;
16828         }
16829         
16830         if (this.disabled) {
16831             input.disabled = true;
16832         }
16833         
16834         var inputblock = {
16835             cls : '',
16836             cn : [
16837                 input
16838             ]
16839         };
16840         
16841         if(this.before){
16842             inputblock.cls += ' input-group';
16843             
16844             inputblock.cn.unshift({
16845                 tag :'span',
16846                 cls : 'input-group-addon input-group-prepend input-group-text',
16847                 html : this.before
16848             });
16849         }
16850         
16851         if(this.removable && !this.multiple){
16852             inputblock.cls += ' roo-removable';
16853             
16854             inputblock.cn.push({
16855                 tag: 'button',
16856                 html : 'x',
16857                 cls : 'roo-combo-removable-btn close'
16858             });
16859         }
16860
16861         if(this.hasFeedback && !this.allowBlank){
16862             
16863             inputblock.cls += ' has-feedback';
16864             
16865             inputblock.cn.push({
16866                 tag: 'span',
16867                 cls: 'glyphicon form-control-feedback'
16868             });
16869             
16870         }
16871         
16872         if (this.after) {
16873             
16874             inputblock.cls += (this.before) ? '' : ' input-group';
16875             
16876             inputblock.cn.push({
16877                 tag :'span',
16878                 cls : 'input-group-addon input-group-append input-group-text',
16879                 html : this.after
16880             });
16881         }
16882
16883         
16884         var ibwrap = inputblock;
16885         
16886         if(this.multiple){
16887             ibwrap = {
16888                 tag: 'ul',
16889                 cls: 'roo-select2-choices',
16890                 cn:[
16891                     {
16892                         tag: 'li',
16893                         cls: 'roo-select2-search-field',
16894                         cn: [
16895
16896                             inputblock
16897                         ]
16898                     }
16899                 ]
16900             };
16901         
16902             
16903         }
16904         
16905         var combobox = {
16906             cls: 'roo-select2-container input-group roo-touchview-combobox ',
16907             cn: [
16908                 {
16909                     tag: 'input',
16910                     type : 'hidden',
16911                     cls: 'form-hidden-field'
16912                 },
16913                 ibwrap
16914             ]
16915         };
16916         
16917         if(!this.multiple && this.showToggleBtn){
16918             
16919             var caret = {
16920                 cls: 'caret'
16921             };
16922             
16923             if (this.caret != false) {
16924                 caret = {
16925                      tag: 'i',
16926                      cls: 'fa fa-' + this.caret
16927                 };
16928                 
16929             }
16930             
16931             combobox.cn.push({
16932                 tag :'span',
16933                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
16934                 cn : [
16935                     Roo.bootstrap.version == 3 ? caret : '',
16936                     {
16937                         tag: 'span',
16938                         cls: 'combobox-clear',
16939                         cn  : [
16940                             {
16941                                 tag : 'i',
16942                                 cls: 'icon-remove'
16943                             }
16944                         ]
16945                     }
16946                 ]
16947
16948             })
16949         }
16950         
16951         if(this.multiple){
16952             combobox.cls += ' roo-select2-container-multi';
16953         }
16954         
16955         var align = this.labelAlign || this.parentLabelAlign();
16956         
16957         if (align ==='left' && this.fieldLabel.length) {
16958
16959             cfg.cn = [
16960                 {
16961                    tag : 'i',
16962                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
16963                    tooltip : 'This field is required'
16964                 },
16965                 {
16966                     tag: 'label',
16967                     cls : 'control-label col-form-label',
16968                     html : this.fieldLabel
16969
16970                 },
16971                 {
16972                     cls : '', 
16973                     cn: [
16974                         combobox
16975                     ]
16976                 }
16977             ];
16978             
16979             var labelCfg = cfg.cn[1];
16980             var contentCfg = cfg.cn[2];
16981             
16982
16983             if(this.indicatorpos == 'right'){
16984                 cfg.cn = [
16985                     {
16986                         tag: 'label',
16987                         'for' :  id,
16988                         cls : 'control-label col-form-label',
16989                         cn : [
16990                             {
16991                                 tag : 'span',
16992                                 html : this.fieldLabel
16993                             },
16994                             {
16995                                 tag : 'i',
16996                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
16997                                 tooltip : 'This field is required'
16998                             }
16999                         ]
17000                     },
17001                     {
17002                         cls : "",
17003                         cn: [
17004                             combobox
17005                         ]
17006                     }
17007
17008                 ];
17009                 
17010                 labelCfg = cfg.cn[0];
17011                 contentCfg = cfg.cn[1];
17012             }
17013             
17014            
17015             
17016             if(this.labelWidth > 12){
17017                 labelCfg.style = "width: " + this.labelWidth + 'px';
17018             }
17019             
17020             if(this.labelWidth < 13 && this.labelmd == 0){
17021                 this.labelmd = this.labelWidth;
17022             }
17023             
17024             if(this.labellg > 0){
17025                 labelCfg.cls += ' col-lg-' + this.labellg;
17026                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17027             }
17028             
17029             if(this.labelmd > 0){
17030                 labelCfg.cls += ' col-md-' + this.labelmd;
17031                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17032             }
17033             
17034             if(this.labelsm > 0){
17035                 labelCfg.cls += ' col-sm-' + this.labelsm;
17036                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17037             }
17038             
17039             if(this.labelxs > 0){
17040                 labelCfg.cls += ' col-xs-' + this.labelxs;
17041                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17042             }
17043                 
17044                 
17045         } else if ( this.fieldLabel.length) {
17046             cfg.cn = [
17047                 {
17048                    tag : 'i',
17049                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17050                    tooltip : 'This field is required'
17051                 },
17052                 {
17053                     tag: 'label',
17054                     cls : 'control-label',
17055                     html : this.fieldLabel
17056
17057                 },
17058                 {
17059                     cls : '', 
17060                     cn: [
17061                         combobox
17062                     ]
17063                 }
17064             ];
17065             
17066             if(this.indicatorpos == 'right'){
17067                 cfg.cn = [
17068                     {
17069                         tag: 'label',
17070                         cls : 'control-label',
17071                         html : this.fieldLabel,
17072                         cn : [
17073                             {
17074                                tag : 'i',
17075                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17076                                tooltip : 'This field is required'
17077                             }
17078                         ]
17079                     },
17080                     {
17081                         cls : '', 
17082                         cn: [
17083                             combobox
17084                         ]
17085                     }
17086                 ];
17087             }
17088         } else {
17089             cfg.cn = combobox;    
17090         }
17091         
17092         
17093         var settings = this;
17094         
17095         ['xs','sm','md','lg'].map(function(size){
17096             if (settings[size]) {
17097                 cfg.cls += ' col-' + size + '-' + settings[size];
17098             }
17099         });
17100         
17101         return cfg;
17102     },
17103     
17104     initTouchView : function()
17105     {
17106         this.renderTouchView();
17107         
17108         this.touchViewEl.on('scroll', function(){
17109             this.el.dom.scrollTop = 0;
17110         }, this);
17111         
17112         this.originalValue = this.getValue();
17113         
17114         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17115         
17116         this.inputEl().on("click", this.showTouchView, this);
17117         if (this.triggerEl) {
17118             this.triggerEl.on("click", this.showTouchView, this);
17119         }
17120         
17121         
17122         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17123         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17124         
17125         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17126         
17127         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17128         this.store.on('load', this.onTouchViewLoad, this);
17129         this.store.on('loadexception', this.onTouchViewLoadException, this);
17130         
17131         if(this.hiddenName){
17132             
17133             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17134             
17135             this.hiddenField.dom.value =
17136                 this.hiddenValue !== undefined ? this.hiddenValue :
17137                 this.value !== undefined ? this.value : '';
17138         
17139             this.el.dom.removeAttribute('name');
17140             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17141         }
17142         
17143         if(this.multiple){
17144             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17145             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17146         }
17147         
17148         if(this.removable && !this.multiple){
17149             var close = this.closeTriggerEl();
17150             if(close){
17151                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17152                 close.on('click', this.removeBtnClick, this, close);
17153             }
17154         }
17155         /*
17156          * fix the bug in Safari iOS8
17157          */
17158         this.inputEl().on("focus", function(e){
17159             document.activeElement.blur();
17160         }, this);
17161         
17162         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17163         
17164         return;
17165         
17166         
17167     },
17168     
17169     renderTouchView : function()
17170     {
17171         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17172         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17173         
17174         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17175         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17176         
17177         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17178         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17179         this.touchViewBodyEl.setStyle('overflow', 'auto');
17180         
17181         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17182         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17183         
17184         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17185         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17186         
17187     },
17188     
17189     showTouchView : function()
17190     {
17191         if(this.disabled){
17192             return;
17193         }
17194         
17195         this.touchViewHeaderEl.hide();
17196
17197         if(this.modalTitle.length){
17198             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17199             this.touchViewHeaderEl.show();
17200         }
17201
17202         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17203         this.touchViewEl.show();
17204
17205         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17206         
17207         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17208         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17209
17210         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17211
17212         if(this.modalTitle.length){
17213             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17214         }
17215         
17216         this.touchViewBodyEl.setHeight(bodyHeight);
17217
17218         if(this.animate){
17219             var _this = this;
17220             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
17221         }else{
17222             this.touchViewEl.addClass('in');
17223         }
17224         
17225         if(this._touchViewMask){
17226             Roo.get(document.body).addClass("x-body-masked");
17227             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17228             this._touchViewMask.setStyle('z-index', 10000);
17229             this._touchViewMask.addClass('show');
17230         }
17231         
17232         this.doTouchViewQuery();
17233         
17234     },
17235     
17236     hideTouchView : function()
17237     {
17238         this.touchViewEl.removeClass('in');
17239
17240         if(this.animate){
17241             var _this = this;
17242             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17243         }else{
17244             this.touchViewEl.setStyle('display', 'none');
17245         }
17246         
17247         if(this._touchViewMask){
17248             this._touchViewMask.removeClass('show');
17249             Roo.get(document.body).removeClass("x-body-masked");
17250         }
17251     },
17252     
17253     setTouchViewValue : function()
17254     {
17255         if(this.multiple){
17256             this.clearItem();
17257         
17258             var _this = this;
17259
17260             Roo.each(this.tickItems, function(o){
17261                 this.addItem(o);
17262             }, this);
17263         }
17264         
17265         this.hideTouchView();
17266     },
17267     
17268     doTouchViewQuery : function()
17269     {
17270         var qe = {
17271             query: '',
17272             forceAll: true,
17273             combo: this,
17274             cancel:false
17275         };
17276         
17277         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17278             return false;
17279         }
17280         
17281         if(!this.alwaysQuery || this.mode == 'local'){
17282             this.onTouchViewLoad();
17283             return;
17284         }
17285         
17286         this.store.load();
17287     },
17288     
17289     onTouchViewBeforeLoad : function(combo,opts)
17290     {
17291         return;
17292     },
17293
17294     // private
17295     onTouchViewLoad : function()
17296     {
17297         if(this.store.getCount() < 1){
17298             this.onTouchViewEmptyResults();
17299             return;
17300         }
17301         
17302         this.clearTouchView();
17303         
17304         var rawValue = this.getRawValue();
17305         
17306         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17307         
17308         this.tickItems = [];
17309         
17310         this.store.data.each(function(d, rowIndex){
17311             var row = this.touchViewListGroup.createChild(template);
17312             
17313             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17314                 row.addClass(d.data.cls);
17315             }
17316             
17317             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17318                 var cfg = {
17319                     data : d.data,
17320                     html : d.data[this.displayField]
17321                 };
17322                 
17323                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17324                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17325                 }
17326             }
17327             row.removeClass('selected');
17328             if(!this.multiple && this.valueField &&
17329                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17330             {
17331                 // radio buttons..
17332                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17333                 row.addClass('selected');
17334             }
17335             
17336             if(this.multiple && this.valueField &&
17337                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17338             {
17339                 
17340                 // checkboxes...
17341                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17342                 this.tickItems.push(d.data);
17343             }
17344             
17345             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17346             
17347         }, this);
17348         
17349         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17350         
17351         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17352
17353         if(this.modalTitle.length){
17354             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17355         }
17356
17357         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17358         
17359         if(this.mobile_restrict_height && listHeight < bodyHeight){
17360             this.touchViewBodyEl.setHeight(listHeight);
17361         }
17362         
17363         var _this = this;
17364         
17365         if(firstChecked && listHeight > bodyHeight){
17366             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17367         }
17368         
17369     },
17370     
17371     onTouchViewLoadException : function()
17372     {
17373         this.hideTouchView();
17374     },
17375     
17376     onTouchViewEmptyResults : function()
17377     {
17378         this.clearTouchView();
17379         
17380         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17381         
17382         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17383         
17384     },
17385     
17386     clearTouchView : function()
17387     {
17388         this.touchViewListGroup.dom.innerHTML = '';
17389     },
17390     
17391     onTouchViewClick : function(e, el, o)
17392     {
17393         e.preventDefault();
17394         
17395         var row = o.row;
17396         var rowIndex = o.rowIndex;
17397         
17398         var r = this.store.getAt(rowIndex);
17399         
17400         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17401             
17402             if(!this.multiple){
17403                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17404                     c.dom.removeAttribute('checked');
17405                 }, this);
17406
17407                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17408
17409                 this.setFromData(r.data);
17410
17411                 var close = this.closeTriggerEl();
17412
17413                 if(close){
17414                     close.show();
17415                 }
17416
17417                 this.hideTouchView();
17418
17419                 this.fireEvent('select', this, r, rowIndex);
17420
17421                 return;
17422             }
17423
17424             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17425                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17426                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17427                 return;
17428             }
17429
17430             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17431             this.addItem(r.data);
17432             this.tickItems.push(r.data);
17433         }
17434     },
17435     
17436     getAutoCreateNativeIOS : function()
17437     {
17438         var cfg = {
17439             cls: 'form-group' //input-group,
17440         };
17441         
17442         var combobox =  {
17443             tag: 'select',
17444             cls : 'roo-ios-select'
17445         };
17446         
17447         if (this.name) {
17448             combobox.name = this.name;
17449         }
17450         
17451         if (this.disabled) {
17452             combobox.disabled = true;
17453         }
17454         
17455         var settings = this;
17456         
17457         ['xs','sm','md','lg'].map(function(size){
17458             if (settings[size]) {
17459                 cfg.cls += ' col-' + size + '-' + settings[size];
17460             }
17461         });
17462         
17463         cfg.cn = combobox;
17464         
17465         return cfg;
17466         
17467     },
17468     
17469     initIOSView : function()
17470     {
17471         this.store.on('load', this.onIOSViewLoad, this);
17472         
17473         return;
17474     },
17475     
17476     onIOSViewLoad : function()
17477     {
17478         if(this.store.getCount() < 1){
17479             return;
17480         }
17481         
17482         this.clearIOSView();
17483         
17484         if(this.allowBlank) {
17485             
17486             var default_text = '-- SELECT --';
17487             
17488             if(this.placeholder.length){
17489                 default_text = this.placeholder;
17490             }
17491             
17492             if(this.emptyTitle.length){
17493                 default_text += ' - ' + this.emptyTitle + ' -';
17494             }
17495             
17496             var opt = this.inputEl().createChild({
17497                 tag: 'option',
17498                 value : 0,
17499                 html : default_text
17500             });
17501             
17502             var o = {};
17503             o[this.valueField] = 0;
17504             o[this.displayField] = default_text;
17505             
17506             this.ios_options.push({
17507                 data : o,
17508                 el : opt
17509             });
17510             
17511         }
17512         
17513         this.store.data.each(function(d, rowIndex){
17514             
17515             var html = '';
17516             
17517             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17518                 html = d.data[this.displayField];
17519             }
17520             
17521             var value = '';
17522             
17523             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17524                 value = d.data[this.valueField];
17525             }
17526             
17527             var option = {
17528                 tag: 'option',
17529                 value : value,
17530                 html : html
17531             };
17532             
17533             if(this.value == d.data[this.valueField]){
17534                 option['selected'] = true;
17535             }
17536             
17537             var opt = this.inputEl().createChild(option);
17538             
17539             this.ios_options.push({
17540                 data : d.data,
17541                 el : opt
17542             });
17543             
17544         }, this);
17545         
17546         this.inputEl().on('change', function(){
17547            this.fireEvent('select', this);
17548         }, this);
17549         
17550     },
17551     
17552     clearIOSView: function()
17553     {
17554         this.inputEl().dom.innerHTML = '';
17555         
17556         this.ios_options = [];
17557     },
17558     
17559     setIOSValue: function(v)
17560     {
17561         this.value = v;
17562         
17563         if(!this.ios_options){
17564             return;
17565         }
17566         
17567         Roo.each(this.ios_options, function(opts){
17568            
17569            opts.el.dom.removeAttribute('selected');
17570            
17571            if(opts.data[this.valueField] != v){
17572                return;
17573            }
17574            
17575            opts.el.dom.setAttribute('selected', true);
17576            
17577         }, this);
17578     }
17579
17580     /** 
17581     * @cfg {Boolean} grow 
17582     * @hide 
17583     */
17584     /** 
17585     * @cfg {Number} growMin 
17586     * @hide 
17587     */
17588     /** 
17589     * @cfg {Number} growMax 
17590     * @hide 
17591     */
17592     /**
17593      * @hide
17594      * @method autoSize
17595      */
17596 });
17597
17598 Roo.apply(Roo.bootstrap.ComboBox,  {
17599     
17600     header : {
17601         tag: 'div',
17602         cls: 'modal-header',
17603         cn: [
17604             {
17605                 tag: 'h4',
17606                 cls: 'modal-title'
17607             }
17608         ]
17609     },
17610     
17611     body : {
17612         tag: 'div',
17613         cls: 'modal-body',
17614         cn: [
17615             {
17616                 tag: 'ul',
17617                 cls: 'list-group'
17618             }
17619         ]
17620     },
17621     
17622     listItemRadio : {
17623         tag: 'li',
17624         cls: 'list-group-item',
17625         cn: [
17626             {
17627                 tag: 'span',
17628                 cls: 'roo-combobox-list-group-item-value'
17629             },
17630             {
17631                 tag: 'div',
17632                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17633                 cn: [
17634                     {
17635                         tag: 'input',
17636                         type: 'radio'
17637                     },
17638                     {
17639                         tag: 'label'
17640                     }
17641                 ]
17642             }
17643         ]
17644     },
17645     
17646     listItemCheckbox : {
17647         tag: 'li',
17648         cls: 'list-group-item',
17649         cn: [
17650             {
17651                 tag: 'span',
17652                 cls: 'roo-combobox-list-group-item-value'
17653             },
17654             {
17655                 tag: 'div',
17656                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17657                 cn: [
17658                     {
17659                         tag: 'input',
17660                         type: 'checkbox'
17661                     },
17662                     {
17663                         tag: 'label'
17664                     }
17665                 ]
17666             }
17667         ]
17668     },
17669     
17670     emptyResult : {
17671         tag: 'div',
17672         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17673     },
17674     
17675     footer : {
17676         tag: 'div',
17677         cls: 'modal-footer',
17678         cn: [
17679             {
17680                 tag: 'div',
17681                 cls: 'row',
17682                 cn: [
17683                     {
17684                         tag: 'div',
17685                         cls: 'col-xs-6 text-left',
17686                         cn: {
17687                             tag: 'button',
17688                             cls: 'btn btn-danger roo-touch-view-cancel',
17689                             html: 'Cancel'
17690                         }
17691                     },
17692                     {
17693                         tag: 'div',
17694                         cls: 'col-xs-6 text-right',
17695                         cn: {
17696                             tag: 'button',
17697                             cls: 'btn btn-success roo-touch-view-ok',
17698                             html: 'OK'
17699                         }
17700                     }
17701                 ]
17702             }
17703         ]
17704         
17705     }
17706 });
17707
17708 Roo.apply(Roo.bootstrap.ComboBox,  {
17709     
17710     touchViewTemplate : {
17711         tag: 'div',
17712         cls: 'modal fade roo-combobox-touch-view',
17713         cn: [
17714             {
17715                 tag: 'div',
17716                 cls: 'modal-dialog',
17717                 style : 'position:fixed', // we have to fix position....
17718                 cn: [
17719                     {
17720                         tag: 'div',
17721                         cls: 'modal-content',
17722                         cn: [
17723                             Roo.bootstrap.ComboBox.header,
17724                             Roo.bootstrap.ComboBox.body,
17725                             Roo.bootstrap.ComboBox.footer
17726                         ]
17727                     }
17728                 ]
17729             }
17730         ]
17731     }
17732 });/*
17733  * Based on:
17734  * Ext JS Library 1.1.1
17735  * Copyright(c) 2006-2007, Ext JS, LLC.
17736  *
17737  * Originally Released Under LGPL - original licence link has changed is not relivant.
17738  *
17739  * Fork - LGPL
17740  * <script type="text/javascript">
17741  */
17742
17743 /**
17744  * @class Roo.View
17745  * @extends Roo.util.Observable
17746  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
17747  * This class also supports single and multi selection modes. <br>
17748  * Create a data model bound view:
17749  <pre><code>
17750  var store = new Roo.data.Store(...);
17751
17752  var view = new Roo.View({
17753     el : "my-element",
17754     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
17755  
17756     singleSelect: true,
17757     selectedClass: "ydataview-selected",
17758     store: store
17759  });
17760
17761  // listen for node click?
17762  view.on("click", function(vw, index, node, e){
17763  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
17764  });
17765
17766  // load XML data
17767  dataModel.load("foobar.xml");
17768  </code></pre>
17769  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
17770  * <br><br>
17771  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
17772  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
17773  * 
17774  * Note: old style constructor is still suported (container, template, config)
17775  * 
17776  * @constructor
17777  * Create a new View
17778  * @param {Object} config The config object
17779  * 
17780  */
17781 Roo.View = function(config, depreciated_tpl, depreciated_config){
17782     
17783     this.parent = false;
17784     
17785     if (typeof(depreciated_tpl) == 'undefined') {
17786         // new way.. - universal constructor.
17787         Roo.apply(this, config);
17788         this.el  = Roo.get(this.el);
17789     } else {
17790         // old format..
17791         this.el  = Roo.get(config);
17792         this.tpl = depreciated_tpl;
17793         Roo.apply(this, depreciated_config);
17794     }
17795     this.wrapEl  = this.el.wrap().wrap();
17796     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
17797     
17798     
17799     if(typeof(this.tpl) == "string"){
17800         this.tpl = new Roo.Template(this.tpl);
17801     } else {
17802         // support xtype ctors..
17803         this.tpl = new Roo.factory(this.tpl, Roo);
17804     }
17805     
17806     
17807     this.tpl.compile();
17808     
17809     /** @private */
17810     this.addEvents({
17811         /**
17812          * @event beforeclick
17813          * Fires before a click is processed. Returns false to cancel the default action.
17814          * @param {Roo.View} this
17815          * @param {Number} index The index of the target node
17816          * @param {HTMLElement} node The target node
17817          * @param {Roo.EventObject} e The raw event object
17818          */
17819             "beforeclick" : true,
17820         /**
17821          * @event click
17822          * Fires when a template node is clicked.
17823          * @param {Roo.View} this
17824          * @param {Number} index The index of the target node
17825          * @param {HTMLElement} node The target node
17826          * @param {Roo.EventObject} e The raw event object
17827          */
17828             "click" : true,
17829         /**
17830          * @event dblclick
17831          * Fires when a template node is double clicked.
17832          * @param {Roo.View} this
17833          * @param {Number} index The index of the target node
17834          * @param {HTMLElement} node The target node
17835          * @param {Roo.EventObject} e The raw event object
17836          */
17837             "dblclick" : true,
17838         /**
17839          * @event contextmenu
17840          * Fires when a template node is right clicked.
17841          * @param {Roo.View} this
17842          * @param {Number} index The index of the target node
17843          * @param {HTMLElement} node The target node
17844          * @param {Roo.EventObject} e The raw event object
17845          */
17846             "contextmenu" : true,
17847         /**
17848          * @event selectionchange
17849          * Fires when the selected nodes change.
17850          * @param {Roo.View} this
17851          * @param {Array} selections Array of the selected nodes
17852          */
17853             "selectionchange" : true,
17854     
17855         /**
17856          * @event beforeselect
17857          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
17858          * @param {Roo.View} this
17859          * @param {HTMLElement} node The node to be selected
17860          * @param {Array} selections Array of currently selected nodes
17861          */
17862             "beforeselect" : true,
17863         /**
17864          * @event preparedata
17865          * Fires on every row to render, to allow you to change the data.
17866          * @param {Roo.View} this
17867          * @param {Object} data to be rendered (change this)
17868          */
17869           "preparedata" : true
17870           
17871           
17872         });
17873
17874
17875
17876     this.el.on({
17877         "click": this.onClick,
17878         "dblclick": this.onDblClick,
17879         "contextmenu": this.onContextMenu,
17880         scope:this
17881     });
17882
17883     this.selections = [];
17884     this.nodes = [];
17885     this.cmp = new Roo.CompositeElementLite([]);
17886     if(this.store){
17887         this.store = Roo.factory(this.store, Roo.data);
17888         this.setStore(this.store, true);
17889     }
17890     
17891     if ( this.footer && this.footer.xtype) {
17892            
17893          var fctr = this.wrapEl.appendChild(document.createElement("div"));
17894         
17895         this.footer.dataSource = this.store;
17896         this.footer.container = fctr;
17897         this.footer = Roo.factory(this.footer, Roo);
17898         fctr.insertFirst(this.el);
17899         
17900         // this is a bit insane - as the paging toolbar seems to detach the el..
17901 //        dom.parentNode.parentNode.parentNode
17902          // they get detached?
17903     }
17904     
17905     
17906     Roo.View.superclass.constructor.call(this);
17907     
17908     
17909 };
17910
17911 Roo.extend(Roo.View, Roo.util.Observable, {
17912     
17913      /**
17914      * @cfg {Roo.data.Store} store Data store to load data from.
17915      */
17916     store : false,
17917     
17918     /**
17919      * @cfg {String|Roo.Element} el The container element.
17920      */
17921     el : '',
17922     
17923     /**
17924      * @cfg {String|Roo.Template} tpl The template used by this View 
17925      */
17926     tpl : false,
17927     /**
17928      * @cfg {String} dataName the named area of the template to use as the data area
17929      *                          Works with domtemplates roo-name="name"
17930      */
17931     dataName: false,
17932     /**
17933      * @cfg {String} selectedClass The css class to add to selected nodes
17934      */
17935     selectedClass : "x-view-selected",
17936      /**
17937      * @cfg {String} emptyText The empty text to show when nothing is loaded.
17938      */
17939     emptyText : "",
17940     
17941     /**
17942      * @cfg {String} text to display on mask (default Loading)
17943      */
17944     mask : false,
17945     /**
17946      * @cfg {Boolean} multiSelect Allow multiple selection
17947      */
17948     multiSelect : false,
17949     /**
17950      * @cfg {Boolean} singleSelect Allow single selection
17951      */
17952     singleSelect:  false,
17953     
17954     /**
17955      * @cfg {Boolean} toggleSelect - selecting 
17956      */
17957     toggleSelect : false,
17958     
17959     /**
17960      * @cfg {Boolean} tickable - selecting 
17961      */
17962     tickable : false,
17963     
17964     /**
17965      * Returns the element this view is bound to.
17966      * @return {Roo.Element}
17967      */
17968     getEl : function(){
17969         return this.wrapEl;
17970     },
17971     
17972     
17973
17974     /**
17975      * Refreshes the view. - called by datachanged on the store. - do not call directly.
17976      */
17977     refresh : function(){
17978         //Roo.log('refresh');
17979         var t = this.tpl;
17980         
17981         // if we are using something like 'domtemplate', then
17982         // the what gets used is:
17983         // t.applySubtemplate(NAME, data, wrapping data..)
17984         // the outer template then get' applied with
17985         //     the store 'extra data'
17986         // and the body get's added to the
17987         //      roo-name="data" node?
17988         //      <span class='roo-tpl-{name}'></span> ?????
17989         
17990         
17991         
17992         this.clearSelections();
17993         this.el.update("");
17994         var html = [];
17995         var records = this.store.getRange();
17996         if(records.length < 1) {
17997             
17998             // is this valid??  = should it render a template??
17999             
18000             this.el.update(this.emptyText);
18001             return;
18002         }
18003         var el = this.el;
18004         if (this.dataName) {
18005             this.el.update(t.apply(this.store.meta)); //????
18006             el = this.el.child('.roo-tpl-' + this.dataName);
18007         }
18008         
18009         for(var i = 0, len = records.length; i < len; i++){
18010             var data = this.prepareData(records[i].data, i, records[i]);
18011             this.fireEvent("preparedata", this, data, i, records[i]);
18012             
18013             var d = Roo.apply({}, data);
18014             
18015             if(this.tickable){
18016                 Roo.apply(d, {'roo-id' : Roo.id()});
18017                 
18018                 var _this = this;
18019             
18020                 Roo.each(this.parent.item, function(item){
18021                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18022                         return;
18023                     }
18024                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18025                 });
18026             }
18027             
18028             html[html.length] = Roo.util.Format.trim(
18029                 this.dataName ?
18030                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18031                     t.apply(d)
18032             );
18033         }
18034         
18035         
18036         
18037         el.update(html.join(""));
18038         this.nodes = el.dom.childNodes;
18039         this.updateIndexes(0);
18040     },
18041     
18042
18043     /**
18044      * Function to override to reformat the data that is sent to
18045      * the template for each node.
18046      * DEPRICATED - use the preparedata event handler.
18047      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18048      * a JSON object for an UpdateManager bound view).
18049      */
18050     prepareData : function(data, index, record)
18051     {
18052         this.fireEvent("preparedata", this, data, index, record);
18053         return data;
18054     },
18055
18056     onUpdate : function(ds, record){
18057         // Roo.log('on update');   
18058         this.clearSelections();
18059         var index = this.store.indexOf(record);
18060         var n = this.nodes[index];
18061         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18062         n.parentNode.removeChild(n);
18063         this.updateIndexes(index, index);
18064     },
18065
18066     
18067     
18068 // --------- FIXME     
18069     onAdd : function(ds, records, index)
18070     {
18071         //Roo.log(['on Add', ds, records, index] );        
18072         this.clearSelections();
18073         if(this.nodes.length == 0){
18074             this.refresh();
18075             return;
18076         }
18077         var n = this.nodes[index];
18078         for(var i = 0, len = records.length; i < len; i++){
18079             var d = this.prepareData(records[i].data, i, records[i]);
18080             if(n){
18081                 this.tpl.insertBefore(n, d);
18082             }else{
18083                 
18084                 this.tpl.append(this.el, d);
18085             }
18086         }
18087         this.updateIndexes(index);
18088     },
18089
18090     onRemove : function(ds, record, index){
18091        // Roo.log('onRemove');
18092         this.clearSelections();
18093         var el = this.dataName  ?
18094             this.el.child('.roo-tpl-' + this.dataName) :
18095             this.el; 
18096         
18097         el.dom.removeChild(this.nodes[index]);
18098         this.updateIndexes(index);
18099     },
18100
18101     /**
18102      * Refresh an individual node.
18103      * @param {Number} index
18104      */
18105     refreshNode : function(index){
18106         this.onUpdate(this.store, this.store.getAt(index));
18107     },
18108
18109     updateIndexes : function(startIndex, endIndex){
18110         var ns = this.nodes;
18111         startIndex = startIndex || 0;
18112         endIndex = endIndex || ns.length - 1;
18113         for(var i = startIndex; i <= endIndex; i++){
18114             ns[i].nodeIndex = i;
18115         }
18116     },
18117
18118     /**
18119      * Changes the data store this view uses and refresh the view.
18120      * @param {Store} store
18121      */
18122     setStore : function(store, initial){
18123         if(!initial && this.store){
18124             this.store.un("datachanged", this.refresh);
18125             this.store.un("add", this.onAdd);
18126             this.store.un("remove", this.onRemove);
18127             this.store.un("update", this.onUpdate);
18128             this.store.un("clear", this.refresh);
18129             this.store.un("beforeload", this.onBeforeLoad);
18130             this.store.un("load", this.onLoad);
18131             this.store.un("loadexception", this.onLoad);
18132         }
18133         if(store){
18134           
18135             store.on("datachanged", this.refresh, this);
18136             store.on("add", this.onAdd, this);
18137             store.on("remove", this.onRemove, this);
18138             store.on("update", this.onUpdate, this);
18139             store.on("clear", this.refresh, this);
18140             store.on("beforeload", this.onBeforeLoad, this);
18141             store.on("load", this.onLoad, this);
18142             store.on("loadexception", this.onLoad, this);
18143         }
18144         
18145         if(store){
18146             this.refresh();
18147         }
18148     },
18149     /**
18150      * onbeforeLoad - masks the loading area.
18151      *
18152      */
18153     onBeforeLoad : function(store,opts)
18154     {
18155          //Roo.log('onBeforeLoad');   
18156         if (!opts.add) {
18157             this.el.update("");
18158         }
18159         this.el.mask(this.mask ? this.mask : "Loading" ); 
18160     },
18161     onLoad : function ()
18162     {
18163         this.el.unmask();
18164     },
18165     
18166
18167     /**
18168      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18169      * @param {HTMLElement} node
18170      * @return {HTMLElement} The template node
18171      */
18172     findItemFromChild : function(node){
18173         var el = this.dataName  ?
18174             this.el.child('.roo-tpl-' + this.dataName,true) :
18175             this.el.dom; 
18176         
18177         if(!node || node.parentNode == el){
18178                     return node;
18179             }
18180             var p = node.parentNode;
18181             while(p && p != el){
18182             if(p.parentNode == el){
18183                 return p;
18184             }
18185             p = p.parentNode;
18186         }
18187             return null;
18188     },
18189
18190     /** @ignore */
18191     onClick : function(e){
18192         var item = this.findItemFromChild(e.getTarget());
18193         if(item){
18194             var index = this.indexOf(item);
18195             if(this.onItemClick(item, index, e) !== false){
18196                 this.fireEvent("click", this, index, item, e);
18197             }
18198         }else{
18199             this.clearSelections();
18200         }
18201     },
18202
18203     /** @ignore */
18204     onContextMenu : function(e){
18205         var item = this.findItemFromChild(e.getTarget());
18206         if(item){
18207             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18208         }
18209     },
18210
18211     /** @ignore */
18212     onDblClick : function(e){
18213         var item = this.findItemFromChild(e.getTarget());
18214         if(item){
18215             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18216         }
18217     },
18218
18219     onItemClick : function(item, index, e)
18220     {
18221         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18222             return false;
18223         }
18224         if (this.toggleSelect) {
18225             var m = this.isSelected(item) ? 'unselect' : 'select';
18226             //Roo.log(m);
18227             var _t = this;
18228             _t[m](item, true, false);
18229             return true;
18230         }
18231         if(this.multiSelect || this.singleSelect){
18232             if(this.multiSelect && e.shiftKey && this.lastSelection){
18233                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18234             }else{
18235                 this.select(item, this.multiSelect && e.ctrlKey);
18236                 this.lastSelection = item;
18237             }
18238             
18239             if(!this.tickable){
18240                 e.preventDefault();
18241             }
18242             
18243         }
18244         return true;
18245     },
18246
18247     /**
18248      * Get the number of selected nodes.
18249      * @return {Number}
18250      */
18251     getSelectionCount : function(){
18252         return this.selections.length;
18253     },
18254
18255     /**
18256      * Get the currently selected nodes.
18257      * @return {Array} An array of HTMLElements
18258      */
18259     getSelectedNodes : function(){
18260         return this.selections;
18261     },
18262
18263     /**
18264      * Get the indexes of the selected nodes.
18265      * @return {Array}
18266      */
18267     getSelectedIndexes : function(){
18268         var indexes = [], s = this.selections;
18269         for(var i = 0, len = s.length; i < len; i++){
18270             indexes.push(s[i].nodeIndex);
18271         }
18272         return indexes;
18273     },
18274
18275     /**
18276      * Clear all selections
18277      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18278      */
18279     clearSelections : function(suppressEvent){
18280         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18281             this.cmp.elements = this.selections;
18282             this.cmp.removeClass(this.selectedClass);
18283             this.selections = [];
18284             if(!suppressEvent){
18285                 this.fireEvent("selectionchange", this, this.selections);
18286             }
18287         }
18288     },
18289
18290     /**
18291      * Returns true if the passed node is selected
18292      * @param {HTMLElement/Number} node The node or node index
18293      * @return {Boolean}
18294      */
18295     isSelected : function(node){
18296         var s = this.selections;
18297         if(s.length < 1){
18298             return false;
18299         }
18300         node = this.getNode(node);
18301         return s.indexOf(node) !== -1;
18302     },
18303
18304     /**
18305      * Selects nodes.
18306      * @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
18307      * @param {Boolean} keepExisting (optional) true to keep existing selections
18308      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18309      */
18310     select : function(nodeInfo, keepExisting, suppressEvent){
18311         if(nodeInfo instanceof Array){
18312             if(!keepExisting){
18313                 this.clearSelections(true);
18314             }
18315             for(var i = 0, len = nodeInfo.length; i < len; i++){
18316                 this.select(nodeInfo[i], true, true);
18317             }
18318             return;
18319         } 
18320         var node = this.getNode(nodeInfo);
18321         if(!node || this.isSelected(node)){
18322             return; // already selected.
18323         }
18324         if(!keepExisting){
18325             this.clearSelections(true);
18326         }
18327         
18328         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18329             Roo.fly(node).addClass(this.selectedClass);
18330             this.selections.push(node);
18331             if(!suppressEvent){
18332                 this.fireEvent("selectionchange", this, this.selections);
18333             }
18334         }
18335         
18336         
18337     },
18338       /**
18339      * Unselects nodes.
18340      * @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
18341      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18342      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18343      */
18344     unselect : function(nodeInfo, keepExisting, suppressEvent)
18345     {
18346         if(nodeInfo instanceof Array){
18347             Roo.each(this.selections, function(s) {
18348                 this.unselect(s, nodeInfo);
18349             }, this);
18350             return;
18351         }
18352         var node = this.getNode(nodeInfo);
18353         if(!node || !this.isSelected(node)){
18354             //Roo.log("not selected");
18355             return; // not selected.
18356         }
18357         // fireevent???
18358         var ns = [];
18359         Roo.each(this.selections, function(s) {
18360             if (s == node ) {
18361                 Roo.fly(node).removeClass(this.selectedClass);
18362
18363                 return;
18364             }
18365             ns.push(s);
18366         },this);
18367         
18368         this.selections= ns;
18369         this.fireEvent("selectionchange", this, this.selections);
18370     },
18371
18372     /**
18373      * Gets a template node.
18374      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18375      * @return {HTMLElement} The node or null if it wasn't found
18376      */
18377     getNode : function(nodeInfo){
18378         if(typeof nodeInfo == "string"){
18379             return document.getElementById(nodeInfo);
18380         }else if(typeof nodeInfo == "number"){
18381             return this.nodes[nodeInfo];
18382         }
18383         return nodeInfo;
18384     },
18385
18386     /**
18387      * Gets a range template nodes.
18388      * @param {Number} startIndex
18389      * @param {Number} endIndex
18390      * @return {Array} An array of nodes
18391      */
18392     getNodes : function(start, end){
18393         var ns = this.nodes;
18394         start = start || 0;
18395         end = typeof end == "undefined" ? ns.length - 1 : end;
18396         var nodes = [];
18397         if(start <= end){
18398             for(var i = start; i <= end; i++){
18399                 nodes.push(ns[i]);
18400             }
18401         } else{
18402             for(var i = start; i >= end; i--){
18403                 nodes.push(ns[i]);
18404             }
18405         }
18406         return nodes;
18407     },
18408
18409     /**
18410      * Finds the index of the passed node
18411      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18412      * @return {Number} The index of the node or -1
18413      */
18414     indexOf : function(node){
18415         node = this.getNode(node);
18416         if(typeof node.nodeIndex == "number"){
18417             return node.nodeIndex;
18418         }
18419         var ns = this.nodes;
18420         for(var i = 0, len = ns.length; i < len; i++){
18421             if(ns[i] == node){
18422                 return i;
18423             }
18424         }
18425         return -1;
18426     }
18427 });
18428 /*
18429  * - LGPL
18430  *
18431  * based on jquery fullcalendar
18432  * 
18433  */
18434
18435 Roo.bootstrap = Roo.bootstrap || {};
18436 /**
18437  * @class Roo.bootstrap.Calendar
18438  * @extends Roo.bootstrap.Component
18439  * Bootstrap Calendar class
18440  * @cfg {Boolean} loadMask (true|false) default false
18441  * @cfg {Object} header generate the user specific header of the calendar, default false
18442
18443  * @constructor
18444  * Create a new Container
18445  * @param {Object} config The config object
18446  */
18447
18448
18449
18450 Roo.bootstrap.Calendar = function(config){
18451     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18452      this.addEvents({
18453         /**
18454              * @event select
18455              * Fires when a date is selected
18456              * @param {DatePicker} this
18457              * @param {Date} date The selected date
18458              */
18459         'select': true,
18460         /**
18461              * @event monthchange
18462              * Fires when the displayed month changes 
18463              * @param {DatePicker} this
18464              * @param {Date} date The selected month
18465              */
18466         'monthchange': true,
18467         /**
18468              * @event evententer
18469              * Fires when mouse over an event
18470              * @param {Calendar} this
18471              * @param {event} Event
18472              */
18473         'evententer': true,
18474         /**
18475              * @event eventleave
18476              * Fires when the mouse leaves an
18477              * @param {Calendar} this
18478              * @param {event}
18479              */
18480         'eventleave': true,
18481         /**
18482              * @event eventclick
18483              * Fires when the mouse click an
18484              * @param {Calendar} this
18485              * @param {event}
18486              */
18487         'eventclick': true
18488         
18489     });
18490
18491 };
18492
18493 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
18494     
18495      /**
18496      * @cfg {Number} startDay
18497      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18498      */
18499     startDay : 0,
18500     
18501     loadMask : false,
18502     
18503     header : false,
18504       
18505     getAutoCreate : function(){
18506         
18507         
18508         var fc_button = function(name, corner, style, content ) {
18509             return Roo.apply({},{
18510                 tag : 'span',
18511                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
18512                          (corner.length ?
18513                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18514                             ''
18515                         ),
18516                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18517                 unselectable: 'on'
18518             });
18519         };
18520         
18521         var header = {};
18522         
18523         if(!this.header){
18524             header = {
18525                 tag : 'table',
18526                 cls : 'fc-header',
18527                 style : 'width:100%',
18528                 cn : [
18529                     {
18530                         tag: 'tr',
18531                         cn : [
18532                             {
18533                                 tag : 'td',
18534                                 cls : 'fc-header-left',
18535                                 cn : [
18536                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
18537                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
18538                                     { tag: 'span', cls: 'fc-header-space' },
18539                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
18540
18541
18542                                 ]
18543                             },
18544
18545                             {
18546                                 tag : 'td',
18547                                 cls : 'fc-header-center',
18548                                 cn : [
18549                                     {
18550                                         tag: 'span',
18551                                         cls: 'fc-header-title',
18552                                         cn : {
18553                                             tag: 'H2',
18554                                             html : 'month / year'
18555                                         }
18556                                     }
18557
18558                                 ]
18559                             },
18560                             {
18561                                 tag : 'td',
18562                                 cls : 'fc-header-right',
18563                                 cn : [
18564                               /*      fc_button('month', 'left', '', 'month' ),
18565                                     fc_button('week', '', '', 'week' ),
18566                                     fc_button('day', 'right', '', 'day' )
18567                                 */    
18568
18569                                 ]
18570                             }
18571
18572                         ]
18573                     }
18574                 ]
18575             };
18576         }
18577         
18578         header = this.header;
18579         
18580        
18581         var cal_heads = function() {
18582             var ret = [];
18583             // fixme - handle this.
18584             
18585             for (var i =0; i < Date.dayNames.length; i++) {
18586                 var d = Date.dayNames[i];
18587                 ret.push({
18588                     tag: 'th',
18589                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18590                     html : d.substring(0,3)
18591                 });
18592                 
18593             }
18594             ret[0].cls += ' fc-first';
18595             ret[6].cls += ' fc-last';
18596             return ret;
18597         };
18598         var cal_cell = function(n) {
18599             return  {
18600                 tag: 'td',
18601                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18602                 cn : [
18603                     {
18604                         cn : [
18605                             {
18606                                 cls: 'fc-day-number',
18607                                 html: 'D'
18608                             },
18609                             {
18610                                 cls: 'fc-day-content',
18611                              
18612                                 cn : [
18613                                      {
18614                                         style: 'position: relative;' // height: 17px;
18615                                     }
18616                                 ]
18617                             }
18618                             
18619                             
18620                         ]
18621                     }
18622                 ]
18623                 
18624             }
18625         };
18626         var cal_rows = function() {
18627             
18628             var ret = [];
18629             for (var r = 0; r < 6; r++) {
18630                 var row= {
18631                     tag : 'tr',
18632                     cls : 'fc-week',
18633                     cn : []
18634                 };
18635                 
18636                 for (var i =0; i < Date.dayNames.length; i++) {
18637                     var d = Date.dayNames[i];
18638                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18639
18640                 }
18641                 row.cn[0].cls+=' fc-first';
18642                 row.cn[0].cn[0].style = 'min-height:90px';
18643                 row.cn[6].cls+=' fc-last';
18644                 ret.push(row);
18645                 
18646             }
18647             ret[0].cls += ' fc-first';
18648             ret[4].cls += ' fc-prev-last';
18649             ret[5].cls += ' fc-last';
18650             return ret;
18651             
18652         };
18653         
18654         var cal_table = {
18655             tag: 'table',
18656             cls: 'fc-border-separate',
18657             style : 'width:100%',
18658             cellspacing  : 0,
18659             cn : [
18660                 { 
18661                     tag: 'thead',
18662                     cn : [
18663                         { 
18664                             tag: 'tr',
18665                             cls : 'fc-first fc-last',
18666                             cn : cal_heads()
18667                         }
18668                     ]
18669                 },
18670                 { 
18671                     tag: 'tbody',
18672                     cn : cal_rows()
18673                 }
18674                   
18675             ]
18676         };
18677          
18678          var cfg = {
18679             cls : 'fc fc-ltr',
18680             cn : [
18681                 header,
18682                 {
18683                     cls : 'fc-content',
18684                     style : "position: relative;",
18685                     cn : [
18686                         {
18687                             cls : 'fc-view fc-view-month fc-grid',
18688                             style : 'position: relative',
18689                             unselectable : 'on',
18690                             cn : [
18691                                 {
18692                                     cls : 'fc-event-container',
18693                                     style : 'position:absolute;z-index:8;top:0;left:0;'
18694                                 },
18695                                 cal_table
18696                             ]
18697                         }
18698                     ]
18699     
18700                 }
18701            ] 
18702             
18703         };
18704         
18705          
18706         
18707         return cfg;
18708     },
18709     
18710     
18711     initEvents : function()
18712     {
18713         if(!this.store){
18714             throw "can not find store for calendar";
18715         }
18716         
18717         var mark = {
18718             tag: "div",
18719             cls:"x-dlg-mask",
18720             style: "text-align:center",
18721             cn: [
18722                 {
18723                     tag: "div",
18724                     style: "background-color:white;width:50%;margin:250 auto",
18725                     cn: [
18726                         {
18727                             tag: "img",
18728                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
18729                         },
18730                         {
18731                             tag: "span",
18732                             html: "Loading"
18733                         }
18734                         
18735                     ]
18736                 }
18737             ]
18738         };
18739         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
18740         
18741         var size = this.el.select('.fc-content', true).first().getSize();
18742         this.maskEl.setSize(size.width, size.height);
18743         this.maskEl.enableDisplayMode("block");
18744         if(!this.loadMask){
18745             this.maskEl.hide();
18746         }
18747         
18748         this.store = Roo.factory(this.store, Roo.data);
18749         this.store.on('load', this.onLoad, this);
18750         this.store.on('beforeload', this.onBeforeLoad, this);
18751         
18752         this.resize();
18753         
18754         this.cells = this.el.select('.fc-day',true);
18755         //Roo.log(this.cells);
18756         this.textNodes = this.el.query('.fc-day-number');
18757         this.cells.addClassOnOver('fc-state-hover');
18758         
18759         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
18760         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
18761         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
18762         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
18763         
18764         this.on('monthchange', this.onMonthChange, this);
18765         
18766         this.update(new Date().clearTime());
18767     },
18768     
18769     resize : function() {
18770         var sz  = this.el.getSize();
18771         
18772         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
18773         this.el.select('.fc-day-content div',true).setHeight(34);
18774     },
18775     
18776     
18777     // private
18778     showPrevMonth : function(e){
18779         this.update(this.activeDate.add("mo", -1));
18780     },
18781     showToday : function(e){
18782         this.update(new Date().clearTime());
18783     },
18784     // private
18785     showNextMonth : function(e){
18786         this.update(this.activeDate.add("mo", 1));
18787     },
18788
18789     // private
18790     showPrevYear : function(){
18791         this.update(this.activeDate.add("y", -1));
18792     },
18793
18794     // private
18795     showNextYear : function(){
18796         this.update(this.activeDate.add("y", 1));
18797     },
18798
18799     
18800    // private
18801     update : function(date)
18802     {
18803         var vd = this.activeDate;
18804         this.activeDate = date;
18805 //        if(vd && this.el){
18806 //            var t = date.getTime();
18807 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
18808 //                Roo.log('using add remove');
18809 //                
18810 //                this.fireEvent('monthchange', this, date);
18811 //                
18812 //                this.cells.removeClass("fc-state-highlight");
18813 //                this.cells.each(function(c){
18814 //                   if(c.dateValue == t){
18815 //                       c.addClass("fc-state-highlight");
18816 //                       setTimeout(function(){
18817 //                            try{c.dom.firstChild.focus();}catch(e){}
18818 //                       }, 50);
18819 //                       return false;
18820 //                   }
18821 //                   return true;
18822 //                });
18823 //                return;
18824 //            }
18825 //        }
18826         
18827         var days = date.getDaysInMonth();
18828         
18829         var firstOfMonth = date.getFirstDateOfMonth();
18830         var startingPos = firstOfMonth.getDay()-this.startDay;
18831         
18832         if(startingPos < this.startDay){
18833             startingPos += 7;
18834         }
18835         
18836         var pm = date.add(Date.MONTH, -1);
18837         var prevStart = pm.getDaysInMonth()-startingPos;
18838 //        
18839         this.cells = this.el.select('.fc-day',true);
18840         this.textNodes = this.el.query('.fc-day-number');
18841         this.cells.addClassOnOver('fc-state-hover');
18842         
18843         var cells = this.cells.elements;
18844         var textEls = this.textNodes;
18845         
18846         Roo.each(cells, function(cell){
18847             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
18848         });
18849         
18850         days += startingPos;
18851
18852         // convert everything to numbers so it's fast
18853         var day = 86400000;
18854         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
18855         //Roo.log(d);
18856         //Roo.log(pm);
18857         //Roo.log(prevStart);
18858         
18859         var today = new Date().clearTime().getTime();
18860         var sel = date.clearTime().getTime();
18861         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
18862         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
18863         var ddMatch = this.disabledDatesRE;
18864         var ddText = this.disabledDatesText;
18865         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
18866         var ddaysText = this.disabledDaysText;
18867         var format = this.format;
18868         
18869         var setCellClass = function(cal, cell){
18870             cell.row = 0;
18871             cell.events = [];
18872             cell.more = [];
18873             //Roo.log('set Cell Class');
18874             cell.title = "";
18875             var t = d.getTime();
18876             
18877             //Roo.log(d);
18878             
18879             cell.dateValue = t;
18880             if(t == today){
18881                 cell.className += " fc-today";
18882                 cell.className += " fc-state-highlight";
18883                 cell.title = cal.todayText;
18884             }
18885             if(t == sel){
18886                 // disable highlight in other month..
18887                 //cell.className += " fc-state-highlight";
18888                 
18889             }
18890             // disabling
18891             if(t < min) {
18892                 cell.className = " fc-state-disabled";
18893                 cell.title = cal.minText;
18894                 return;
18895             }
18896             if(t > max) {
18897                 cell.className = " fc-state-disabled";
18898                 cell.title = cal.maxText;
18899                 return;
18900             }
18901             if(ddays){
18902                 if(ddays.indexOf(d.getDay()) != -1){
18903                     cell.title = ddaysText;
18904                     cell.className = " fc-state-disabled";
18905                 }
18906             }
18907             if(ddMatch && format){
18908                 var fvalue = d.dateFormat(format);
18909                 if(ddMatch.test(fvalue)){
18910                     cell.title = ddText.replace("%0", fvalue);
18911                     cell.className = " fc-state-disabled";
18912                 }
18913             }
18914             
18915             if (!cell.initialClassName) {
18916                 cell.initialClassName = cell.dom.className;
18917             }
18918             
18919             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
18920         };
18921
18922         var i = 0;
18923         
18924         for(; i < startingPos; i++) {
18925             textEls[i].innerHTML = (++prevStart);
18926             d.setDate(d.getDate()+1);
18927             
18928             cells[i].className = "fc-past fc-other-month";
18929             setCellClass(this, cells[i]);
18930         }
18931         
18932         var intDay = 0;
18933         
18934         for(; i < days; i++){
18935             intDay = i - startingPos + 1;
18936             textEls[i].innerHTML = (intDay);
18937             d.setDate(d.getDate()+1);
18938             
18939             cells[i].className = ''; // "x-date-active";
18940             setCellClass(this, cells[i]);
18941         }
18942         var extraDays = 0;
18943         
18944         for(; i < 42; i++) {
18945             textEls[i].innerHTML = (++extraDays);
18946             d.setDate(d.getDate()+1);
18947             
18948             cells[i].className = "fc-future fc-other-month";
18949             setCellClass(this, cells[i]);
18950         }
18951         
18952         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
18953         
18954         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
18955         
18956         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
18957         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
18958         
18959         if(totalRows != 6){
18960             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
18961             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
18962         }
18963         
18964         this.fireEvent('monthchange', this, date);
18965         
18966         
18967         /*
18968         if(!this.internalRender){
18969             var main = this.el.dom.firstChild;
18970             var w = main.offsetWidth;
18971             this.el.setWidth(w + this.el.getBorderWidth("lr"));
18972             Roo.fly(main).setWidth(w);
18973             this.internalRender = true;
18974             // opera does not respect the auto grow header center column
18975             // then, after it gets a width opera refuses to recalculate
18976             // without a second pass
18977             if(Roo.isOpera && !this.secondPass){
18978                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
18979                 this.secondPass = true;
18980                 this.update.defer(10, this, [date]);
18981             }
18982         }
18983         */
18984         
18985     },
18986     
18987     findCell : function(dt) {
18988         dt = dt.clearTime().getTime();
18989         var ret = false;
18990         this.cells.each(function(c){
18991             //Roo.log("check " +c.dateValue + '?=' + dt);
18992             if(c.dateValue == dt){
18993                 ret = c;
18994                 return false;
18995             }
18996             return true;
18997         });
18998         
18999         return ret;
19000     },
19001     
19002     findCells : function(ev) {
19003         var s = ev.start.clone().clearTime().getTime();
19004        // Roo.log(s);
19005         var e= ev.end.clone().clearTime().getTime();
19006        // Roo.log(e);
19007         var ret = [];
19008         this.cells.each(function(c){
19009              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19010             
19011             if(c.dateValue > e){
19012                 return ;
19013             }
19014             if(c.dateValue < s){
19015                 return ;
19016             }
19017             ret.push(c);
19018         });
19019         
19020         return ret;    
19021     },
19022     
19023 //    findBestRow: function(cells)
19024 //    {
19025 //        var ret = 0;
19026 //        
19027 //        for (var i =0 ; i < cells.length;i++) {
19028 //            ret  = Math.max(cells[i].rows || 0,ret);
19029 //        }
19030 //        return ret;
19031 //        
19032 //    },
19033     
19034     
19035     addItem : function(ev)
19036     {
19037         // look for vertical location slot in
19038         var cells = this.findCells(ev);
19039         
19040 //        ev.row = this.findBestRow(cells);
19041         
19042         // work out the location.
19043         
19044         var crow = false;
19045         var rows = [];
19046         for(var i =0; i < cells.length; i++) {
19047             
19048             cells[i].row = cells[0].row;
19049             
19050             if(i == 0){
19051                 cells[i].row = cells[i].row + 1;
19052             }
19053             
19054             if (!crow) {
19055                 crow = {
19056                     start : cells[i],
19057                     end :  cells[i]
19058                 };
19059                 continue;
19060             }
19061             if (crow.start.getY() == cells[i].getY()) {
19062                 // on same row.
19063                 crow.end = cells[i];
19064                 continue;
19065             }
19066             // different row.
19067             rows.push(crow);
19068             crow = {
19069                 start: cells[i],
19070                 end : cells[i]
19071             };
19072             
19073         }
19074         
19075         rows.push(crow);
19076         ev.els = [];
19077         ev.rows = rows;
19078         ev.cells = cells;
19079         
19080         cells[0].events.push(ev);
19081         
19082         this.calevents.push(ev);
19083     },
19084     
19085     clearEvents: function() {
19086         
19087         if(!this.calevents){
19088             return;
19089         }
19090         
19091         Roo.each(this.cells.elements, function(c){
19092             c.row = 0;
19093             c.events = [];
19094             c.more = [];
19095         });
19096         
19097         Roo.each(this.calevents, function(e) {
19098             Roo.each(e.els, function(el) {
19099                 el.un('mouseenter' ,this.onEventEnter, this);
19100                 el.un('mouseleave' ,this.onEventLeave, this);
19101                 el.remove();
19102             },this);
19103         },this);
19104         
19105         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19106             e.remove();
19107         });
19108         
19109     },
19110     
19111     renderEvents: function()
19112     {   
19113         var _this = this;
19114         
19115         this.cells.each(function(c) {
19116             
19117             if(c.row < 5){
19118                 return;
19119             }
19120             
19121             var ev = c.events;
19122             
19123             var r = 4;
19124             if(c.row != c.events.length){
19125                 r = 4 - (4 - (c.row - c.events.length));
19126             }
19127             
19128             c.events = ev.slice(0, r);
19129             c.more = ev.slice(r);
19130             
19131             if(c.more.length && c.more.length == 1){
19132                 c.events.push(c.more.pop());
19133             }
19134             
19135             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19136             
19137         });
19138             
19139         this.cells.each(function(c) {
19140             
19141             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19142             
19143             
19144             for (var e = 0; e < c.events.length; e++){
19145                 var ev = c.events[e];
19146                 var rows = ev.rows;
19147                 
19148                 for(var i = 0; i < rows.length; i++) {
19149                 
19150                     // how many rows should it span..
19151
19152                     var  cfg = {
19153                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19154                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19155
19156                         unselectable : "on",
19157                         cn : [
19158                             {
19159                                 cls: 'fc-event-inner',
19160                                 cn : [
19161     //                                {
19162     //                                  tag:'span',
19163     //                                  cls: 'fc-event-time',
19164     //                                  html : cells.length > 1 ? '' : ev.time
19165     //                                },
19166                                     {
19167                                       tag:'span',
19168                                       cls: 'fc-event-title',
19169                                       html : String.format('{0}', ev.title)
19170                                     }
19171
19172
19173                                 ]
19174                             },
19175                             {
19176                                 cls: 'ui-resizable-handle ui-resizable-e',
19177                                 html : '&nbsp;&nbsp;&nbsp'
19178                             }
19179
19180                         ]
19181                     };
19182
19183                     if (i == 0) {
19184                         cfg.cls += ' fc-event-start';
19185                     }
19186                     if ((i+1) == rows.length) {
19187                         cfg.cls += ' fc-event-end';
19188                     }
19189
19190                     var ctr = _this.el.select('.fc-event-container',true).first();
19191                     var cg = ctr.createChild(cfg);
19192
19193                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19194                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19195
19196                     var r = (c.more.length) ? 1 : 0;
19197                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19198                     cg.setWidth(ebox.right - sbox.x -2);
19199
19200                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19201                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19202                     cg.on('click', _this.onEventClick, _this, ev);
19203
19204                     ev.els.push(cg);
19205                     
19206                 }
19207                 
19208             }
19209             
19210             
19211             if(c.more.length){
19212                 var  cfg = {
19213                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19214                     style : 'position: absolute',
19215                     unselectable : "on",
19216                     cn : [
19217                         {
19218                             cls: 'fc-event-inner',
19219                             cn : [
19220                                 {
19221                                   tag:'span',
19222                                   cls: 'fc-event-title',
19223                                   html : 'More'
19224                                 }
19225
19226
19227                             ]
19228                         },
19229                         {
19230                             cls: 'ui-resizable-handle ui-resizable-e',
19231                             html : '&nbsp;&nbsp;&nbsp'
19232                         }
19233
19234                     ]
19235                 };
19236
19237                 var ctr = _this.el.select('.fc-event-container',true).first();
19238                 var cg = ctr.createChild(cfg);
19239
19240                 var sbox = c.select('.fc-day-content',true).first().getBox();
19241                 var ebox = c.select('.fc-day-content',true).first().getBox();
19242                 //Roo.log(cg);
19243                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19244                 cg.setWidth(ebox.right - sbox.x -2);
19245
19246                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19247                 
19248             }
19249             
19250         });
19251         
19252         
19253         
19254     },
19255     
19256     onEventEnter: function (e, el,event,d) {
19257         this.fireEvent('evententer', this, el, event);
19258     },
19259     
19260     onEventLeave: function (e, el,event,d) {
19261         this.fireEvent('eventleave', this, el, event);
19262     },
19263     
19264     onEventClick: function (e, el,event,d) {
19265         this.fireEvent('eventclick', this, el, event);
19266     },
19267     
19268     onMonthChange: function () {
19269         this.store.load();
19270     },
19271     
19272     onMoreEventClick: function(e, el, more)
19273     {
19274         var _this = this;
19275         
19276         this.calpopover.placement = 'right';
19277         this.calpopover.setTitle('More');
19278         
19279         this.calpopover.setContent('');
19280         
19281         var ctr = this.calpopover.el.select('.popover-content', true).first();
19282         
19283         Roo.each(more, function(m){
19284             var cfg = {
19285                 cls : 'fc-event-hori fc-event-draggable',
19286                 html : m.title
19287             };
19288             var cg = ctr.createChild(cfg);
19289             
19290             cg.on('click', _this.onEventClick, _this, m);
19291         });
19292         
19293         this.calpopover.show(el);
19294         
19295         
19296     },
19297     
19298     onLoad: function () 
19299     {   
19300         this.calevents = [];
19301         var cal = this;
19302         
19303         if(this.store.getCount() > 0){
19304             this.store.data.each(function(d){
19305                cal.addItem({
19306                     id : d.data.id,
19307                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19308                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19309                     time : d.data.start_time,
19310                     title : d.data.title,
19311                     description : d.data.description,
19312                     venue : d.data.venue
19313                 });
19314             });
19315         }
19316         
19317         this.renderEvents();
19318         
19319         if(this.calevents.length && this.loadMask){
19320             this.maskEl.hide();
19321         }
19322     },
19323     
19324     onBeforeLoad: function()
19325     {
19326         this.clearEvents();
19327         if(this.loadMask){
19328             this.maskEl.show();
19329         }
19330     }
19331 });
19332
19333  
19334  /*
19335  * - LGPL
19336  *
19337  * element
19338  * 
19339  */
19340
19341 /**
19342  * @class Roo.bootstrap.Popover
19343  * @extends Roo.bootstrap.Component
19344  * Bootstrap Popover class
19345  * @cfg {String} html contents of the popover   (or false to use children..)
19346  * @cfg {String} title of popover (or false to hide)
19347  * @cfg {String} placement how it is placed
19348  * @cfg {String} trigger click || hover (or false to trigger manually)
19349  * @cfg {String} over what (parent or false to trigger manually.)
19350  * @cfg {Number} delay - delay before showing
19351  
19352  * @constructor
19353  * Create a new Popover
19354  * @param {Object} config The config object
19355  */
19356
19357 Roo.bootstrap.Popover = function(config){
19358     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19359     
19360     this.addEvents({
19361         // raw events
19362          /**
19363          * @event show
19364          * After the popover show
19365          * 
19366          * @param {Roo.bootstrap.Popover} this
19367          */
19368         "show" : true,
19369         /**
19370          * @event hide
19371          * After the popover hide
19372          * 
19373          * @param {Roo.bootstrap.Popover} this
19374          */
19375         "hide" : true
19376     });
19377 };
19378
19379 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
19380     
19381     title: 'Fill in a title',
19382     html: false,
19383     
19384     placement : 'right',
19385     trigger : 'hover', // hover
19386     
19387     delay : 0,
19388     
19389     over: 'parent',
19390     
19391     can_build_overlaid : false,
19392     
19393     getChildContainer : function()
19394     {
19395         return this.el.select('.popover-content',true).first();
19396     },
19397     
19398     getAutoCreate : function(){
19399          
19400         var cfg = {
19401            cls : 'popover roo-dynamic',
19402            style: 'display:block',
19403            cn : [
19404                 {
19405                     cls : 'arrow'
19406                 },
19407                 {
19408                     cls : 'popover-inner',
19409                     cn : [
19410                         {
19411                             tag: 'h3',
19412                             cls: 'popover-title popover-header',
19413                             html : this.title
19414                         },
19415                         {
19416                             cls : 'popover-content popover-body',
19417                             html : this.html
19418                         }
19419                     ]
19420                     
19421                 }
19422            ]
19423         };
19424         
19425         return cfg;
19426     },
19427     setTitle: function(str)
19428     {
19429         this.title = str;
19430         this.el.select('.popover-title',true).first().dom.innerHTML = str;
19431     },
19432     setContent: function(str)
19433     {
19434         this.html = str;
19435         this.el.select('.popover-content',true).first().dom.innerHTML = str;
19436     },
19437     // as it get's added to the bottom of the page.
19438     onRender : function(ct, position)
19439     {
19440         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19441         if(!this.el){
19442             var cfg = Roo.apply({},  this.getAutoCreate());
19443             cfg.id = Roo.id();
19444             
19445             if (this.cls) {
19446                 cfg.cls += ' ' + this.cls;
19447             }
19448             if (this.style) {
19449                 cfg.style = this.style;
19450             }
19451             //Roo.log("adding to ");
19452             this.el = Roo.get(document.body).createChild(cfg, position);
19453 //            Roo.log(this.el);
19454         }
19455         this.initEvents();
19456     },
19457     
19458     initEvents : function()
19459     {
19460         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
19461         this.el.enableDisplayMode('block');
19462         this.el.hide();
19463         if (this.over === false) {
19464             return; 
19465         }
19466         if (this.triggers === false) {
19467             return;
19468         }
19469         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
19470         var triggers = this.trigger ? this.trigger.split(' ') : [];
19471         Roo.each(triggers, function(trigger) {
19472         
19473             if (trigger == 'click') {
19474                 on_el.on('click', this.toggle, this);
19475             } else if (trigger != 'manual') {
19476                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
19477                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19478       
19479                 on_el.on(eventIn  ,this.enter, this);
19480                 on_el.on(eventOut, this.leave, this);
19481             }
19482         }, this);
19483         
19484     },
19485     
19486     
19487     // private
19488     timeout : null,
19489     hoverState : null,
19490     
19491     toggle : function () {
19492         this.hoverState == 'in' ? this.leave() : this.enter();
19493     },
19494     
19495     enter : function () {
19496         
19497         clearTimeout(this.timeout);
19498     
19499         this.hoverState = 'in';
19500     
19501         if (!this.delay || !this.delay.show) {
19502             this.show();
19503             return;
19504         }
19505         var _t = this;
19506         this.timeout = setTimeout(function () {
19507             if (_t.hoverState == 'in') {
19508                 _t.show();
19509             }
19510         }, this.delay.show)
19511     },
19512     
19513     leave : function() {
19514         clearTimeout(this.timeout);
19515     
19516         this.hoverState = 'out';
19517     
19518         if (!this.delay || !this.delay.hide) {
19519             this.hide();
19520             return;
19521         }
19522         var _t = this;
19523         this.timeout = setTimeout(function () {
19524             if (_t.hoverState == 'out') {
19525                 _t.hide();
19526             }
19527         }, this.delay.hide)
19528     },
19529     
19530     show : function (on_el)
19531     {
19532         if (!on_el) {
19533             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
19534         }
19535         
19536         // set content.
19537         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
19538         if (this.html !== false) {
19539             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
19540         }
19541         this.el.removeClass([
19542             'fade','top','bottom', 'left', 'right','in',
19543             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19544         ]);
19545         if (!this.title.length) {
19546             this.el.select('.popover-title',true).hide();
19547         }
19548         
19549         var placement = typeof this.placement == 'function' ?
19550             this.placement.call(this, this.el, on_el) :
19551             this.placement;
19552             
19553         var autoToken = /\s?auto?\s?/i;
19554         var autoPlace = autoToken.test(placement);
19555         if (autoPlace) {
19556             placement = placement.replace(autoToken, '') || 'top';
19557         }
19558         
19559         //this.el.detach()
19560         //this.el.setXY([0,0]);
19561         this.el.show();
19562         this.el.dom.style.display='block';
19563         this.el.addClass(placement);
19564         
19565         //this.el.appendTo(on_el);
19566         
19567         var p = this.getPosition();
19568         var box = this.el.getBox();
19569         
19570         if (autoPlace) {
19571             // fixme..
19572         }
19573         var align = Roo.bootstrap.Popover.alignment[placement];
19574         
19575 //        Roo.log(align);
19576         this.el.alignTo(on_el, align[0],align[1]);
19577         //var arrow = this.el.select('.arrow',true).first();
19578         //arrow.set(align[2], 
19579         
19580         this.el.addClass('in');
19581         
19582         
19583         if (this.el.hasClass('fade')) {
19584             // fade it?
19585         }
19586         
19587         this.hoverState = 'in';
19588         
19589         this.fireEvent('show', this);
19590         
19591     },
19592     hide : function()
19593     {
19594         this.el.setXY([0,0]);
19595         this.el.removeClass('in');
19596         this.el.hide();
19597         this.hoverState = null;
19598         
19599         this.fireEvent('hide', this);
19600     }
19601     
19602 });
19603
19604 Roo.bootstrap.Popover.alignment = {
19605     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
19606     'right' : ['l-r', [10,0], 'left bs-popover-left'],
19607     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
19608     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
19609 };
19610
19611  /*
19612  * - LGPL
19613  *
19614  * Progress
19615  * 
19616  */
19617
19618 /**
19619  * @class Roo.bootstrap.Progress
19620  * @extends Roo.bootstrap.Component
19621  * Bootstrap Progress class
19622  * @cfg {Boolean} striped striped of the progress bar
19623  * @cfg {Boolean} active animated of the progress bar
19624  * 
19625  * 
19626  * @constructor
19627  * Create a new Progress
19628  * @param {Object} config The config object
19629  */
19630
19631 Roo.bootstrap.Progress = function(config){
19632     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
19633 };
19634
19635 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
19636     
19637     striped : false,
19638     active: false,
19639     
19640     getAutoCreate : function(){
19641         var cfg = {
19642             tag: 'div',
19643             cls: 'progress'
19644         };
19645         
19646         
19647         if(this.striped){
19648             cfg.cls += ' progress-striped';
19649         }
19650       
19651         if(this.active){
19652             cfg.cls += ' active';
19653         }
19654         
19655         
19656         return cfg;
19657     }
19658    
19659 });
19660
19661  
19662
19663  /*
19664  * - LGPL
19665  *
19666  * ProgressBar
19667  * 
19668  */
19669
19670 /**
19671  * @class Roo.bootstrap.ProgressBar
19672  * @extends Roo.bootstrap.Component
19673  * Bootstrap ProgressBar class
19674  * @cfg {Number} aria_valuenow aria-value now
19675  * @cfg {Number} aria_valuemin aria-value min
19676  * @cfg {Number} aria_valuemax aria-value max
19677  * @cfg {String} label label for the progress bar
19678  * @cfg {String} panel (success | info | warning | danger )
19679  * @cfg {String} role role of the progress bar
19680  * @cfg {String} sr_only text
19681  * 
19682  * 
19683  * @constructor
19684  * Create a new ProgressBar
19685  * @param {Object} config The config object
19686  */
19687
19688 Roo.bootstrap.ProgressBar = function(config){
19689     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
19690 };
19691
19692 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
19693     
19694     aria_valuenow : 0,
19695     aria_valuemin : 0,
19696     aria_valuemax : 100,
19697     label : false,
19698     panel : false,
19699     role : false,
19700     sr_only: false,
19701     
19702     getAutoCreate : function()
19703     {
19704         
19705         var cfg = {
19706             tag: 'div',
19707             cls: 'progress-bar',
19708             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
19709         };
19710         
19711         if(this.sr_only){
19712             cfg.cn = {
19713                 tag: 'span',
19714                 cls: 'sr-only',
19715                 html: this.sr_only
19716             }
19717         }
19718         
19719         if(this.role){
19720             cfg.role = this.role;
19721         }
19722         
19723         if(this.aria_valuenow){
19724             cfg['aria-valuenow'] = this.aria_valuenow;
19725         }
19726         
19727         if(this.aria_valuemin){
19728             cfg['aria-valuemin'] = this.aria_valuemin;
19729         }
19730         
19731         if(this.aria_valuemax){
19732             cfg['aria-valuemax'] = this.aria_valuemax;
19733         }
19734         
19735         if(this.label && !this.sr_only){
19736             cfg.html = this.label;
19737         }
19738         
19739         if(this.panel){
19740             cfg.cls += ' progress-bar-' + this.panel;
19741         }
19742         
19743         return cfg;
19744     },
19745     
19746     update : function(aria_valuenow)
19747     {
19748         this.aria_valuenow = aria_valuenow;
19749         
19750         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
19751     }
19752    
19753 });
19754
19755  
19756
19757  /*
19758  * - LGPL
19759  *
19760  * column
19761  * 
19762  */
19763
19764 /**
19765  * @class Roo.bootstrap.TabGroup
19766  * @extends Roo.bootstrap.Column
19767  * Bootstrap Column class
19768  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
19769  * @cfg {Boolean} carousel true to make the group behave like a carousel
19770  * @cfg {Boolean} bullets show bullets for the panels
19771  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
19772  * @cfg {Number} timer auto slide timer .. default 0 millisecond
19773  * @cfg {Boolean} showarrow (true|false) show arrow default true
19774  * 
19775  * @constructor
19776  * Create a new TabGroup
19777  * @param {Object} config The config object
19778  */
19779
19780 Roo.bootstrap.TabGroup = function(config){
19781     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
19782     if (!this.navId) {
19783         this.navId = Roo.id();
19784     }
19785     this.tabs = [];
19786     Roo.bootstrap.TabGroup.register(this);
19787     
19788 };
19789
19790 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
19791     
19792     carousel : false,
19793     transition : false,
19794     bullets : 0,
19795     timer : 0,
19796     autoslide : false,
19797     slideFn : false,
19798     slideOnTouch : false,
19799     showarrow : true,
19800     
19801     getAutoCreate : function()
19802     {
19803         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
19804         
19805         cfg.cls += ' tab-content';
19806         
19807         if (this.carousel) {
19808             cfg.cls += ' carousel slide';
19809             
19810             cfg.cn = [{
19811                cls : 'carousel-inner',
19812                cn : []
19813             }];
19814         
19815             if(this.bullets  && !Roo.isTouch){
19816                 
19817                 var bullets = {
19818                     cls : 'carousel-bullets',
19819                     cn : []
19820                 };
19821                
19822                 if(this.bullets_cls){
19823                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
19824                 }
19825                 
19826                 bullets.cn.push({
19827                     cls : 'clear'
19828                 });
19829                 
19830                 cfg.cn[0].cn.push(bullets);
19831             }
19832             
19833             if(this.showarrow){
19834                 cfg.cn[0].cn.push({
19835                     tag : 'div',
19836                     class : 'carousel-arrow',
19837                     cn : [
19838                         {
19839                             tag : 'div',
19840                             class : 'carousel-prev',
19841                             cn : [
19842                                 {
19843                                     tag : 'i',
19844                                     class : 'fa fa-chevron-left'
19845                                 }
19846                             ]
19847                         },
19848                         {
19849                             tag : 'div',
19850                             class : 'carousel-next',
19851                             cn : [
19852                                 {
19853                                     tag : 'i',
19854                                     class : 'fa fa-chevron-right'
19855                                 }
19856                             ]
19857                         }
19858                     ]
19859                 });
19860             }
19861             
19862         }
19863         
19864         return cfg;
19865     },
19866     
19867     initEvents:  function()
19868     {
19869 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
19870 //            this.el.on("touchstart", this.onTouchStart, this);
19871 //        }
19872         
19873         if(this.autoslide){
19874             var _this = this;
19875             
19876             this.slideFn = window.setInterval(function() {
19877                 _this.showPanelNext();
19878             }, this.timer);
19879         }
19880         
19881         if(this.showarrow){
19882             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
19883             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
19884         }
19885         
19886         
19887     },
19888     
19889 //    onTouchStart : function(e, el, o)
19890 //    {
19891 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
19892 //            return;
19893 //        }
19894 //        
19895 //        this.showPanelNext();
19896 //    },
19897     
19898     
19899     getChildContainer : function()
19900     {
19901         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
19902     },
19903     
19904     /**
19905     * register a Navigation item
19906     * @param {Roo.bootstrap.NavItem} the navitem to add
19907     */
19908     register : function(item)
19909     {
19910         this.tabs.push( item);
19911         item.navId = this.navId; // not really needed..
19912         this.addBullet();
19913     
19914     },
19915     
19916     getActivePanel : function()
19917     {
19918         var r = false;
19919         Roo.each(this.tabs, function(t) {
19920             if (t.active) {
19921                 r = t;
19922                 return false;
19923             }
19924             return null;
19925         });
19926         return r;
19927         
19928     },
19929     getPanelByName : function(n)
19930     {
19931         var r = false;
19932         Roo.each(this.tabs, function(t) {
19933             if (t.tabId == n) {
19934                 r = t;
19935                 return false;
19936             }
19937             return null;
19938         });
19939         return r;
19940     },
19941     indexOfPanel : function(p)
19942     {
19943         var r = false;
19944         Roo.each(this.tabs, function(t,i) {
19945             if (t.tabId == p.tabId) {
19946                 r = i;
19947                 return false;
19948             }
19949             return null;
19950         });
19951         return r;
19952     },
19953     /**
19954      * show a specific panel
19955      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
19956      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
19957      */
19958     showPanel : function (pan)
19959     {
19960         if(this.transition || typeof(pan) == 'undefined'){
19961             Roo.log("waiting for the transitionend");
19962             return false;
19963         }
19964         
19965         if (typeof(pan) == 'number') {
19966             pan = this.tabs[pan];
19967         }
19968         
19969         if (typeof(pan) == 'string') {
19970             pan = this.getPanelByName(pan);
19971         }
19972         
19973         var cur = this.getActivePanel();
19974         
19975         if(!pan || !cur){
19976             Roo.log('pan or acitve pan is undefined');
19977             return false;
19978         }
19979         
19980         if (pan.tabId == this.getActivePanel().tabId) {
19981             return true;
19982         }
19983         
19984         if (false === cur.fireEvent('beforedeactivate')) {
19985             return false;
19986         }
19987         
19988         if(this.bullets > 0 && !Roo.isTouch){
19989             this.setActiveBullet(this.indexOfPanel(pan));
19990         }
19991         
19992         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
19993             
19994             //class="carousel-item carousel-item-next carousel-item-left"
19995             
19996             this.transition = true;
19997             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
19998             var lr = dir == 'next' ? 'left' : 'right';
19999             pan.el.addClass(dir); // or prev
20000             pan.el.addClass('carousel-item-' + dir); // or prev
20001             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20002             cur.el.addClass(lr); // or right
20003             pan.el.addClass(lr);
20004             cur.el.addClass('carousel-item-' +lr); // or right
20005             pan.el.addClass('carousel-item-' +lr);
20006             
20007             
20008             var _this = this;
20009             cur.el.on('transitionend', function() {
20010                 Roo.log("trans end?");
20011                 
20012                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20013                 pan.setActive(true);
20014                 
20015                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20016                 cur.setActive(false);
20017                 
20018                 _this.transition = false;
20019                 
20020             }, this, { single:  true } );
20021             
20022             return true;
20023         }
20024         
20025         cur.setActive(false);
20026         pan.setActive(true);
20027         
20028         return true;
20029         
20030     },
20031     showPanelNext : function()
20032     {
20033         var i = this.indexOfPanel(this.getActivePanel());
20034         
20035         if (i >= this.tabs.length - 1 && !this.autoslide) {
20036             return;
20037         }
20038         
20039         if (i >= this.tabs.length - 1 && this.autoslide) {
20040             i = -1;
20041         }
20042         
20043         this.showPanel(this.tabs[i+1]);
20044     },
20045     
20046     showPanelPrev : function()
20047     {
20048         var i = this.indexOfPanel(this.getActivePanel());
20049         
20050         if (i  < 1 && !this.autoslide) {
20051             return;
20052         }
20053         
20054         if (i < 1 && this.autoslide) {
20055             i = this.tabs.length;
20056         }
20057         
20058         this.showPanel(this.tabs[i-1]);
20059     },
20060     
20061     
20062     addBullet: function()
20063     {
20064         if(!this.bullets || Roo.isTouch){
20065             return;
20066         }
20067         var ctr = this.el.select('.carousel-bullets',true).first();
20068         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20069         var bullet = ctr.createChild({
20070             cls : 'bullet bullet-' + i
20071         },ctr.dom.lastChild);
20072         
20073         
20074         var _this = this;
20075         
20076         bullet.on('click', (function(e, el, o, ii, t){
20077
20078             e.preventDefault();
20079
20080             this.showPanel(ii);
20081
20082             if(this.autoslide && this.slideFn){
20083                 clearInterval(this.slideFn);
20084                 this.slideFn = window.setInterval(function() {
20085                     _this.showPanelNext();
20086                 }, this.timer);
20087             }
20088
20089         }).createDelegate(this, [i, bullet], true));
20090                 
20091         
20092     },
20093      
20094     setActiveBullet : function(i)
20095     {
20096         if(Roo.isTouch){
20097             return;
20098         }
20099         
20100         Roo.each(this.el.select('.bullet', true).elements, function(el){
20101             el.removeClass('selected');
20102         });
20103
20104         var bullet = this.el.select('.bullet-' + i, true).first();
20105         
20106         if(!bullet){
20107             return;
20108         }
20109         
20110         bullet.addClass('selected');
20111     }
20112     
20113     
20114   
20115 });
20116
20117  
20118
20119  
20120  
20121 Roo.apply(Roo.bootstrap.TabGroup, {
20122     
20123     groups: {},
20124      /**
20125     * register a Navigation Group
20126     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20127     */
20128     register : function(navgrp)
20129     {
20130         this.groups[navgrp.navId] = navgrp;
20131         
20132     },
20133     /**
20134     * fetch a Navigation Group based on the navigation ID
20135     * if one does not exist , it will get created.
20136     * @param {string} the navgroup to add
20137     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20138     */
20139     get: function(navId) {
20140         if (typeof(this.groups[navId]) == 'undefined') {
20141             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20142         }
20143         return this.groups[navId] ;
20144     }
20145     
20146     
20147     
20148 });
20149
20150  /*
20151  * - LGPL
20152  *
20153  * TabPanel
20154  * 
20155  */
20156
20157 /**
20158  * @class Roo.bootstrap.TabPanel
20159  * @extends Roo.bootstrap.Component
20160  * Bootstrap TabPanel class
20161  * @cfg {Boolean} active panel active
20162  * @cfg {String} html panel content
20163  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20164  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20165  * @cfg {String} href click to link..
20166  * 
20167  * 
20168  * @constructor
20169  * Create a new TabPanel
20170  * @param {Object} config The config object
20171  */
20172
20173 Roo.bootstrap.TabPanel = function(config){
20174     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20175     this.addEvents({
20176         /**
20177              * @event changed
20178              * Fires when the active status changes
20179              * @param {Roo.bootstrap.TabPanel} this
20180              * @param {Boolean} state the new state
20181             
20182          */
20183         'changed': true,
20184         /**
20185              * @event beforedeactivate
20186              * Fires before a tab is de-activated - can be used to do validation on a form.
20187              * @param {Roo.bootstrap.TabPanel} this
20188              * @return {Boolean} false if there is an error
20189             
20190          */
20191         'beforedeactivate': true
20192      });
20193     
20194     this.tabId = this.tabId || Roo.id();
20195   
20196 };
20197
20198 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
20199     
20200     active: false,
20201     html: false,
20202     tabId: false,
20203     navId : false,
20204     href : '',
20205     
20206     getAutoCreate : function(){
20207         
20208         
20209         var cfg = {
20210             tag: 'div',
20211             // item is needed for carousel - not sure if it has any effect otherwise
20212             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20213             html: this.html || ''
20214         };
20215         
20216         if(this.active){
20217             cfg.cls += ' active';
20218         }
20219         
20220         if(this.tabId){
20221             cfg.tabId = this.tabId;
20222         }
20223         
20224         
20225         
20226         return cfg;
20227     },
20228     
20229     initEvents:  function()
20230     {
20231         var p = this.parent();
20232         
20233         this.navId = this.navId || p.navId;
20234         
20235         if (typeof(this.navId) != 'undefined') {
20236             // not really needed.. but just in case.. parent should be a NavGroup.
20237             var tg = Roo.bootstrap.TabGroup.get(this.navId);
20238             
20239             tg.register(this);
20240             
20241             var i = tg.tabs.length - 1;
20242             
20243             if(this.active && tg.bullets > 0 && i < tg.bullets){
20244                 tg.setActiveBullet(i);
20245             }
20246         }
20247         
20248         this.el.on('click', this.onClick, this);
20249         
20250         if(Roo.isTouch){
20251             this.el.on("touchstart", this.onTouchStart, this);
20252             this.el.on("touchmove", this.onTouchMove, this);
20253             this.el.on("touchend", this.onTouchEnd, this);
20254         }
20255         
20256     },
20257     
20258     onRender : function(ct, position)
20259     {
20260         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20261     },
20262     
20263     setActive : function(state)
20264     {
20265         Roo.log("panel - set active " + this.tabId + "=" + state);
20266         
20267         this.active = state;
20268         if (!state) {
20269             this.el.removeClass('active');
20270             
20271         } else  if (!this.el.hasClass('active')) {
20272             this.el.addClass('active');
20273         }
20274         
20275         this.fireEvent('changed', this, state);
20276     },
20277     
20278     onClick : function(e)
20279     {
20280         e.preventDefault();
20281         
20282         if(!this.href.length){
20283             return;
20284         }
20285         
20286         window.location.href = this.href;
20287     },
20288     
20289     startX : 0,
20290     startY : 0,
20291     endX : 0,
20292     endY : 0,
20293     swiping : false,
20294     
20295     onTouchStart : function(e)
20296     {
20297         this.swiping = false;
20298         
20299         this.startX = e.browserEvent.touches[0].clientX;
20300         this.startY = e.browserEvent.touches[0].clientY;
20301     },
20302     
20303     onTouchMove : function(e)
20304     {
20305         this.swiping = true;
20306         
20307         this.endX = e.browserEvent.touches[0].clientX;
20308         this.endY = e.browserEvent.touches[0].clientY;
20309     },
20310     
20311     onTouchEnd : function(e)
20312     {
20313         if(!this.swiping){
20314             this.onClick(e);
20315             return;
20316         }
20317         
20318         var tabGroup = this.parent();
20319         
20320         if(this.endX > this.startX){ // swiping right
20321             tabGroup.showPanelPrev();
20322             return;
20323         }
20324         
20325         if(this.startX > this.endX){ // swiping left
20326             tabGroup.showPanelNext();
20327             return;
20328         }
20329     }
20330     
20331     
20332 });
20333  
20334
20335  
20336
20337  /*
20338  * - LGPL
20339  *
20340  * DateField
20341  * 
20342  */
20343
20344 /**
20345  * @class Roo.bootstrap.DateField
20346  * @extends Roo.bootstrap.Input
20347  * Bootstrap DateField class
20348  * @cfg {Number} weekStart default 0
20349  * @cfg {String} viewMode default empty, (months|years)
20350  * @cfg {String} minViewMode default empty, (months|years)
20351  * @cfg {Number} startDate default -Infinity
20352  * @cfg {Number} endDate default Infinity
20353  * @cfg {Boolean} todayHighlight default false
20354  * @cfg {Boolean} todayBtn default false
20355  * @cfg {Boolean} calendarWeeks default false
20356  * @cfg {Object} daysOfWeekDisabled default empty
20357  * @cfg {Boolean} singleMode default false (true | false)
20358  * 
20359  * @cfg {Boolean} keyboardNavigation default true
20360  * @cfg {String} language default en
20361  * 
20362  * @constructor
20363  * Create a new DateField
20364  * @param {Object} config The config object
20365  */
20366
20367 Roo.bootstrap.DateField = function(config){
20368     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20369      this.addEvents({
20370             /**
20371              * @event show
20372              * Fires when this field show.
20373              * @param {Roo.bootstrap.DateField} this
20374              * @param {Mixed} date The date value
20375              */
20376             show : true,
20377             /**
20378              * @event show
20379              * Fires when this field hide.
20380              * @param {Roo.bootstrap.DateField} this
20381              * @param {Mixed} date The date value
20382              */
20383             hide : true,
20384             /**
20385              * @event select
20386              * Fires when select a date.
20387              * @param {Roo.bootstrap.DateField} this
20388              * @param {Mixed} date The date value
20389              */
20390             select : true,
20391             /**
20392              * @event beforeselect
20393              * Fires when before select a date.
20394              * @param {Roo.bootstrap.DateField} this
20395              * @param {Mixed} date The date value
20396              */
20397             beforeselect : true
20398         });
20399 };
20400
20401 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
20402     
20403     /**
20404      * @cfg {String} format
20405      * The default date format string which can be overriden for localization support.  The format must be
20406      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20407      */
20408     format : "m/d/y",
20409     /**
20410      * @cfg {String} altFormats
20411      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20412      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20413      */
20414     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20415     
20416     weekStart : 0,
20417     
20418     viewMode : '',
20419     
20420     minViewMode : '',
20421     
20422     todayHighlight : false,
20423     
20424     todayBtn: false,
20425     
20426     language: 'en',
20427     
20428     keyboardNavigation: true,
20429     
20430     calendarWeeks: false,
20431     
20432     startDate: -Infinity,
20433     
20434     endDate: Infinity,
20435     
20436     daysOfWeekDisabled: [],
20437     
20438     _events: [],
20439     
20440     singleMode : false,
20441     
20442     UTCDate: function()
20443     {
20444         return new Date(Date.UTC.apply(Date, arguments));
20445     },
20446     
20447     UTCToday: function()
20448     {
20449         var today = new Date();
20450         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20451     },
20452     
20453     getDate: function() {
20454             var d = this.getUTCDate();
20455             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20456     },
20457     
20458     getUTCDate: function() {
20459             return this.date;
20460     },
20461     
20462     setDate: function(d) {
20463             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20464     },
20465     
20466     setUTCDate: function(d) {
20467             this.date = d;
20468             this.setValue(this.formatDate(this.date));
20469     },
20470         
20471     onRender: function(ct, position)
20472     {
20473         
20474         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20475         
20476         this.language = this.language || 'en';
20477         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20478         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20479         
20480         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
20481         this.format = this.format || 'm/d/y';
20482         this.isInline = false;
20483         this.isInput = true;
20484         this.component = this.el.select('.add-on', true).first() || false;
20485         this.component = (this.component && this.component.length === 0) ? false : this.component;
20486         this.hasInput = this.component && this.inputEl().length;
20487         
20488         if (typeof(this.minViewMode === 'string')) {
20489             switch (this.minViewMode) {
20490                 case 'months':
20491                     this.minViewMode = 1;
20492                     break;
20493                 case 'years':
20494                     this.minViewMode = 2;
20495                     break;
20496                 default:
20497                     this.minViewMode = 0;
20498                     break;
20499             }
20500         }
20501         
20502         if (typeof(this.viewMode === 'string')) {
20503             switch (this.viewMode) {
20504                 case 'months':
20505                     this.viewMode = 1;
20506                     break;
20507                 case 'years':
20508                     this.viewMode = 2;
20509                     break;
20510                 default:
20511                     this.viewMode = 0;
20512                     break;
20513             }
20514         }
20515                 
20516         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
20517         
20518 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
20519         
20520         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20521         
20522         this.picker().on('mousedown', this.onMousedown, this);
20523         this.picker().on('click', this.onClick, this);
20524         
20525         this.picker().addClass('datepicker-dropdown');
20526         
20527         this.startViewMode = this.viewMode;
20528         
20529         if(this.singleMode){
20530             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
20531                 v.setVisibilityMode(Roo.Element.DISPLAY);
20532                 v.hide();
20533             });
20534             
20535             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20536                 v.setStyle('width', '189px');
20537             });
20538         }
20539         
20540         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
20541             if(!this.calendarWeeks){
20542                 v.remove();
20543                 return;
20544             }
20545             
20546             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20547             v.attr('colspan', function(i, val){
20548                 return parseInt(val) + 1;
20549             });
20550         });
20551                         
20552         
20553         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
20554         
20555         this.setStartDate(this.startDate);
20556         this.setEndDate(this.endDate);
20557         
20558         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
20559         
20560         this.fillDow();
20561         this.fillMonths();
20562         this.update();
20563         this.showMode();
20564         
20565         if(this.isInline) {
20566             this.showPopup();
20567         }
20568     },
20569     
20570     picker : function()
20571     {
20572         return this.pickerEl;
20573 //        return this.el.select('.datepicker', true).first();
20574     },
20575     
20576     fillDow: function()
20577     {
20578         var dowCnt = this.weekStart;
20579         
20580         var dow = {
20581             tag: 'tr',
20582             cn: [
20583                 
20584             ]
20585         };
20586         
20587         if(this.calendarWeeks){
20588             dow.cn.push({
20589                 tag: 'th',
20590                 cls: 'cw',
20591                 html: '&nbsp;'
20592             })
20593         }
20594         
20595         while (dowCnt < this.weekStart + 7) {
20596             dow.cn.push({
20597                 tag: 'th',
20598                 cls: 'dow',
20599                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
20600             });
20601         }
20602         
20603         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
20604     },
20605     
20606     fillMonths: function()
20607     {    
20608         var i = 0;
20609         var months = this.picker().select('>.datepicker-months td', true).first();
20610         
20611         months.dom.innerHTML = '';
20612         
20613         while (i < 12) {
20614             var month = {
20615                 tag: 'span',
20616                 cls: 'month',
20617                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
20618             };
20619             
20620             months.createChild(month);
20621         }
20622         
20623     },
20624     
20625     update: function()
20626     {
20627         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;
20628         
20629         if (this.date < this.startDate) {
20630             this.viewDate = new Date(this.startDate);
20631         } else if (this.date > this.endDate) {
20632             this.viewDate = new Date(this.endDate);
20633         } else {
20634             this.viewDate = new Date(this.date);
20635         }
20636         
20637         this.fill();
20638     },
20639     
20640     fill: function() 
20641     {
20642         var d = new Date(this.viewDate),
20643                 year = d.getUTCFullYear(),
20644                 month = d.getUTCMonth(),
20645                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
20646                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
20647                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
20648                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
20649                 currentDate = this.date && this.date.valueOf(),
20650                 today = this.UTCToday();
20651         
20652         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
20653         
20654 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20655         
20656 //        this.picker.select('>tfoot th.today').
20657 //                                              .text(dates[this.language].today)
20658 //                                              .toggle(this.todayBtn !== false);
20659     
20660         this.updateNavArrows();
20661         this.fillMonths();
20662                                                 
20663         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
20664         
20665         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
20666          
20667         prevMonth.setUTCDate(day);
20668         
20669         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
20670         
20671         var nextMonth = new Date(prevMonth);
20672         
20673         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
20674         
20675         nextMonth = nextMonth.valueOf();
20676         
20677         var fillMonths = false;
20678         
20679         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
20680         
20681         while(prevMonth.valueOf() <= nextMonth) {
20682             var clsName = '';
20683             
20684             if (prevMonth.getUTCDay() === this.weekStart) {
20685                 if(fillMonths){
20686                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
20687                 }
20688                     
20689                 fillMonths = {
20690                     tag: 'tr',
20691                     cn: []
20692                 };
20693                 
20694                 if(this.calendarWeeks){
20695                     // ISO 8601: First week contains first thursday.
20696                     // ISO also states week starts on Monday, but we can be more abstract here.
20697                     var
20698                     // Start of current week: based on weekstart/current date
20699                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
20700                     // Thursday of this week
20701                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
20702                     // First Thursday of year, year from thursday
20703                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
20704                     // Calendar week: ms between thursdays, div ms per day, div 7 days
20705                     calWeek =  (th - yth) / 864e5 / 7 + 1;
20706                     
20707                     fillMonths.cn.push({
20708                         tag: 'td',
20709                         cls: 'cw',
20710                         html: calWeek
20711                     });
20712                 }
20713             }
20714             
20715             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
20716                 clsName += ' old';
20717             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
20718                 clsName += ' new';
20719             }
20720             if (this.todayHighlight &&
20721                 prevMonth.getUTCFullYear() == today.getFullYear() &&
20722                 prevMonth.getUTCMonth() == today.getMonth() &&
20723                 prevMonth.getUTCDate() == today.getDate()) {
20724                 clsName += ' today';
20725             }
20726             
20727             if (currentDate && prevMonth.valueOf() === currentDate) {
20728                 clsName += ' active';
20729             }
20730             
20731             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
20732                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
20733                     clsName += ' disabled';
20734             }
20735             
20736             fillMonths.cn.push({
20737                 tag: 'td',
20738                 cls: 'day ' + clsName,
20739                 html: prevMonth.getDate()
20740             });
20741             
20742             prevMonth.setDate(prevMonth.getDate()+1);
20743         }
20744           
20745         var currentYear = this.date && this.date.getUTCFullYear();
20746         var currentMonth = this.date && this.date.getUTCMonth();
20747         
20748         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
20749         
20750         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
20751             v.removeClass('active');
20752             
20753             if(currentYear === year && k === currentMonth){
20754                 v.addClass('active');
20755             }
20756             
20757             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
20758                 v.addClass('disabled');
20759             }
20760             
20761         });
20762         
20763         
20764         year = parseInt(year/10, 10) * 10;
20765         
20766         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
20767         
20768         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
20769         
20770         year -= 1;
20771         for (var i = -1; i < 11; i++) {
20772             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
20773                 tag: 'span',
20774                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
20775                 html: year
20776             });
20777             
20778             year += 1;
20779         }
20780     },
20781     
20782     showMode: function(dir) 
20783     {
20784         if (dir) {
20785             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
20786         }
20787         
20788         Roo.each(this.picker().select('>div',true).elements, function(v){
20789             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20790             v.hide();
20791         });
20792         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
20793     },
20794     
20795     place: function()
20796     {
20797         if(this.isInline) {
20798             return;
20799         }
20800         
20801         this.picker().removeClass(['bottom', 'top']);
20802         
20803         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20804             /*
20805              * place to the top of element!
20806              *
20807              */
20808             
20809             this.picker().addClass('top');
20810             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20811             
20812             return;
20813         }
20814         
20815         this.picker().addClass('bottom');
20816         
20817         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20818     },
20819     
20820     parseDate : function(value)
20821     {
20822         if(!value || value instanceof Date){
20823             return value;
20824         }
20825         var v = Date.parseDate(value, this.format);
20826         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
20827             v = Date.parseDate(value, 'Y-m-d');
20828         }
20829         if(!v && this.altFormats){
20830             if(!this.altFormatsArray){
20831                 this.altFormatsArray = this.altFormats.split("|");
20832             }
20833             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
20834                 v = Date.parseDate(value, this.altFormatsArray[i]);
20835             }
20836         }
20837         return v;
20838     },
20839     
20840     formatDate : function(date, fmt)
20841     {   
20842         return (!date || !(date instanceof Date)) ?
20843         date : date.dateFormat(fmt || this.format);
20844     },
20845     
20846     onFocus : function()
20847     {
20848         Roo.bootstrap.DateField.superclass.onFocus.call(this);
20849         this.showPopup();
20850     },
20851     
20852     onBlur : function()
20853     {
20854         Roo.bootstrap.DateField.superclass.onBlur.call(this);
20855         
20856         var d = this.inputEl().getValue();
20857         
20858         this.setValue(d);
20859                 
20860         this.hidePopup();
20861     },
20862     
20863     showPopup : function()
20864     {
20865         this.picker().show();
20866         this.update();
20867         this.place();
20868         
20869         this.fireEvent('showpopup', this, this.date);
20870     },
20871     
20872     hidePopup : function()
20873     {
20874         if(this.isInline) {
20875             return;
20876         }
20877         this.picker().hide();
20878         this.viewMode = this.startViewMode;
20879         this.showMode();
20880         
20881         this.fireEvent('hidepopup', this, this.date);
20882         
20883     },
20884     
20885     onMousedown: function(e)
20886     {
20887         e.stopPropagation();
20888         e.preventDefault();
20889     },
20890     
20891     keyup: function(e)
20892     {
20893         Roo.bootstrap.DateField.superclass.keyup.call(this);
20894         this.update();
20895     },
20896
20897     setValue: function(v)
20898     {
20899         if(this.fireEvent('beforeselect', this, v) !== false){
20900             var d = new Date(this.parseDate(v) ).clearTime();
20901         
20902             if(isNaN(d.getTime())){
20903                 this.date = this.viewDate = '';
20904                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
20905                 return;
20906             }
20907
20908             v = this.formatDate(d);
20909
20910             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
20911
20912             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
20913
20914             this.update();
20915
20916             this.fireEvent('select', this, this.date);
20917         }
20918     },
20919     
20920     getValue: function()
20921     {
20922         return this.formatDate(this.date);
20923     },
20924     
20925     fireKey: function(e)
20926     {
20927         if (!this.picker().isVisible()){
20928             if (e.keyCode == 27) { // allow escape to hide and re-show picker
20929                 this.showPopup();
20930             }
20931             return;
20932         }
20933         
20934         var dateChanged = false,
20935         dir, day, month,
20936         newDate, newViewDate;
20937         
20938         switch(e.keyCode){
20939             case 27: // escape
20940                 this.hidePopup();
20941                 e.preventDefault();
20942                 break;
20943             case 37: // left
20944             case 39: // right
20945                 if (!this.keyboardNavigation) {
20946                     break;
20947                 }
20948                 dir = e.keyCode == 37 ? -1 : 1;
20949                 
20950                 if (e.ctrlKey){
20951                     newDate = this.moveYear(this.date, dir);
20952                     newViewDate = this.moveYear(this.viewDate, dir);
20953                 } else if (e.shiftKey){
20954                     newDate = this.moveMonth(this.date, dir);
20955                     newViewDate = this.moveMonth(this.viewDate, dir);
20956                 } else {
20957                     newDate = new Date(this.date);
20958                     newDate.setUTCDate(this.date.getUTCDate() + dir);
20959                     newViewDate = new Date(this.viewDate);
20960                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
20961                 }
20962                 if (this.dateWithinRange(newDate)){
20963                     this.date = newDate;
20964                     this.viewDate = newViewDate;
20965                     this.setValue(this.formatDate(this.date));
20966 //                    this.update();
20967                     e.preventDefault();
20968                     dateChanged = true;
20969                 }
20970                 break;
20971             case 38: // up
20972             case 40: // down
20973                 if (!this.keyboardNavigation) {
20974                     break;
20975                 }
20976                 dir = e.keyCode == 38 ? -1 : 1;
20977                 if (e.ctrlKey){
20978                     newDate = this.moveYear(this.date, dir);
20979                     newViewDate = this.moveYear(this.viewDate, dir);
20980                 } else if (e.shiftKey){
20981                     newDate = this.moveMonth(this.date, dir);
20982                     newViewDate = this.moveMonth(this.viewDate, dir);
20983                 } else {
20984                     newDate = new Date(this.date);
20985                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
20986                     newViewDate = new Date(this.viewDate);
20987                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
20988                 }
20989                 if (this.dateWithinRange(newDate)){
20990                     this.date = newDate;
20991                     this.viewDate = newViewDate;
20992                     this.setValue(this.formatDate(this.date));
20993 //                    this.update();
20994                     e.preventDefault();
20995                     dateChanged = true;
20996                 }
20997                 break;
20998             case 13: // enter
20999                 this.setValue(this.formatDate(this.date));
21000                 this.hidePopup();
21001                 e.preventDefault();
21002                 break;
21003             case 9: // tab
21004                 this.setValue(this.formatDate(this.date));
21005                 this.hidePopup();
21006                 break;
21007             case 16: // shift
21008             case 17: // ctrl
21009             case 18: // alt
21010                 break;
21011             default :
21012                 this.hidePopup();
21013                 
21014         }
21015     },
21016     
21017     
21018     onClick: function(e) 
21019     {
21020         e.stopPropagation();
21021         e.preventDefault();
21022         
21023         var target = e.getTarget();
21024         
21025         if(target.nodeName.toLowerCase() === 'i'){
21026             target = Roo.get(target).dom.parentNode;
21027         }
21028         
21029         var nodeName = target.nodeName;
21030         var className = target.className;
21031         var html = target.innerHTML;
21032         //Roo.log(nodeName);
21033         
21034         switch(nodeName.toLowerCase()) {
21035             case 'th':
21036                 switch(className) {
21037                     case 'switch':
21038                         this.showMode(1);
21039                         break;
21040                     case 'prev':
21041                     case 'next':
21042                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21043                         switch(this.viewMode){
21044                                 case 0:
21045                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21046                                         break;
21047                                 case 1:
21048                                 case 2:
21049                                         this.viewDate = this.moveYear(this.viewDate, dir);
21050                                         break;
21051                         }
21052                         this.fill();
21053                         break;
21054                     case 'today':
21055                         var date = new Date();
21056                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21057 //                        this.fill()
21058                         this.setValue(this.formatDate(this.date));
21059                         
21060                         this.hidePopup();
21061                         break;
21062                 }
21063                 break;
21064             case 'span':
21065                 if (className.indexOf('disabled') < 0) {
21066                     this.viewDate.setUTCDate(1);
21067                     if (className.indexOf('month') > -1) {
21068                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21069                     } else {
21070                         var year = parseInt(html, 10) || 0;
21071                         this.viewDate.setUTCFullYear(year);
21072                         
21073                     }
21074                     
21075                     if(this.singleMode){
21076                         this.setValue(this.formatDate(this.viewDate));
21077                         this.hidePopup();
21078                         return;
21079                     }
21080                     
21081                     this.showMode(-1);
21082                     this.fill();
21083                 }
21084                 break;
21085                 
21086             case 'td':
21087                 //Roo.log(className);
21088                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21089                     var day = parseInt(html, 10) || 1;
21090                     var year = this.viewDate.getUTCFullYear(),
21091                         month = this.viewDate.getUTCMonth();
21092
21093                     if (className.indexOf('old') > -1) {
21094                         if(month === 0 ){
21095                             month = 11;
21096                             year -= 1;
21097                         }else{
21098                             month -= 1;
21099                         }
21100                     } else if (className.indexOf('new') > -1) {
21101                         if (month == 11) {
21102                             month = 0;
21103                             year += 1;
21104                         } else {
21105                             month += 1;
21106                         }
21107                     }
21108                     //Roo.log([year,month,day]);
21109                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21110                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21111 //                    this.fill();
21112                     //Roo.log(this.formatDate(this.date));
21113                     this.setValue(this.formatDate(this.date));
21114                     this.hidePopup();
21115                 }
21116                 break;
21117         }
21118     },
21119     
21120     setStartDate: function(startDate)
21121     {
21122         this.startDate = startDate || -Infinity;
21123         if (this.startDate !== -Infinity) {
21124             this.startDate = this.parseDate(this.startDate);
21125         }
21126         this.update();
21127         this.updateNavArrows();
21128     },
21129
21130     setEndDate: function(endDate)
21131     {
21132         this.endDate = endDate || Infinity;
21133         if (this.endDate !== Infinity) {
21134             this.endDate = this.parseDate(this.endDate);
21135         }
21136         this.update();
21137         this.updateNavArrows();
21138     },
21139     
21140     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21141     {
21142         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21143         if (typeof(this.daysOfWeekDisabled) !== 'object') {
21144             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21145         }
21146         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21147             return parseInt(d, 10);
21148         });
21149         this.update();
21150         this.updateNavArrows();
21151     },
21152     
21153     updateNavArrows: function() 
21154     {
21155         if(this.singleMode){
21156             return;
21157         }
21158         
21159         var d = new Date(this.viewDate),
21160         year = d.getUTCFullYear(),
21161         month = d.getUTCMonth();
21162         
21163         Roo.each(this.picker().select('.prev', true).elements, function(v){
21164             v.show();
21165             switch (this.viewMode) {
21166                 case 0:
21167
21168                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21169                         v.hide();
21170                     }
21171                     break;
21172                 case 1:
21173                 case 2:
21174                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21175                         v.hide();
21176                     }
21177                     break;
21178             }
21179         });
21180         
21181         Roo.each(this.picker().select('.next', true).elements, function(v){
21182             v.show();
21183             switch (this.viewMode) {
21184                 case 0:
21185
21186                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21187                         v.hide();
21188                     }
21189                     break;
21190                 case 1:
21191                 case 2:
21192                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21193                         v.hide();
21194                     }
21195                     break;
21196             }
21197         })
21198     },
21199     
21200     moveMonth: function(date, dir)
21201     {
21202         if (!dir) {
21203             return date;
21204         }
21205         var new_date = new Date(date.valueOf()),
21206         day = new_date.getUTCDate(),
21207         month = new_date.getUTCMonth(),
21208         mag = Math.abs(dir),
21209         new_month, test;
21210         dir = dir > 0 ? 1 : -1;
21211         if (mag == 1){
21212             test = dir == -1
21213             // If going back one month, make sure month is not current month
21214             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21215             ? function(){
21216                 return new_date.getUTCMonth() == month;
21217             }
21218             // If going forward one month, make sure month is as expected
21219             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21220             : function(){
21221                 return new_date.getUTCMonth() != new_month;
21222             };
21223             new_month = month + dir;
21224             new_date.setUTCMonth(new_month);
21225             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21226             if (new_month < 0 || new_month > 11) {
21227                 new_month = (new_month + 12) % 12;
21228             }
21229         } else {
21230             // For magnitudes >1, move one month at a time...
21231             for (var i=0; i<mag; i++) {
21232                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21233                 new_date = this.moveMonth(new_date, dir);
21234             }
21235             // ...then reset the day, keeping it in the new month
21236             new_month = new_date.getUTCMonth();
21237             new_date.setUTCDate(day);
21238             test = function(){
21239                 return new_month != new_date.getUTCMonth();
21240             };
21241         }
21242         // Common date-resetting loop -- if date is beyond end of month, make it
21243         // end of month
21244         while (test()){
21245             new_date.setUTCDate(--day);
21246             new_date.setUTCMonth(new_month);
21247         }
21248         return new_date;
21249     },
21250
21251     moveYear: function(date, dir)
21252     {
21253         return this.moveMonth(date, dir*12);
21254     },
21255
21256     dateWithinRange: function(date)
21257     {
21258         return date >= this.startDate && date <= this.endDate;
21259     },
21260
21261     
21262     remove: function() 
21263     {
21264         this.picker().remove();
21265     },
21266     
21267     validateValue : function(value)
21268     {
21269         if(this.getVisibilityEl().hasClass('hidden')){
21270             return true;
21271         }
21272         
21273         if(value.length < 1)  {
21274             if(this.allowBlank){
21275                 return true;
21276             }
21277             return false;
21278         }
21279         
21280         if(value.length < this.minLength){
21281             return false;
21282         }
21283         if(value.length > this.maxLength){
21284             return false;
21285         }
21286         if(this.vtype){
21287             var vt = Roo.form.VTypes;
21288             if(!vt[this.vtype](value, this)){
21289                 return false;
21290             }
21291         }
21292         if(typeof this.validator == "function"){
21293             var msg = this.validator(value);
21294             if(msg !== true){
21295                 return false;
21296             }
21297         }
21298         
21299         if(this.regex && !this.regex.test(value)){
21300             return false;
21301         }
21302         
21303         if(typeof(this.parseDate(value)) == 'undefined'){
21304             return false;
21305         }
21306         
21307         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21308             return false;
21309         }      
21310         
21311         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21312             return false;
21313         } 
21314         
21315         
21316         return true;
21317     },
21318     
21319     reset : function()
21320     {
21321         this.date = this.viewDate = '';
21322         
21323         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21324     }
21325    
21326 });
21327
21328 Roo.apply(Roo.bootstrap.DateField,  {
21329     
21330     head : {
21331         tag: 'thead',
21332         cn: [
21333         {
21334             tag: 'tr',
21335             cn: [
21336             {
21337                 tag: 'th',
21338                 cls: 'prev',
21339                 html: '<i class="fa fa-arrow-left"/>'
21340             },
21341             {
21342                 tag: 'th',
21343                 cls: 'switch',
21344                 colspan: '5'
21345             },
21346             {
21347                 tag: 'th',
21348                 cls: 'next',
21349                 html: '<i class="fa fa-arrow-right"/>'
21350             }
21351
21352             ]
21353         }
21354         ]
21355     },
21356     
21357     content : {
21358         tag: 'tbody',
21359         cn: [
21360         {
21361             tag: 'tr',
21362             cn: [
21363             {
21364                 tag: 'td',
21365                 colspan: '7'
21366             }
21367             ]
21368         }
21369         ]
21370     },
21371     
21372     footer : {
21373         tag: 'tfoot',
21374         cn: [
21375         {
21376             tag: 'tr',
21377             cn: [
21378             {
21379                 tag: 'th',
21380                 colspan: '7',
21381                 cls: 'today'
21382             }
21383                     
21384             ]
21385         }
21386         ]
21387     },
21388     
21389     dates:{
21390         en: {
21391             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21392             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21393             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21394             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21395             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21396             today: "Today"
21397         }
21398     },
21399     
21400     modes: [
21401     {
21402         clsName: 'days',
21403         navFnc: 'Month',
21404         navStep: 1
21405     },
21406     {
21407         clsName: 'months',
21408         navFnc: 'FullYear',
21409         navStep: 1
21410     },
21411     {
21412         clsName: 'years',
21413         navFnc: 'FullYear',
21414         navStep: 10
21415     }]
21416 });
21417
21418 Roo.apply(Roo.bootstrap.DateField,  {
21419   
21420     template : {
21421         tag: 'div',
21422         cls: 'datepicker dropdown-menu roo-dynamic',
21423         cn: [
21424         {
21425             tag: 'div',
21426             cls: 'datepicker-days',
21427             cn: [
21428             {
21429                 tag: 'table',
21430                 cls: 'table-condensed',
21431                 cn:[
21432                 Roo.bootstrap.DateField.head,
21433                 {
21434                     tag: 'tbody'
21435                 },
21436                 Roo.bootstrap.DateField.footer
21437                 ]
21438             }
21439             ]
21440         },
21441         {
21442             tag: 'div',
21443             cls: 'datepicker-months',
21444             cn: [
21445             {
21446                 tag: 'table',
21447                 cls: 'table-condensed',
21448                 cn:[
21449                 Roo.bootstrap.DateField.head,
21450                 Roo.bootstrap.DateField.content,
21451                 Roo.bootstrap.DateField.footer
21452                 ]
21453             }
21454             ]
21455         },
21456         {
21457             tag: 'div',
21458             cls: 'datepicker-years',
21459             cn: [
21460             {
21461                 tag: 'table',
21462                 cls: 'table-condensed',
21463                 cn:[
21464                 Roo.bootstrap.DateField.head,
21465                 Roo.bootstrap.DateField.content,
21466                 Roo.bootstrap.DateField.footer
21467                 ]
21468             }
21469             ]
21470         }
21471         ]
21472     }
21473 });
21474
21475  
21476
21477  /*
21478  * - LGPL
21479  *
21480  * TimeField
21481  * 
21482  */
21483
21484 /**
21485  * @class Roo.bootstrap.TimeField
21486  * @extends Roo.bootstrap.Input
21487  * Bootstrap DateField class
21488  * 
21489  * 
21490  * @constructor
21491  * Create a new TimeField
21492  * @param {Object} config The config object
21493  */
21494
21495 Roo.bootstrap.TimeField = function(config){
21496     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
21497     this.addEvents({
21498             /**
21499              * @event show
21500              * Fires when this field show.
21501              * @param {Roo.bootstrap.DateField} thisthis
21502              * @param {Mixed} date The date value
21503              */
21504             show : true,
21505             /**
21506              * @event show
21507              * Fires when this field hide.
21508              * @param {Roo.bootstrap.DateField} this
21509              * @param {Mixed} date The date value
21510              */
21511             hide : true,
21512             /**
21513              * @event select
21514              * Fires when select a date.
21515              * @param {Roo.bootstrap.DateField} this
21516              * @param {Mixed} date The date value
21517              */
21518             select : true
21519         });
21520 };
21521
21522 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
21523     
21524     /**
21525      * @cfg {String} format
21526      * The default time format string which can be overriden for localization support.  The format must be
21527      * valid according to {@link Date#parseDate} (defaults to 'H:i').
21528      */
21529     format : "H:i",
21530        
21531     onRender: function(ct, position)
21532     {
21533         
21534         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
21535                 
21536         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
21537         
21538         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21539         
21540         this.pop = this.picker().select('>.datepicker-time',true).first();
21541         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21542         
21543         this.picker().on('mousedown', this.onMousedown, this);
21544         this.picker().on('click', this.onClick, this);
21545         
21546         this.picker().addClass('datepicker-dropdown');
21547     
21548         this.fillTime();
21549         this.update();
21550             
21551         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
21552         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
21553         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
21554         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
21555         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
21556         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
21557
21558     },
21559     
21560     fireKey: function(e){
21561         if (!this.picker().isVisible()){
21562             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21563                 this.show();
21564             }
21565             return;
21566         }
21567
21568         e.preventDefault();
21569         
21570         switch(e.keyCode){
21571             case 27: // escape
21572                 this.hide();
21573                 break;
21574             case 37: // left
21575             case 39: // right
21576                 this.onTogglePeriod();
21577                 break;
21578             case 38: // up
21579                 this.onIncrementMinutes();
21580                 break;
21581             case 40: // down
21582                 this.onDecrementMinutes();
21583                 break;
21584             case 13: // enter
21585             case 9: // tab
21586                 this.setTime();
21587                 break;
21588         }
21589     },
21590     
21591     onClick: function(e) {
21592         e.stopPropagation();
21593         e.preventDefault();
21594     },
21595     
21596     picker : function()
21597     {
21598         return this.el.select('.datepicker', true).first();
21599     },
21600     
21601     fillTime: function()
21602     {    
21603         var time = this.pop.select('tbody', true).first();
21604         
21605         time.dom.innerHTML = '';
21606         
21607         time.createChild({
21608             tag: 'tr',
21609             cn: [
21610                 {
21611                     tag: 'td',
21612                     cn: [
21613                         {
21614                             tag: 'a',
21615                             href: '#',
21616                             cls: 'btn',
21617                             cn: [
21618                                 {
21619                                     tag: 'span',
21620                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
21621                                 }
21622                             ]
21623                         } 
21624                     ]
21625                 },
21626                 {
21627                     tag: 'td',
21628                     cls: 'separator'
21629                 },
21630                 {
21631                     tag: 'td',
21632                     cn: [
21633                         {
21634                             tag: 'a',
21635                             href: '#',
21636                             cls: 'btn',
21637                             cn: [
21638                                 {
21639                                     tag: 'span',
21640                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
21641                                 }
21642                             ]
21643                         }
21644                     ]
21645                 },
21646                 {
21647                     tag: 'td',
21648                     cls: 'separator'
21649                 }
21650             ]
21651         });
21652         
21653         time.createChild({
21654             tag: 'tr',
21655             cn: [
21656                 {
21657                     tag: 'td',
21658                     cn: [
21659                         {
21660                             tag: 'span',
21661                             cls: 'timepicker-hour',
21662                             html: '00'
21663                         }  
21664                     ]
21665                 },
21666                 {
21667                     tag: 'td',
21668                     cls: 'separator',
21669                     html: ':'
21670                 },
21671                 {
21672                     tag: 'td',
21673                     cn: [
21674                         {
21675                             tag: 'span',
21676                             cls: 'timepicker-minute',
21677                             html: '00'
21678                         }  
21679                     ]
21680                 },
21681                 {
21682                     tag: 'td',
21683                     cls: 'separator'
21684                 },
21685                 {
21686                     tag: 'td',
21687                     cn: [
21688                         {
21689                             tag: 'button',
21690                             type: 'button',
21691                             cls: 'btn btn-primary period',
21692                             html: 'AM'
21693                             
21694                         }
21695                     ]
21696                 }
21697             ]
21698         });
21699         
21700         time.createChild({
21701             tag: 'tr',
21702             cn: [
21703                 {
21704                     tag: 'td',
21705                     cn: [
21706                         {
21707                             tag: 'a',
21708                             href: '#',
21709                             cls: 'btn',
21710                             cn: [
21711                                 {
21712                                     tag: 'span',
21713                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
21714                                 }
21715                             ]
21716                         }
21717                     ]
21718                 },
21719                 {
21720                     tag: 'td',
21721                     cls: 'separator'
21722                 },
21723                 {
21724                     tag: 'td',
21725                     cn: [
21726                         {
21727                             tag: 'a',
21728                             href: '#',
21729                             cls: 'btn',
21730                             cn: [
21731                                 {
21732                                     tag: 'span',
21733                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
21734                                 }
21735                             ]
21736                         }
21737                     ]
21738                 },
21739                 {
21740                     tag: 'td',
21741                     cls: 'separator'
21742                 }
21743             ]
21744         });
21745         
21746     },
21747     
21748     update: function()
21749     {
21750         
21751         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
21752         
21753         this.fill();
21754     },
21755     
21756     fill: function() 
21757     {
21758         var hours = this.time.getHours();
21759         var minutes = this.time.getMinutes();
21760         var period = 'AM';
21761         
21762         if(hours > 11){
21763             period = 'PM';
21764         }
21765         
21766         if(hours == 0){
21767             hours = 12;
21768         }
21769         
21770         
21771         if(hours > 12){
21772             hours = hours - 12;
21773         }
21774         
21775         if(hours < 10){
21776             hours = '0' + hours;
21777         }
21778         
21779         if(minutes < 10){
21780             minutes = '0' + minutes;
21781         }
21782         
21783         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
21784         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
21785         this.pop.select('button', true).first().dom.innerHTML = period;
21786         
21787     },
21788     
21789     place: function()
21790     {   
21791         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
21792         
21793         var cls = ['bottom'];
21794         
21795         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
21796             cls.pop();
21797             cls.push('top');
21798         }
21799         
21800         cls.push('right');
21801         
21802         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
21803             cls.pop();
21804             cls.push('left');
21805         }
21806         
21807         this.picker().addClass(cls.join('-'));
21808         
21809         var _this = this;
21810         
21811         Roo.each(cls, function(c){
21812             if(c == 'bottom'){
21813                 _this.picker().setTop(_this.inputEl().getHeight());
21814                 return;
21815             }
21816             if(c == 'top'){
21817                 _this.picker().setTop(0 - _this.picker().getHeight());
21818                 return;
21819             }
21820             
21821             if(c == 'left'){
21822                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
21823                 return;
21824             }
21825             if(c == 'right'){
21826                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
21827                 return;
21828             }
21829         });
21830         
21831     },
21832   
21833     onFocus : function()
21834     {
21835         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
21836         this.show();
21837     },
21838     
21839     onBlur : function()
21840     {
21841         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
21842         this.hide();
21843     },
21844     
21845     show : function()
21846     {
21847         this.picker().show();
21848         this.pop.show();
21849         this.update();
21850         this.place();
21851         
21852         this.fireEvent('show', this, this.date);
21853     },
21854     
21855     hide : function()
21856     {
21857         this.picker().hide();
21858         this.pop.hide();
21859         
21860         this.fireEvent('hide', this, this.date);
21861     },
21862     
21863     setTime : function()
21864     {
21865         this.hide();
21866         this.setValue(this.time.format(this.format));
21867         
21868         this.fireEvent('select', this, this.date);
21869         
21870         
21871     },
21872     
21873     onMousedown: function(e){
21874         e.stopPropagation();
21875         e.preventDefault();
21876     },
21877     
21878     onIncrementHours: function()
21879     {
21880         Roo.log('onIncrementHours');
21881         this.time = this.time.add(Date.HOUR, 1);
21882         this.update();
21883         
21884     },
21885     
21886     onDecrementHours: function()
21887     {
21888         Roo.log('onDecrementHours');
21889         this.time = this.time.add(Date.HOUR, -1);
21890         this.update();
21891     },
21892     
21893     onIncrementMinutes: function()
21894     {
21895         Roo.log('onIncrementMinutes');
21896         this.time = this.time.add(Date.MINUTE, 1);
21897         this.update();
21898     },
21899     
21900     onDecrementMinutes: function()
21901     {
21902         Roo.log('onDecrementMinutes');
21903         this.time = this.time.add(Date.MINUTE, -1);
21904         this.update();
21905     },
21906     
21907     onTogglePeriod: function()
21908     {
21909         Roo.log('onTogglePeriod');
21910         this.time = this.time.add(Date.HOUR, 12);
21911         this.update();
21912     }
21913     
21914    
21915 });
21916
21917 Roo.apply(Roo.bootstrap.TimeField,  {
21918     
21919     content : {
21920         tag: 'tbody',
21921         cn: [
21922             {
21923                 tag: 'tr',
21924                 cn: [
21925                 {
21926                     tag: 'td',
21927                     colspan: '7'
21928                 }
21929                 ]
21930             }
21931         ]
21932     },
21933     
21934     footer : {
21935         tag: 'tfoot',
21936         cn: [
21937             {
21938                 tag: 'tr',
21939                 cn: [
21940                 {
21941                     tag: 'th',
21942                     colspan: '7',
21943                     cls: '',
21944                     cn: [
21945                         {
21946                             tag: 'button',
21947                             cls: 'btn btn-info ok',
21948                             html: 'OK'
21949                         }
21950                     ]
21951                 }
21952
21953                 ]
21954             }
21955         ]
21956     }
21957 });
21958
21959 Roo.apply(Roo.bootstrap.TimeField,  {
21960   
21961     template : {
21962         tag: 'div',
21963         cls: 'datepicker dropdown-menu',
21964         cn: [
21965             {
21966                 tag: 'div',
21967                 cls: 'datepicker-time',
21968                 cn: [
21969                 {
21970                     tag: 'table',
21971                     cls: 'table-condensed',
21972                     cn:[
21973                     Roo.bootstrap.TimeField.content,
21974                     Roo.bootstrap.TimeField.footer
21975                     ]
21976                 }
21977                 ]
21978             }
21979         ]
21980     }
21981 });
21982
21983  
21984
21985  /*
21986  * - LGPL
21987  *
21988  * MonthField
21989  * 
21990  */
21991
21992 /**
21993  * @class Roo.bootstrap.MonthField
21994  * @extends Roo.bootstrap.Input
21995  * Bootstrap MonthField class
21996  * 
21997  * @cfg {String} language default en
21998  * 
21999  * @constructor
22000  * Create a new MonthField
22001  * @param {Object} config The config object
22002  */
22003
22004 Roo.bootstrap.MonthField = function(config){
22005     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22006     
22007     this.addEvents({
22008         /**
22009          * @event show
22010          * Fires when this field show.
22011          * @param {Roo.bootstrap.MonthField} this
22012          * @param {Mixed} date The date value
22013          */
22014         show : true,
22015         /**
22016          * @event show
22017          * Fires when this field hide.
22018          * @param {Roo.bootstrap.MonthField} this
22019          * @param {Mixed} date The date value
22020          */
22021         hide : true,
22022         /**
22023          * @event select
22024          * Fires when select a date.
22025          * @param {Roo.bootstrap.MonthField} this
22026          * @param {String} oldvalue The old value
22027          * @param {String} newvalue The new value
22028          */
22029         select : true
22030     });
22031 };
22032
22033 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22034     
22035     onRender: function(ct, position)
22036     {
22037         
22038         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22039         
22040         this.language = this.language || 'en';
22041         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22042         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22043         
22044         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22045         this.isInline = false;
22046         this.isInput = true;
22047         this.component = this.el.select('.add-on', true).first() || false;
22048         this.component = (this.component && this.component.length === 0) ? false : this.component;
22049         this.hasInput = this.component && this.inputEL().length;
22050         
22051         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22052         
22053         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22054         
22055         this.picker().on('mousedown', this.onMousedown, this);
22056         this.picker().on('click', this.onClick, this);
22057         
22058         this.picker().addClass('datepicker-dropdown');
22059         
22060         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22061             v.setStyle('width', '189px');
22062         });
22063         
22064         this.fillMonths();
22065         
22066         this.update();
22067         
22068         if(this.isInline) {
22069             this.show();
22070         }
22071         
22072     },
22073     
22074     setValue: function(v, suppressEvent)
22075     {   
22076         var o = this.getValue();
22077         
22078         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22079         
22080         this.update();
22081
22082         if(suppressEvent !== true){
22083             this.fireEvent('select', this, o, v);
22084         }
22085         
22086     },
22087     
22088     getValue: function()
22089     {
22090         return this.value;
22091     },
22092     
22093     onClick: function(e) 
22094     {
22095         e.stopPropagation();
22096         e.preventDefault();
22097         
22098         var target = e.getTarget();
22099         
22100         if(target.nodeName.toLowerCase() === 'i'){
22101             target = Roo.get(target).dom.parentNode;
22102         }
22103         
22104         var nodeName = target.nodeName;
22105         var className = target.className;
22106         var html = target.innerHTML;
22107         
22108         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22109             return;
22110         }
22111         
22112         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22113         
22114         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22115         
22116         this.hide();
22117                         
22118     },
22119     
22120     picker : function()
22121     {
22122         return this.pickerEl;
22123     },
22124     
22125     fillMonths: function()
22126     {    
22127         var i = 0;
22128         var months = this.picker().select('>.datepicker-months td', true).first();
22129         
22130         months.dom.innerHTML = '';
22131         
22132         while (i < 12) {
22133             var month = {
22134                 tag: 'span',
22135                 cls: 'month',
22136                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22137             };
22138             
22139             months.createChild(month);
22140         }
22141         
22142     },
22143     
22144     update: function()
22145     {
22146         var _this = this;
22147         
22148         if(typeof(this.vIndex) == 'undefined' && this.value.length){
22149             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22150         }
22151         
22152         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22153             e.removeClass('active');
22154             
22155             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22156                 e.addClass('active');
22157             }
22158         })
22159     },
22160     
22161     place: function()
22162     {
22163         if(this.isInline) {
22164             return;
22165         }
22166         
22167         this.picker().removeClass(['bottom', 'top']);
22168         
22169         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22170             /*
22171              * place to the top of element!
22172              *
22173              */
22174             
22175             this.picker().addClass('top');
22176             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22177             
22178             return;
22179         }
22180         
22181         this.picker().addClass('bottom');
22182         
22183         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22184     },
22185     
22186     onFocus : function()
22187     {
22188         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22189         this.show();
22190     },
22191     
22192     onBlur : function()
22193     {
22194         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22195         
22196         var d = this.inputEl().getValue();
22197         
22198         this.setValue(d);
22199                 
22200         this.hide();
22201     },
22202     
22203     show : function()
22204     {
22205         this.picker().show();
22206         this.picker().select('>.datepicker-months', true).first().show();
22207         this.update();
22208         this.place();
22209         
22210         this.fireEvent('show', this, this.date);
22211     },
22212     
22213     hide : function()
22214     {
22215         if(this.isInline) {
22216             return;
22217         }
22218         this.picker().hide();
22219         this.fireEvent('hide', this, this.date);
22220         
22221     },
22222     
22223     onMousedown: function(e)
22224     {
22225         e.stopPropagation();
22226         e.preventDefault();
22227     },
22228     
22229     keyup: function(e)
22230     {
22231         Roo.bootstrap.MonthField.superclass.keyup.call(this);
22232         this.update();
22233     },
22234
22235     fireKey: function(e)
22236     {
22237         if (!this.picker().isVisible()){
22238             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
22239                 this.show();
22240             }
22241             return;
22242         }
22243         
22244         var dir;
22245         
22246         switch(e.keyCode){
22247             case 27: // escape
22248                 this.hide();
22249                 e.preventDefault();
22250                 break;
22251             case 37: // left
22252             case 39: // right
22253                 dir = e.keyCode == 37 ? -1 : 1;
22254                 
22255                 this.vIndex = this.vIndex + dir;
22256                 
22257                 if(this.vIndex < 0){
22258                     this.vIndex = 0;
22259                 }
22260                 
22261                 if(this.vIndex > 11){
22262                     this.vIndex = 11;
22263                 }
22264                 
22265                 if(isNaN(this.vIndex)){
22266                     this.vIndex = 0;
22267                 }
22268                 
22269                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22270                 
22271                 break;
22272             case 38: // up
22273             case 40: // down
22274                 
22275                 dir = e.keyCode == 38 ? -1 : 1;
22276                 
22277                 this.vIndex = this.vIndex + dir * 4;
22278                 
22279                 if(this.vIndex < 0){
22280                     this.vIndex = 0;
22281                 }
22282                 
22283                 if(this.vIndex > 11){
22284                     this.vIndex = 11;
22285                 }
22286                 
22287                 if(isNaN(this.vIndex)){
22288                     this.vIndex = 0;
22289                 }
22290                 
22291                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22292                 break;
22293                 
22294             case 13: // enter
22295                 
22296                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22297                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22298                 }
22299                 
22300                 this.hide();
22301                 e.preventDefault();
22302                 break;
22303             case 9: // tab
22304                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22305                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22306                 }
22307                 this.hide();
22308                 break;
22309             case 16: // shift
22310             case 17: // ctrl
22311             case 18: // alt
22312                 break;
22313             default :
22314                 this.hide();
22315                 
22316         }
22317     },
22318     
22319     remove: function() 
22320     {
22321         this.picker().remove();
22322     }
22323    
22324 });
22325
22326 Roo.apply(Roo.bootstrap.MonthField,  {
22327     
22328     content : {
22329         tag: 'tbody',
22330         cn: [
22331         {
22332             tag: 'tr',
22333             cn: [
22334             {
22335                 tag: 'td',
22336                 colspan: '7'
22337             }
22338             ]
22339         }
22340         ]
22341     },
22342     
22343     dates:{
22344         en: {
22345             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22346             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22347         }
22348     }
22349 });
22350
22351 Roo.apply(Roo.bootstrap.MonthField,  {
22352   
22353     template : {
22354         tag: 'div',
22355         cls: 'datepicker dropdown-menu roo-dynamic',
22356         cn: [
22357             {
22358                 tag: 'div',
22359                 cls: 'datepicker-months',
22360                 cn: [
22361                 {
22362                     tag: 'table',
22363                     cls: 'table-condensed',
22364                     cn:[
22365                         Roo.bootstrap.DateField.content
22366                     ]
22367                 }
22368                 ]
22369             }
22370         ]
22371     }
22372 });
22373
22374  
22375
22376  
22377  /*
22378  * - LGPL
22379  *
22380  * CheckBox
22381  * 
22382  */
22383
22384 /**
22385  * @class Roo.bootstrap.CheckBox
22386  * @extends Roo.bootstrap.Input
22387  * Bootstrap CheckBox class
22388  * 
22389  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22390  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22391  * @cfg {String} boxLabel The text that appears beside the checkbox
22392  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22393  * @cfg {Boolean} checked initnal the element
22394  * @cfg {Boolean} inline inline the element (default false)
22395  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22396  * @cfg {String} tooltip label tooltip
22397  * 
22398  * @constructor
22399  * Create a new CheckBox
22400  * @param {Object} config The config object
22401  */
22402
22403 Roo.bootstrap.CheckBox = function(config){
22404     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22405    
22406     this.addEvents({
22407         /**
22408         * @event check
22409         * Fires when the element is checked or unchecked.
22410         * @param {Roo.bootstrap.CheckBox} this This input
22411         * @param {Boolean} checked The new checked value
22412         */
22413        check : true,
22414        /**
22415         * @event click
22416         * Fires when the element is click.
22417         * @param {Roo.bootstrap.CheckBox} this This input
22418         */
22419        click : true
22420     });
22421     
22422 };
22423
22424 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
22425   
22426     inputType: 'checkbox',
22427     inputValue: 1,
22428     valueOff: 0,
22429     boxLabel: false,
22430     checked: false,
22431     weight : false,
22432     inline: false,
22433     tooltip : '',
22434     
22435     // checkbox success does not make any sense really.. 
22436     invalidClass : "",
22437     validClass : "",
22438     
22439     
22440     getAutoCreate : function()
22441     {
22442         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22443         
22444         var id = Roo.id();
22445         
22446         var cfg = {};
22447         
22448         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22449         
22450         if(this.inline){
22451             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
22452         }
22453         
22454         var input =  {
22455             tag: 'input',
22456             id : id,
22457             type : this.inputType,
22458             value : this.inputValue,
22459             cls : 'roo-' + this.inputType, //'form-box',
22460             placeholder : this.placeholder || ''
22461             
22462         };
22463         
22464         if(this.inputType != 'radio'){
22465             var hidden =  {
22466                 tag: 'input',
22467                 type : 'hidden',
22468                 cls : 'roo-hidden-value',
22469                 value : this.checked ? this.inputValue : this.valueOff
22470             };
22471         }
22472         
22473             
22474         if (this.weight) { // Validity check?
22475             cfg.cls += " " + this.inputType + "-" + this.weight;
22476         }
22477         
22478         if (this.disabled) {
22479             input.disabled=true;
22480         }
22481         
22482         if(this.checked){
22483             input.checked = this.checked;
22484         }
22485         
22486         if (this.name) {
22487             
22488             input.name = this.name;
22489             
22490             if(this.inputType != 'radio'){
22491                 hidden.name = this.name;
22492                 input.name = '_hidden_' + this.name;
22493             }
22494         }
22495         
22496         if (this.size) {
22497             input.cls += ' input-' + this.size;
22498         }
22499         
22500         var settings=this;
22501         
22502         ['xs','sm','md','lg'].map(function(size){
22503             if (settings[size]) {
22504                 cfg.cls += ' col-' + size + '-' + settings[size];
22505             }
22506         });
22507         
22508         var inputblock = input;
22509          
22510         if (this.before || this.after) {
22511             
22512             inputblock = {
22513                 cls : 'input-group',
22514                 cn :  [] 
22515             };
22516             
22517             if (this.before) {
22518                 inputblock.cn.push({
22519                     tag :'span',
22520                     cls : 'input-group-addon',
22521                     html : this.before
22522                 });
22523             }
22524             
22525             inputblock.cn.push(input);
22526             
22527             if(this.inputType != 'radio'){
22528                 inputblock.cn.push(hidden);
22529             }
22530             
22531             if (this.after) {
22532                 inputblock.cn.push({
22533                     tag :'span',
22534                     cls : 'input-group-addon',
22535                     html : this.after
22536                 });
22537             }
22538             
22539         }
22540         var boxLabelCfg = false;
22541         
22542         if(this.boxLabel){
22543            
22544             boxLabelCfg = {
22545                 tag: 'label',
22546                 //'for': id, // box label is handled by onclick - so no for...
22547                 cls: 'box-label',
22548                 html: this.boxLabel
22549             };
22550             if(this.tooltip){
22551                 boxLabelCfg.tooltip = this.tooltip;
22552             }
22553              
22554         }
22555         
22556         
22557         if (align ==='left' && this.fieldLabel.length) {
22558 //                Roo.log("left and has label");
22559             cfg.cn = [
22560                 {
22561                     tag: 'label',
22562                     'for' :  id,
22563                     cls : 'control-label',
22564                     html : this.fieldLabel
22565                 },
22566                 {
22567                     cls : "", 
22568                     cn: [
22569                         inputblock
22570                     ]
22571                 }
22572             ];
22573             
22574             if (boxLabelCfg) {
22575                 cfg.cn[1].cn.push(boxLabelCfg);
22576             }
22577             
22578             if(this.labelWidth > 12){
22579                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
22580             }
22581             
22582             if(this.labelWidth < 13 && this.labelmd == 0){
22583                 this.labelmd = this.labelWidth;
22584             }
22585             
22586             if(this.labellg > 0){
22587                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
22588                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
22589             }
22590             
22591             if(this.labelmd > 0){
22592                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
22593                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
22594             }
22595             
22596             if(this.labelsm > 0){
22597                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
22598                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
22599             }
22600             
22601             if(this.labelxs > 0){
22602                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
22603                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
22604             }
22605             
22606         } else if ( this.fieldLabel.length) {
22607 //                Roo.log(" label");
22608                 cfg.cn = [
22609                    
22610                     {
22611                         tag: this.boxLabel ? 'span' : 'label',
22612                         'for': id,
22613                         cls: 'control-label box-input-label',
22614                         //cls : 'input-group-addon',
22615                         html : this.fieldLabel
22616                     },
22617                     
22618                     inputblock
22619                     
22620                 ];
22621                 if (boxLabelCfg) {
22622                     cfg.cn.push(boxLabelCfg);
22623                 }
22624
22625         } else {
22626             
22627 //                Roo.log(" no label && no align");
22628                 cfg.cn = [  inputblock ] ;
22629                 if (boxLabelCfg) {
22630                     cfg.cn.push(boxLabelCfg);
22631                 }
22632
22633                 
22634         }
22635         
22636        
22637         
22638         if(this.inputType != 'radio'){
22639             cfg.cn.push(hidden);
22640         }
22641         
22642         return cfg;
22643         
22644     },
22645     
22646     /**
22647      * return the real input element.
22648      */
22649     inputEl: function ()
22650     {
22651         return this.el.select('input.roo-' + this.inputType,true).first();
22652     },
22653     hiddenEl: function ()
22654     {
22655         return this.el.select('input.roo-hidden-value',true).first();
22656     },
22657     
22658     labelEl: function()
22659     {
22660         return this.el.select('label.control-label',true).first();
22661     },
22662     /* depricated... */
22663     
22664     label: function()
22665     {
22666         return this.labelEl();
22667     },
22668     
22669     boxLabelEl: function()
22670     {
22671         return this.el.select('label.box-label',true).first();
22672     },
22673     
22674     initEvents : function()
22675     {
22676 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
22677         
22678         this.inputEl().on('click', this.onClick,  this);
22679         
22680         if (this.boxLabel) { 
22681             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
22682         }
22683         
22684         this.startValue = this.getValue();
22685         
22686         if(this.groupId){
22687             Roo.bootstrap.CheckBox.register(this);
22688         }
22689     },
22690     
22691     onClick : function(e)
22692     {   
22693         if(this.fireEvent('click', this, e) !== false){
22694             this.setChecked(!this.checked);
22695         }
22696         
22697     },
22698     
22699     setChecked : function(state,suppressEvent)
22700     {
22701         this.startValue = this.getValue();
22702
22703         if(this.inputType == 'radio'){
22704             
22705             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22706                 e.dom.checked = false;
22707             });
22708             
22709             this.inputEl().dom.checked = true;
22710             
22711             this.inputEl().dom.value = this.inputValue;
22712             
22713             if(suppressEvent !== true){
22714                 this.fireEvent('check', this, true);
22715             }
22716             
22717             this.validate();
22718             
22719             return;
22720         }
22721         
22722         this.checked = state;
22723         
22724         this.inputEl().dom.checked = state;
22725         
22726         
22727         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
22728         
22729         if(suppressEvent !== true){
22730             this.fireEvent('check', this, state);
22731         }
22732         
22733         this.validate();
22734     },
22735     
22736     getValue : function()
22737     {
22738         if(this.inputType == 'radio'){
22739             return this.getGroupValue();
22740         }
22741         
22742         return this.hiddenEl().dom.value;
22743         
22744     },
22745     
22746     getGroupValue : function()
22747     {
22748         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
22749             return '';
22750         }
22751         
22752         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
22753     },
22754     
22755     setValue : function(v,suppressEvent)
22756     {
22757         if(this.inputType == 'radio'){
22758             this.setGroupValue(v, suppressEvent);
22759             return;
22760         }
22761         
22762         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
22763         
22764         this.validate();
22765     },
22766     
22767     setGroupValue : function(v, suppressEvent)
22768     {
22769         this.startValue = this.getValue();
22770         
22771         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22772             e.dom.checked = false;
22773             
22774             if(e.dom.value == v){
22775                 e.dom.checked = true;
22776             }
22777         });
22778         
22779         if(suppressEvent !== true){
22780             this.fireEvent('check', this, true);
22781         }
22782
22783         this.validate();
22784         
22785         return;
22786     },
22787     
22788     validate : function()
22789     {
22790         if(this.getVisibilityEl().hasClass('hidden')){
22791             return true;
22792         }
22793         
22794         if(
22795                 this.disabled || 
22796                 (this.inputType == 'radio' && this.validateRadio()) ||
22797                 (this.inputType == 'checkbox' && this.validateCheckbox())
22798         ){
22799             this.markValid();
22800             return true;
22801         }
22802         
22803         this.markInvalid();
22804         return false;
22805     },
22806     
22807     validateRadio : function()
22808     {
22809         if(this.getVisibilityEl().hasClass('hidden')){
22810             return true;
22811         }
22812         
22813         if(this.allowBlank){
22814             return true;
22815         }
22816         
22817         var valid = false;
22818         
22819         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22820             if(!e.dom.checked){
22821                 return;
22822             }
22823             
22824             valid = true;
22825             
22826             return false;
22827         });
22828         
22829         return valid;
22830     },
22831     
22832     validateCheckbox : function()
22833     {
22834         if(!this.groupId){
22835             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
22836             //return (this.getValue() == this.inputValue) ? true : false;
22837         }
22838         
22839         var group = Roo.bootstrap.CheckBox.get(this.groupId);
22840         
22841         if(!group){
22842             return false;
22843         }
22844         
22845         var r = false;
22846         
22847         for(var i in group){
22848             if(group[i].el.isVisible(true)){
22849                 r = false;
22850                 break;
22851             }
22852             
22853             r = true;
22854         }
22855         
22856         for(var i in group){
22857             if(r){
22858                 break;
22859             }
22860             
22861             r = (group[i].getValue() == group[i].inputValue) ? true : false;
22862         }
22863         
22864         return r;
22865     },
22866     
22867     /**
22868      * Mark this field as valid
22869      */
22870     markValid : function()
22871     {
22872         var _this = this;
22873         
22874         this.fireEvent('valid', this);
22875         
22876         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
22877         
22878         if(this.groupId){
22879             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
22880         }
22881         
22882         if(label){
22883             label.markValid();
22884         }
22885
22886         if(this.inputType == 'radio'){
22887             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22888                 var fg = e.findParent('.form-group', false, true);
22889                 if (Roo.bootstrap.version == 3) {
22890                     fg.removeClass([_this.invalidClass, _this.validClass]);
22891                     fg.addClass(_this.validClass);
22892                 } else {
22893                     fg.removeClass(['is-valid', 'is-invalid']);
22894                     fg.addClass('is-valid');
22895                 }
22896             });
22897             
22898             return;
22899         }
22900
22901         if(!this.groupId){
22902             var fg = this.el.findParent('.form-group', false, true);
22903             if (Roo.bootstrap.version == 3) {
22904                 fg.removeClass([this.invalidClass, this.validClass]);
22905                 fg.addClass(this.validClass);
22906             } else {
22907                 fg.removeClass(['is-valid', 'is-invalid']);
22908                 fg.addClass('is-valid');
22909             }
22910             return;
22911         }
22912         
22913         var group = Roo.bootstrap.CheckBox.get(this.groupId);
22914         
22915         if(!group){
22916             return;
22917         }
22918         
22919         for(var i in group){
22920             var fg = group[i].el.findParent('.form-group', false, true);
22921             if (Roo.bootstrap.version == 3) {
22922                 fg.removeClass([this.invalidClass, this.validClass]);
22923                 fg.addClass(this.validClass);
22924             } else {
22925                 fg.removeClass(['is-valid', 'is-invalid']);
22926                 fg.addClass('is-valid');
22927             }
22928         }
22929     },
22930     
22931      /**
22932      * Mark this field as invalid
22933      * @param {String} msg The validation message
22934      */
22935     markInvalid : function(msg)
22936     {
22937         if(this.allowBlank){
22938             return;
22939         }
22940         
22941         var _this = this;
22942         
22943         this.fireEvent('invalid', this, msg);
22944         
22945         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
22946         
22947         if(this.groupId){
22948             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
22949         }
22950         
22951         if(label){
22952             label.markInvalid();
22953         }
22954             
22955         if(this.inputType == 'radio'){
22956             
22957             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22958                 var fg = e.findParent('.form-group', false, true);
22959                 if (Roo.bootstrap.version == 3) {
22960                     fg.removeClass([_this.invalidClass, _this.validClass]);
22961                     fg.addClass(_this.invalidClass);
22962                 } else {
22963                     fg.removeClass(['is-invalid', 'is-valid']);
22964                     fg.addClass('is-invalid');
22965                 }
22966             });
22967             
22968             return;
22969         }
22970         
22971         if(!this.groupId){
22972             var fg = this.el.findParent('.form-group', false, true);
22973             if (Roo.bootstrap.version == 3) {
22974                 fg.removeClass([_this.invalidClass, _this.validClass]);
22975                 fg.addClass(_this.invalidClass);
22976             } else {
22977                 fg.removeClass(['is-invalid', 'is-valid']);
22978                 fg.addClass('is-invalid');
22979             }
22980             return;
22981         }
22982         
22983         var group = Roo.bootstrap.CheckBox.get(this.groupId);
22984         
22985         if(!group){
22986             return;
22987         }
22988         
22989         for(var i in group){
22990             var fg = group[i].el.findParent('.form-group', false, true);
22991             if (Roo.bootstrap.version == 3) {
22992                 fg.removeClass([_this.invalidClass, _this.validClass]);
22993                 fg.addClass(_this.invalidClass);
22994             } else {
22995                 fg.removeClass(['is-invalid', 'is-valid']);
22996                 fg.addClass('is-invalid');
22997             }
22998         }
22999         
23000     },
23001     
23002     clearInvalid : function()
23003     {
23004         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23005         
23006         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23007         
23008         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23009         
23010         if (label && label.iconEl) {
23011             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23012             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23013         }
23014     },
23015     
23016     disable : function()
23017     {
23018         if(this.inputType != 'radio'){
23019             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23020             return;
23021         }
23022         
23023         var _this = this;
23024         
23025         if(this.rendered){
23026             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23027                 _this.getActionEl().addClass(this.disabledClass);
23028                 e.dom.disabled = true;
23029             });
23030         }
23031         
23032         this.disabled = true;
23033         this.fireEvent("disable", this);
23034         return this;
23035     },
23036
23037     enable : function()
23038     {
23039         if(this.inputType != 'radio'){
23040             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23041             return;
23042         }
23043         
23044         var _this = this;
23045         
23046         if(this.rendered){
23047             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23048                 _this.getActionEl().removeClass(this.disabledClass);
23049                 e.dom.disabled = false;
23050             });
23051         }
23052         
23053         this.disabled = false;
23054         this.fireEvent("enable", this);
23055         return this;
23056     },
23057     
23058     setBoxLabel : function(v)
23059     {
23060         this.boxLabel = v;
23061         
23062         if(this.rendered){
23063             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23064         }
23065     }
23066
23067 });
23068
23069 Roo.apply(Roo.bootstrap.CheckBox, {
23070     
23071     groups: {},
23072     
23073      /**
23074     * register a CheckBox Group
23075     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23076     */
23077     register : function(checkbox)
23078     {
23079         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23080             this.groups[checkbox.groupId] = {};
23081         }
23082         
23083         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23084             return;
23085         }
23086         
23087         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23088         
23089     },
23090     /**
23091     * fetch a CheckBox Group based on the group ID
23092     * @param {string} the group ID
23093     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23094     */
23095     get: function(groupId) {
23096         if (typeof(this.groups[groupId]) == 'undefined') {
23097             return false;
23098         }
23099         
23100         return this.groups[groupId] ;
23101     }
23102     
23103     
23104 });
23105 /*
23106  * - LGPL
23107  *
23108  * RadioItem
23109  * 
23110  */
23111
23112 /**
23113  * @class Roo.bootstrap.Radio
23114  * @extends Roo.bootstrap.Component
23115  * Bootstrap Radio class
23116  * @cfg {String} boxLabel - the label associated
23117  * @cfg {String} value - the value of radio
23118  * 
23119  * @constructor
23120  * Create a new Radio
23121  * @param {Object} config The config object
23122  */
23123 Roo.bootstrap.Radio = function(config){
23124     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23125     
23126 };
23127
23128 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23129     
23130     boxLabel : '',
23131     
23132     value : '',
23133     
23134     getAutoCreate : function()
23135     {
23136         var cfg = {
23137             tag : 'div',
23138             cls : 'form-group radio',
23139             cn : [
23140                 {
23141                     tag : 'label',
23142                     cls : 'box-label',
23143                     html : this.boxLabel
23144                 }
23145             ]
23146         };
23147         
23148         return cfg;
23149     },
23150     
23151     initEvents : function() 
23152     {
23153         this.parent().register(this);
23154         
23155         this.el.on('click', this.onClick, this);
23156         
23157     },
23158     
23159     onClick : function(e)
23160     {
23161         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23162             this.setChecked(true);
23163         }
23164     },
23165     
23166     setChecked : function(state, suppressEvent)
23167     {
23168         this.parent().setValue(this.value, suppressEvent);
23169         
23170     },
23171     
23172     setBoxLabel : function(v)
23173     {
23174         this.boxLabel = v;
23175         
23176         if(this.rendered){
23177             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23178         }
23179     }
23180     
23181 });
23182  
23183
23184  /*
23185  * - LGPL
23186  *
23187  * Input
23188  * 
23189  */
23190
23191 /**
23192  * @class Roo.bootstrap.SecurePass
23193  * @extends Roo.bootstrap.Input
23194  * Bootstrap SecurePass class
23195  *
23196  * 
23197  * @constructor
23198  * Create a new SecurePass
23199  * @param {Object} config The config object
23200  */
23201  
23202 Roo.bootstrap.SecurePass = function (config) {
23203     // these go here, so the translation tool can replace them..
23204     this.errors = {
23205         PwdEmpty: "Please type a password, and then retype it to confirm.",
23206         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23207         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23208         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23209         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23210         FNInPwd: "Your password can't contain your first name. Please type a different password.",
23211         LNInPwd: "Your password can't contain your last name. Please type a different password.",
23212         TooWeak: "Your password is Too Weak."
23213     },
23214     this.meterLabel = "Password strength:";
23215     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23216     this.meterClass = [
23217         "roo-password-meter-tooweak", 
23218         "roo-password-meter-weak", 
23219         "roo-password-meter-medium", 
23220         "roo-password-meter-strong", 
23221         "roo-password-meter-grey"
23222     ];
23223     
23224     this.errors = {};
23225     
23226     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23227 }
23228
23229 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23230     /**
23231      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23232      * {
23233      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
23234      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23235      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23236      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23237      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23238      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
23239      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
23240      * })
23241      */
23242     // private
23243     
23244     meterWidth: 300,
23245     errorMsg :'',    
23246     errors: false,
23247     imageRoot: '/',
23248     /**
23249      * @cfg {String/Object} Label for the strength meter (defaults to
23250      * 'Password strength:')
23251      */
23252     // private
23253     meterLabel: '',
23254     /**
23255      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23256      * ['Weak', 'Medium', 'Strong'])
23257      */
23258     // private    
23259     pwdStrengths: false,    
23260     // private
23261     strength: 0,
23262     // private
23263     _lastPwd: null,
23264     // private
23265     kCapitalLetter: 0,
23266     kSmallLetter: 1,
23267     kDigit: 2,
23268     kPunctuation: 3,
23269     
23270     insecure: false,
23271     // private
23272     initEvents: function ()
23273     {
23274         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23275
23276         if (this.el.is('input[type=password]') && Roo.isSafari) {
23277             this.el.on('keydown', this.SafariOnKeyDown, this);
23278         }
23279
23280         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23281     },
23282     // private
23283     onRender: function (ct, position)
23284     {
23285         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23286         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23287         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23288
23289         this.trigger.createChild({
23290                    cn: [
23291                     {
23292                     //id: 'PwdMeter',
23293                     tag: 'div',
23294                     cls: 'roo-password-meter-grey col-xs-12',
23295                     style: {
23296                         //width: 0,
23297                         //width: this.meterWidth + 'px'                                                
23298                         }
23299                     },
23300                     {                            
23301                          cls: 'roo-password-meter-text'                          
23302                     }
23303                 ]            
23304         });
23305
23306          
23307         if (this.hideTrigger) {
23308             this.trigger.setDisplayed(false);
23309         }
23310         this.setSize(this.width || '', this.height || '');
23311     },
23312     // private
23313     onDestroy: function ()
23314     {
23315         if (this.trigger) {
23316             this.trigger.removeAllListeners();
23317             this.trigger.remove();
23318         }
23319         if (this.wrap) {
23320             this.wrap.remove();
23321         }
23322         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23323     },
23324     // private
23325     checkStrength: function ()
23326     {
23327         var pwd = this.inputEl().getValue();
23328         if (pwd == this._lastPwd) {
23329             return;
23330         }
23331
23332         var strength;
23333         if (this.ClientSideStrongPassword(pwd)) {
23334             strength = 3;
23335         } else if (this.ClientSideMediumPassword(pwd)) {
23336             strength = 2;
23337         } else if (this.ClientSideWeakPassword(pwd)) {
23338             strength = 1;
23339         } else {
23340             strength = 0;
23341         }
23342         
23343         Roo.log('strength1: ' + strength);
23344         
23345         //var pm = this.trigger.child('div/div/div').dom;
23346         var pm = this.trigger.child('div/div');
23347         pm.removeClass(this.meterClass);
23348         pm.addClass(this.meterClass[strength]);
23349                 
23350         
23351         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23352                 
23353         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23354         
23355         this._lastPwd = pwd;
23356     },
23357     reset: function ()
23358     {
23359         Roo.bootstrap.SecurePass.superclass.reset.call(this);
23360         
23361         this._lastPwd = '';
23362         
23363         var pm = this.trigger.child('div/div');
23364         pm.removeClass(this.meterClass);
23365         pm.addClass('roo-password-meter-grey');        
23366         
23367         
23368         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23369         
23370         pt.innerHTML = '';
23371         this.inputEl().dom.type='password';
23372     },
23373     // private
23374     validateValue: function (value)
23375     {
23376         
23377         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23378             return false;
23379         }
23380         if (value.length == 0) {
23381             if (this.allowBlank) {
23382                 this.clearInvalid();
23383                 return true;
23384             }
23385
23386             this.markInvalid(this.errors.PwdEmpty);
23387             this.errorMsg = this.errors.PwdEmpty;
23388             return false;
23389         }
23390         
23391         if(this.insecure){
23392             return true;
23393         }
23394         
23395         if ('[\x21-\x7e]*'.match(value)) {
23396             this.markInvalid(this.errors.PwdBadChar);
23397             this.errorMsg = this.errors.PwdBadChar;
23398             return false;
23399         }
23400         if (value.length < 6) {
23401             this.markInvalid(this.errors.PwdShort);
23402             this.errorMsg = this.errors.PwdShort;
23403             return false;
23404         }
23405         if (value.length > 16) {
23406             this.markInvalid(this.errors.PwdLong);
23407             this.errorMsg = this.errors.PwdLong;
23408             return false;
23409         }
23410         var strength;
23411         if (this.ClientSideStrongPassword(value)) {
23412             strength = 3;
23413         } else if (this.ClientSideMediumPassword(value)) {
23414             strength = 2;
23415         } else if (this.ClientSideWeakPassword(value)) {
23416             strength = 1;
23417         } else {
23418             strength = 0;
23419         }
23420
23421         
23422         if (strength < 2) {
23423             //this.markInvalid(this.errors.TooWeak);
23424             this.errorMsg = this.errors.TooWeak;
23425             //return false;
23426         }
23427         
23428         
23429         console.log('strength2: ' + strength);
23430         
23431         //var pm = this.trigger.child('div/div/div').dom;
23432         
23433         var pm = this.trigger.child('div/div');
23434         pm.removeClass(this.meterClass);
23435         pm.addClass(this.meterClass[strength]);
23436                 
23437         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23438                 
23439         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23440         
23441         this.errorMsg = ''; 
23442         return true;
23443     },
23444     // private
23445     CharacterSetChecks: function (type)
23446     {
23447         this.type = type;
23448         this.fResult = false;
23449     },
23450     // private
23451     isctype: function (character, type)
23452     {
23453         switch (type) {  
23454             case this.kCapitalLetter:
23455                 if (character >= 'A' && character <= 'Z') {
23456                     return true;
23457                 }
23458                 break;
23459             
23460             case this.kSmallLetter:
23461                 if (character >= 'a' && character <= 'z') {
23462                     return true;
23463                 }
23464                 break;
23465             
23466             case this.kDigit:
23467                 if (character >= '0' && character <= '9') {
23468                     return true;
23469                 }
23470                 break;
23471             
23472             case this.kPunctuation:
23473                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
23474                     return true;
23475                 }
23476                 break;
23477             
23478             default:
23479                 return false;
23480         }
23481
23482     },
23483     // private
23484     IsLongEnough: function (pwd, size)
23485     {
23486         return !(pwd == null || isNaN(size) || pwd.length < size);
23487     },
23488     // private
23489     SpansEnoughCharacterSets: function (word, nb)
23490     {
23491         if (!this.IsLongEnough(word, nb))
23492         {
23493             return false;
23494         }
23495
23496         var characterSetChecks = new Array(
23497             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
23498             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
23499         );
23500         
23501         for (var index = 0; index < word.length; ++index) {
23502             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23503                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
23504                     characterSetChecks[nCharSet].fResult = true;
23505                     break;
23506                 }
23507             }
23508         }
23509
23510         var nCharSets = 0;
23511         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23512             if (characterSetChecks[nCharSet].fResult) {
23513                 ++nCharSets;
23514             }
23515         }
23516
23517         if (nCharSets < nb) {
23518             return false;
23519         }
23520         return true;
23521     },
23522     // private
23523     ClientSideStrongPassword: function (pwd)
23524     {
23525         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
23526     },
23527     // private
23528     ClientSideMediumPassword: function (pwd)
23529     {
23530         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
23531     },
23532     // private
23533     ClientSideWeakPassword: function (pwd)
23534     {
23535         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
23536     }
23537           
23538 })//<script type="text/javascript">
23539
23540 /*
23541  * Based  Ext JS Library 1.1.1
23542  * Copyright(c) 2006-2007, Ext JS, LLC.
23543  * LGPL
23544  *
23545  */
23546  
23547 /**
23548  * @class Roo.HtmlEditorCore
23549  * @extends Roo.Component
23550  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
23551  *
23552  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23553  */
23554
23555 Roo.HtmlEditorCore = function(config){
23556     
23557     
23558     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
23559     
23560     
23561     this.addEvents({
23562         /**
23563          * @event initialize
23564          * Fires when the editor is fully initialized (including the iframe)
23565          * @param {Roo.HtmlEditorCore} this
23566          */
23567         initialize: true,
23568         /**
23569          * @event activate
23570          * Fires when the editor is first receives the focus. Any insertion must wait
23571          * until after this event.
23572          * @param {Roo.HtmlEditorCore} this
23573          */
23574         activate: true,
23575          /**
23576          * @event beforesync
23577          * Fires before the textarea is updated with content from the editor iframe. Return false
23578          * to cancel the sync.
23579          * @param {Roo.HtmlEditorCore} this
23580          * @param {String} html
23581          */
23582         beforesync: true,
23583          /**
23584          * @event beforepush
23585          * Fires before the iframe editor is updated with content from the textarea. Return false
23586          * to cancel the push.
23587          * @param {Roo.HtmlEditorCore} this
23588          * @param {String} html
23589          */
23590         beforepush: true,
23591          /**
23592          * @event sync
23593          * Fires when the textarea is updated with content from the editor iframe.
23594          * @param {Roo.HtmlEditorCore} this
23595          * @param {String} html
23596          */
23597         sync: true,
23598          /**
23599          * @event push
23600          * Fires when the iframe editor is updated with content from the textarea.
23601          * @param {Roo.HtmlEditorCore} this
23602          * @param {String} html
23603          */
23604         push: true,
23605         
23606         /**
23607          * @event editorevent
23608          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23609          * @param {Roo.HtmlEditorCore} this
23610          */
23611         editorevent: true
23612         
23613     });
23614     
23615     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
23616     
23617     // defaults : white / black...
23618     this.applyBlacklists();
23619     
23620     
23621     
23622 };
23623
23624
23625 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
23626
23627
23628      /**
23629      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
23630      */
23631     
23632     owner : false,
23633     
23634      /**
23635      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23636      *                        Roo.resizable.
23637      */
23638     resizable : false,
23639      /**
23640      * @cfg {Number} height (in pixels)
23641      */   
23642     height: 300,
23643    /**
23644      * @cfg {Number} width (in pixels)
23645      */   
23646     width: 500,
23647     
23648     /**
23649      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23650      * 
23651      */
23652     stylesheets: false,
23653     
23654     // id of frame..
23655     frameId: false,
23656     
23657     // private properties
23658     validationEvent : false,
23659     deferHeight: true,
23660     initialized : false,
23661     activated : false,
23662     sourceEditMode : false,
23663     onFocus : Roo.emptyFn,
23664     iframePad:3,
23665     hideMode:'offsets',
23666     
23667     clearUp: true,
23668     
23669     // blacklist + whitelisted elements..
23670     black: false,
23671     white: false,
23672      
23673     bodyCls : '',
23674
23675     /**
23676      * Protected method that will not generally be called directly. It
23677      * is called when the editor initializes the iframe with HTML contents. Override this method if you
23678      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
23679      */
23680     getDocMarkup : function(){
23681         // body styles..
23682         var st = '';
23683         
23684         // inherit styels from page...?? 
23685         if (this.stylesheets === false) {
23686             
23687             Roo.get(document.head).select('style').each(function(node) {
23688                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23689             });
23690             
23691             Roo.get(document.head).select('link').each(function(node) { 
23692                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23693             });
23694             
23695         } else if (!this.stylesheets.length) {
23696                 // simple..
23697                 st = '<style type="text/css">' +
23698                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23699                    '</style>';
23700         } else { 
23701             st = '<style type="text/css">' +
23702                     this.stylesheets +
23703                 '</style>';
23704         }
23705         
23706         st +=  '<style type="text/css">' +
23707             'IMG { cursor: pointer } ' +
23708         '</style>';
23709
23710         var cls = 'roo-htmleditor-body';
23711         
23712         if(this.bodyCls.length){
23713             cls += ' ' + this.bodyCls;
23714         }
23715         
23716         return '<html><head>' + st  +
23717             //<style type="text/css">' +
23718             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23719             //'</style>' +
23720             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
23721     },
23722
23723     // private
23724     onRender : function(ct, position)
23725     {
23726         var _t = this;
23727         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
23728         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
23729         
23730         
23731         this.el.dom.style.border = '0 none';
23732         this.el.dom.setAttribute('tabIndex', -1);
23733         this.el.addClass('x-hidden hide');
23734         
23735         
23736         
23737         if(Roo.isIE){ // fix IE 1px bogus margin
23738             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
23739         }
23740        
23741         
23742         this.frameId = Roo.id();
23743         
23744          
23745         
23746         var iframe = this.owner.wrap.createChild({
23747             tag: 'iframe',
23748             cls: 'form-control', // bootstrap..
23749             id: this.frameId,
23750             name: this.frameId,
23751             frameBorder : 'no',
23752             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
23753         }, this.el
23754         );
23755         
23756         
23757         this.iframe = iframe.dom;
23758
23759          this.assignDocWin();
23760         
23761         this.doc.designMode = 'on';
23762        
23763         this.doc.open();
23764         this.doc.write(this.getDocMarkup());
23765         this.doc.close();
23766
23767         
23768         var task = { // must defer to wait for browser to be ready
23769             run : function(){
23770                 //console.log("run task?" + this.doc.readyState);
23771                 this.assignDocWin();
23772                 if(this.doc.body || this.doc.readyState == 'complete'){
23773                     try {
23774                         this.doc.designMode="on";
23775                     } catch (e) {
23776                         return;
23777                     }
23778                     Roo.TaskMgr.stop(task);
23779                     this.initEditor.defer(10, this);
23780                 }
23781             },
23782             interval : 10,
23783             duration: 10000,
23784             scope: this
23785         };
23786         Roo.TaskMgr.start(task);
23787
23788     },
23789
23790     // private
23791     onResize : function(w, h)
23792     {
23793          Roo.log('resize: ' +w + ',' + h );
23794         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
23795         if(!this.iframe){
23796             return;
23797         }
23798         if(typeof w == 'number'){
23799             
23800             this.iframe.style.width = w + 'px';
23801         }
23802         if(typeof h == 'number'){
23803             
23804             this.iframe.style.height = h + 'px';
23805             if(this.doc){
23806                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
23807             }
23808         }
23809         
23810     },
23811
23812     /**
23813      * Toggles the editor between standard and source edit mode.
23814      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23815      */
23816     toggleSourceEdit : function(sourceEditMode){
23817         
23818         this.sourceEditMode = sourceEditMode === true;
23819         
23820         if(this.sourceEditMode){
23821  
23822             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
23823             
23824         }else{
23825             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
23826             //this.iframe.className = '';
23827             this.deferFocus();
23828         }
23829         //this.setSize(this.owner.wrap.getSize());
23830         //this.fireEvent('editmodechange', this, this.sourceEditMode);
23831     },
23832
23833     
23834   
23835
23836     /**
23837      * Protected method that will not generally be called directly. If you need/want
23838      * custom HTML cleanup, this is the method you should override.
23839      * @param {String} html The HTML to be cleaned
23840      * return {String} The cleaned HTML
23841      */
23842     cleanHtml : function(html){
23843         html = String(html);
23844         if(html.length > 5){
23845             if(Roo.isSafari){ // strip safari nonsense
23846                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
23847             }
23848         }
23849         if(html == '&nbsp;'){
23850             html = '';
23851         }
23852         return html;
23853     },
23854
23855     /**
23856      * HTML Editor -> Textarea
23857      * Protected method that will not generally be called directly. Syncs the contents
23858      * of the editor iframe with the textarea.
23859      */
23860     syncValue : function(){
23861         if(this.initialized){
23862             var bd = (this.doc.body || this.doc.documentElement);
23863             //this.cleanUpPaste(); -- this is done else where and causes havoc..
23864             var html = bd.innerHTML;
23865             if(Roo.isSafari){
23866                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
23867                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
23868                 if(m && m[1]){
23869                     html = '<div style="'+m[0]+'">' + html + '</div>';
23870                 }
23871             }
23872             html = this.cleanHtml(html);
23873             // fix up the special chars.. normaly like back quotes in word...
23874             // however we do not want to do this with chinese..
23875             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
23876                 
23877                 var cc = match.charCodeAt();
23878
23879                 // Get the character value, handling surrogate pairs
23880                 if (match.length == 2) {
23881                     // It's a surrogate pair, calculate the Unicode code point
23882                     var high = match.charCodeAt(0) - 0xD800;
23883                     var low  = match.charCodeAt(1) - 0xDC00;
23884                     cc = (high * 0x400) + low + 0x10000;
23885                 }  else if (
23886                     (cc >= 0x4E00 && cc < 0xA000 ) ||
23887                     (cc >= 0x3400 && cc < 0x4E00 ) ||
23888                     (cc >= 0xf900 && cc < 0xfb00 )
23889                 ) {
23890                         return match;
23891                 }  
23892          
23893                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
23894                 return "&#" + cc + ";";
23895                 
23896                 
23897             });
23898             
23899             
23900              
23901             if(this.owner.fireEvent('beforesync', this, html) !== false){
23902                 this.el.dom.value = html;
23903                 this.owner.fireEvent('sync', this, html);
23904             }
23905         }
23906     },
23907
23908     /**
23909      * Protected method that will not generally be called directly. Pushes the value of the textarea
23910      * into the iframe editor.
23911      */
23912     pushValue : function(){
23913         if(this.initialized){
23914             var v = this.el.dom.value.trim();
23915             
23916 //            if(v.length < 1){
23917 //                v = '&#160;';
23918 //            }
23919             
23920             if(this.owner.fireEvent('beforepush', this, v) !== false){
23921                 var d = (this.doc.body || this.doc.documentElement);
23922                 d.innerHTML = v;
23923                 this.cleanUpPaste();
23924                 this.el.dom.value = d.innerHTML;
23925                 this.owner.fireEvent('push', this, v);
23926             }
23927         }
23928     },
23929
23930     // private
23931     deferFocus : function(){
23932         this.focus.defer(10, this);
23933     },
23934
23935     // doc'ed in Field
23936     focus : function(){
23937         if(this.win && !this.sourceEditMode){
23938             this.win.focus();
23939         }else{
23940             this.el.focus();
23941         }
23942     },
23943     
23944     assignDocWin: function()
23945     {
23946         var iframe = this.iframe;
23947         
23948          if(Roo.isIE){
23949             this.doc = iframe.contentWindow.document;
23950             this.win = iframe.contentWindow;
23951         } else {
23952 //            if (!Roo.get(this.frameId)) {
23953 //                return;
23954 //            }
23955 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
23956 //            this.win = Roo.get(this.frameId).dom.contentWindow;
23957             
23958             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
23959                 return;
23960             }
23961             
23962             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
23963             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
23964         }
23965     },
23966     
23967     // private
23968     initEditor : function(){
23969         //console.log("INIT EDITOR");
23970         this.assignDocWin();
23971         
23972         
23973         
23974         this.doc.designMode="on";
23975         this.doc.open();
23976         this.doc.write(this.getDocMarkup());
23977         this.doc.close();
23978         
23979         var dbody = (this.doc.body || this.doc.documentElement);
23980         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
23981         // this copies styles from the containing element into thsi one..
23982         // not sure why we need all of this..
23983         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
23984         
23985         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
23986         //ss['background-attachment'] = 'fixed'; // w3c
23987         dbody.bgProperties = 'fixed'; // ie
23988         //Roo.DomHelper.applyStyles(dbody, ss);
23989         Roo.EventManager.on(this.doc, {
23990             //'mousedown': this.onEditorEvent,
23991             'mouseup': this.onEditorEvent,
23992             'dblclick': this.onEditorEvent,
23993             'click': this.onEditorEvent,
23994             'keyup': this.onEditorEvent,
23995             buffer:100,
23996             scope: this
23997         });
23998         if(Roo.isGecko){
23999             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24000         }
24001         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24002             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24003         }
24004         this.initialized = true;
24005
24006         this.owner.fireEvent('initialize', this);
24007         this.pushValue();
24008     },
24009
24010     // private
24011     onDestroy : function(){
24012         
24013         
24014         
24015         if(this.rendered){
24016             
24017             //for (var i =0; i < this.toolbars.length;i++) {
24018             //    // fixme - ask toolbars for heights?
24019             //    this.toolbars[i].onDestroy();
24020            // }
24021             
24022             //this.wrap.dom.innerHTML = '';
24023             //this.wrap.remove();
24024         }
24025     },
24026
24027     // private
24028     onFirstFocus : function(){
24029         
24030         this.assignDocWin();
24031         
24032         
24033         this.activated = true;
24034          
24035     
24036         if(Roo.isGecko){ // prevent silly gecko errors
24037             this.win.focus();
24038             var s = this.win.getSelection();
24039             if(!s.focusNode || s.focusNode.nodeType != 3){
24040                 var r = s.getRangeAt(0);
24041                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24042                 r.collapse(true);
24043                 this.deferFocus();
24044             }
24045             try{
24046                 this.execCmd('useCSS', true);
24047                 this.execCmd('styleWithCSS', false);
24048             }catch(e){}
24049         }
24050         this.owner.fireEvent('activate', this);
24051     },
24052
24053     // private
24054     adjustFont: function(btn){
24055         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24056         //if(Roo.isSafari){ // safari
24057         //    adjust *= 2;
24058        // }
24059         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24060         if(Roo.isSafari){ // safari
24061             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24062             v =  (v < 10) ? 10 : v;
24063             v =  (v > 48) ? 48 : v;
24064             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24065             
24066         }
24067         
24068         
24069         v = Math.max(1, v+adjust);
24070         
24071         this.execCmd('FontSize', v  );
24072     },
24073
24074     onEditorEvent : function(e)
24075     {
24076         this.owner.fireEvent('editorevent', this, e);
24077       //  this.updateToolbar();
24078         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24079     },
24080
24081     insertTag : function(tg)
24082     {
24083         // could be a bit smarter... -> wrap the current selected tRoo..
24084         if (tg.toLowerCase() == 'span' ||
24085             tg.toLowerCase() == 'code' ||
24086             tg.toLowerCase() == 'sup' ||
24087             tg.toLowerCase() == 'sub' 
24088             ) {
24089             
24090             range = this.createRange(this.getSelection());
24091             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24092             wrappingNode.appendChild(range.extractContents());
24093             range.insertNode(wrappingNode);
24094
24095             return;
24096             
24097             
24098             
24099         }
24100         this.execCmd("formatblock",   tg);
24101         
24102     },
24103     
24104     insertText : function(txt)
24105     {
24106         
24107         
24108         var range = this.createRange();
24109         range.deleteContents();
24110                //alert(Sender.getAttribute('label'));
24111                
24112         range.insertNode(this.doc.createTextNode(txt));
24113     } ,
24114     
24115      
24116
24117     /**
24118      * Executes a Midas editor command on the editor document and performs necessary focus and
24119      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24120      * @param {String} cmd The Midas command
24121      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24122      */
24123     relayCmd : function(cmd, value){
24124         this.win.focus();
24125         this.execCmd(cmd, value);
24126         this.owner.fireEvent('editorevent', this);
24127         //this.updateToolbar();
24128         this.owner.deferFocus();
24129     },
24130
24131     /**
24132      * Executes a Midas editor command directly on the editor document.
24133      * For visual commands, you should use {@link #relayCmd} instead.
24134      * <b>This should only be called after the editor is initialized.</b>
24135      * @param {String} cmd The Midas command
24136      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24137      */
24138     execCmd : function(cmd, value){
24139         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24140         this.syncValue();
24141     },
24142  
24143  
24144    
24145     /**
24146      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24147      * to insert tRoo.
24148      * @param {String} text | dom node.. 
24149      */
24150     insertAtCursor : function(text)
24151     {
24152         
24153         if(!this.activated){
24154             return;
24155         }
24156         /*
24157         if(Roo.isIE){
24158             this.win.focus();
24159             var r = this.doc.selection.createRange();
24160             if(r){
24161                 r.collapse(true);
24162                 r.pasteHTML(text);
24163                 this.syncValue();
24164                 this.deferFocus();
24165             
24166             }
24167             return;
24168         }
24169         */
24170         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24171             this.win.focus();
24172             
24173             
24174             // from jquery ui (MIT licenced)
24175             var range, node;
24176             var win = this.win;
24177             
24178             if (win.getSelection && win.getSelection().getRangeAt) {
24179                 range = win.getSelection().getRangeAt(0);
24180                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24181                 range.insertNode(node);
24182             } else if (win.document.selection && win.document.selection.createRange) {
24183                 // no firefox support
24184                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24185                 win.document.selection.createRange().pasteHTML(txt);
24186             } else {
24187                 // no firefox support
24188                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24189                 this.execCmd('InsertHTML', txt);
24190             } 
24191             
24192             this.syncValue();
24193             
24194             this.deferFocus();
24195         }
24196     },
24197  // private
24198     mozKeyPress : function(e){
24199         if(e.ctrlKey){
24200             var c = e.getCharCode(), cmd;
24201           
24202             if(c > 0){
24203                 c = String.fromCharCode(c).toLowerCase();
24204                 switch(c){
24205                     case 'b':
24206                         cmd = 'bold';
24207                         break;
24208                     case 'i':
24209                         cmd = 'italic';
24210                         break;
24211                     
24212                     case 'u':
24213                         cmd = 'underline';
24214                         break;
24215                     
24216                     case 'v':
24217                         this.cleanUpPaste.defer(100, this);
24218                         return;
24219                         
24220                 }
24221                 if(cmd){
24222                     this.win.focus();
24223                     this.execCmd(cmd);
24224                     this.deferFocus();
24225                     e.preventDefault();
24226                 }
24227                 
24228             }
24229         }
24230     },
24231
24232     // private
24233     fixKeys : function(){ // load time branching for fastest keydown performance
24234         if(Roo.isIE){
24235             return function(e){
24236                 var k = e.getKey(), r;
24237                 if(k == e.TAB){
24238                     e.stopEvent();
24239                     r = this.doc.selection.createRange();
24240                     if(r){
24241                         r.collapse(true);
24242                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24243                         this.deferFocus();
24244                     }
24245                     return;
24246                 }
24247                 
24248                 if(k == e.ENTER){
24249                     r = this.doc.selection.createRange();
24250                     if(r){
24251                         var target = r.parentElement();
24252                         if(!target || target.tagName.toLowerCase() != 'li'){
24253                             e.stopEvent();
24254                             r.pasteHTML('<br />');
24255                             r.collapse(false);
24256                             r.select();
24257                         }
24258                     }
24259                 }
24260                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24261                     this.cleanUpPaste.defer(100, this);
24262                     return;
24263                 }
24264                 
24265                 
24266             };
24267         }else if(Roo.isOpera){
24268             return function(e){
24269                 var k = e.getKey();
24270                 if(k == e.TAB){
24271                     e.stopEvent();
24272                     this.win.focus();
24273                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24274                     this.deferFocus();
24275                 }
24276                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24277                     this.cleanUpPaste.defer(100, this);
24278                     return;
24279                 }
24280                 
24281             };
24282         }else if(Roo.isSafari){
24283             return function(e){
24284                 var k = e.getKey();
24285                 
24286                 if(k == e.TAB){
24287                     e.stopEvent();
24288                     this.execCmd('InsertText','\t');
24289                     this.deferFocus();
24290                     return;
24291                 }
24292                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24293                     this.cleanUpPaste.defer(100, this);
24294                     return;
24295                 }
24296                 
24297              };
24298         }
24299     }(),
24300     
24301     getAllAncestors: function()
24302     {
24303         var p = this.getSelectedNode();
24304         var a = [];
24305         if (!p) {
24306             a.push(p); // push blank onto stack..
24307             p = this.getParentElement();
24308         }
24309         
24310         
24311         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24312             a.push(p);
24313             p = p.parentNode;
24314         }
24315         a.push(this.doc.body);
24316         return a;
24317     },
24318     lastSel : false,
24319     lastSelNode : false,
24320     
24321     
24322     getSelection : function() 
24323     {
24324         this.assignDocWin();
24325         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24326     },
24327     
24328     getSelectedNode: function() 
24329     {
24330         // this may only work on Gecko!!!
24331         
24332         // should we cache this!!!!
24333         
24334         
24335         
24336          
24337         var range = this.createRange(this.getSelection()).cloneRange();
24338         
24339         if (Roo.isIE) {
24340             var parent = range.parentElement();
24341             while (true) {
24342                 var testRange = range.duplicate();
24343                 testRange.moveToElementText(parent);
24344                 if (testRange.inRange(range)) {
24345                     break;
24346                 }
24347                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24348                     break;
24349                 }
24350                 parent = parent.parentElement;
24351             }
24352             return parent;
24353         }
24354         
24355         // is ancestor a text element.
24356         var ac =  range.commonAncestorContainer;
24357         if (ac.nodeType == 3) {
24358             ac = ac.parentNode;
24359         }
24360         
24361         var ar = ac.childNodes;
24362          
24363         var nodes = [];
24364         var other_nodes = [];
24365         var has_other_nodes = false;
24366         for (var i=0;i<ar.length;i++) {
24367             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24368                 continue;
24369             }
24370             // fullly contained node.
24371             
24372             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24373                 nodes.push(ar[i]);
24374                 continue;
24375             }
24376             
24377             // probably selected..
24378             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24379                 other_nodes.push(ar[i]);
24380                 continue;
24381             }
24382             // outer..
24383             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24384                 continue;
24385             }
24386             
24387             
24388             has_other_nodes = true;
24389         }
24390         if (!nodes.length && other_nodes.length) {
24391             nodes= other_nodes;
24392         }
24393         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24394             return false;
24395         }
24396         
24397         return nodes[0];
24398     },
24399     createRange: function(sel)
24400     {
24401         // this has strange effects when using with 
24402         // top toolbar - not sure if it's a great idea.
24403         //this.editor.contentWindow.focus();
24404         if (typeof sel != "undefined") {
24405             try {
24406                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24407             } catch(e) {
24408                 return this.doc.createRange();
24409             }
24410         } else {
24411             return this.doc.createRange();
24412         }
24413     },
24414     getParentElement: function()
24415     {
24416         
24417         this.assignDocWin();
24418         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24419         
24420         var range = this.createRange(sel);
24421          
24422         try {
24423             var p = range.commonAncestorContainer;
24424             while (p.nodeType == 3) { // text node
24425                 p = p.parentNode;
24426             }
24427             return p;
24428         } catch (e) {
24429             return null;
24430         }
24431     
24432     },
24433     /***
24434      *
24435      * Range intersection.. the hard stuff...
24436      *  '-1' = before
24437      *  '0' = hits..
24438      *  '1' = after.
24439      *         [ -- selected range --- ]
24440      *   [fail]                        [fail]
24441      *
24442      *    basically..
24443      *      if end is before start or  hits it. fail.
24444      *      if start is after end or hits it fail.
24445      *
24446      *   if either hits (but other is outside. - then it's not 
24447      *   
24448      *    
24449      **/
24450     
24451     
24452     // @see http://www.thismuchiknow.co.uk/?p=64.
24453     rangeIntersectsNode : function(range, node)
24454     {
24455         var nodeRange = node.ownerDocument.createRange();
24456         try {
24457             nodeRange.selectNode(node);
24458         } catch (e) {
24459             nodeRange.selectNodeContents(node);
24460         }
24461     
24462         var rangeStartRange = range.cloneRange();
24463         rangeStartRange.collapse(true);
24464     
24465         var rangeEndRange = range.cloneRange();
24466         rangeEndRange.collapse(false);
24467     
24468         var nodeStartRange = nodeRange.cloneRange();
24469         nodeStartRange.collapse(true);
24470     
24471         var nodeEndRange = nodeRange.cloneRange();
24472         nodeEndRange.collapse(false);
24473     
24474         return rangeStartRange.compareBoundaryPoints(
24475                  Range.START_TO_START, nodeEndRange) == -1 &&
24476                rangeEndRange.compareBoundaryPoints(
24477                  Range.START_TO_START, nodeStartRange) == 1;
24478         
24479          
24480     },
24481     rangeCompareNode : function(range, node)
24482     {
24483         var nodeRange = node.ownerDocument.createRange();
24484         try {
24485             nodeRange.selectNode(node);
24486         } catch (e) {
24487             nodeRange.selectNodeContents(node);
24488         }
24489         
24490         
24491         range.collapse(true);
24492     
24493         nodeRange.collapse(true);
24494      
24495         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
24496         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
24497          
24498         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
24499         
24500         var nodeIsBefore   =  ss == 1;
24501         var nodeIsAfter    = ee == -1;
24502         
24503         if (nodeIsBefore && nodeIsAfter) {
24504             return 0; // outer
24505         }
24506         if (!nodeIsBefore && nodeIsAfter) {
24507             return 1; //right trailed.
24508         }
24509         
24510         if (nodeIsBefore && !nodeIsAfter) {
24511             return 2;  // left trailed.
24512         }
24513         // fully contined.
24514         return 3;
24515     },
24516
24517     // private? - in a new class?
24518     cleanUpPaste :  function()
24519     {
24520         // cleans up the whole document..
24521         Roo.log('cleanuppaste');
24522         
24523         this.cleanUpChildren(this.doc.body);
24524         var clean = this.cleanWordChars(this.doc.body.innerHTML);
24525         if (clean != this.doc.body.innerHTML) {
24526             this.doc.body.innerHTML = clean;
24527         }
24528         
24529     },
24530     
24531     cleanWordChars : function(input) {// change the chars to hex code
24532         var he = Roo.HtmlEditorCore;
24533         
24534         var output = input;
24535         Roo.each(he.swapCodes, function(sw) { 
24536             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
24537             
24538             output = output.replace(swapper, sw[1]);
24539         });
24540         
24541         return output;
24542     },
24543     
24544     
24545     cleanUpChildren : function (n)
24546     {
24547         if (!n.childNodes.length) {
24548             return;
24549         }
24550         for (var i = n.childNodes.length-1; i > -1 ; i--) {
24551            this.cleanUpChild(n.childNodes[i]);
24552         }
24553     },
24554     
24555     
24556         
24557     
24558     cleanUpChild : function (node)
24559     {
24560         var ed = this;
24561         //console.log(node);
24562         if (node.nodeName == "#text") {
24563             // clean up silly Windows -- stuff?
24564             return; 
24565         }
24566         if (node.nodeName == "#comment") {
24567             node.parentNode.removeChild(node);
24568             // clean up silly Windows -- stuff?
24569             return; 
24570         }
24571         var lcname = node.tagName.toLowerCase();
24572         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
24573         // whitelist of tags..
24574         
24575         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
24576             // remove node.
24577             node.parentNode.removeChild(node);
24578             return;
24579             
24580         }
24581         
24582         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
24583         
24584         // spans with no attributes - just remove them..
24585         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
24586             remove_keep_children = true;
24587         }
24588         
24589         // remove <a name=....> as rendering on yahoo mailer is borked with this.
24590         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
24591         
24592         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
24593         //    remove_keep_children = true;
24594         //}
24595         
24596         if (remove_keep_children) {
24597             this.cleanUpChildren(node);
24598             // inserts everything just before this node...
24599             while (node.childNodes.length) {
24600                 var cn = node.childNodes[0];
24601                 node.removeChild(cn);
24602                 node.parentNode.insertBefore(cn, node);
24603             }
24604             node.parentNode.removeChild(node);
24605             return;
24606         }
24607         
24608         if (!node.attributes || !node.attributes.length) {
24609             
24610           
24611             
24612             
24613             this.cleanUpChildren(node);
24614             return;
24615         }
24616         
24617         function cleanAttr(n,v)
24618         {
24619             
24620             if (v.match(/^\./) || v.match(/^\//)) {
24621                 return;
24622             }
24623             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
24624                 return;
24625             }
24626             if (v.match(/^#/)) {
24627                 return;
24628             }
24629 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
24630             node.removeAttribute(n);
24631             
24632         }
24633         
24634         var cwhite = this.cwhite;
24635         var cblack = this.cblack;
24636             
24637         function cleanStyle(n,v)
24638         {
24639             if (v.match(/expression/)) { //XSS?? should we even bother..
24640                 node.removeAttribute(n);
24641                 return;
24642             }
24643             
24644             var parts = v.split(/;/);
24645             var clean = [];
24646             
24647             Roo.each(parts, function(p) {
24648                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
24649                 if (!p.length) {
24650                     return true;
24651                 }
24652                 var l = p.split(':').shift().replace(/\s+/g,'');
24653                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
24654                 
24655                 if ( cwhite.length && cblack.indexOf(l) > -1) {
24656 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24657                     //node.removeAttribute(n);
24658                     return true;
24659                 }
24660                 //Roo.log()
24661                 // only allow 'c whitelisted system attributes'
24662                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
24663 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24664                     //node.removeAttribute(n);
24665                     return true;
24666                 }
24667                 
24668                 
24669                  
24670                 
24671                 clean.push(p);
24672                 return true;
24673             });
24674             if (clean.length) { 
24675                 node.setAttribute(n, clean.join(';'));
24676             } else {
24677                 node.removeAttribute(n);
24678             }
24679             
24680         }
24681         
24682         
24683         for (var i = node.attributes.length-1; i > -1 ; i--) {
24684             var a = node.attributes[i];
24685             //console.log(a);
24686             
24687             if (a.name.toLowerCase().substr(0,2)=='on')  {
24688                 node.removeAttribute(a.name);
24689                 continue;
24690             }
24691             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
24692                 node.removeAttribute(a.name);
24693                 continue;
24694             }
24695             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
24696                 cleanAttr(a.name,a.value); // fixme..
24697                 continue;
24698             }
24699             if (a.name == 'style') {
24700                 cleanStyle(a.name,a.value);
24701                 continue;
24702             }
24703             /// clean up MS crap..
24704             // tecnically this should be a list of valid class'es..
24705             
24706             
24707             if (a.name == 'class') {
24708                 if (a.value.match(/^Mso/)) {
24709                     node.removeAttribute('class');
24710                 }
24711                 
24712                 if (a.value.match(/^body$/)) {
24713                     node.removeAttribute('class');
24714                 }
24715                 continue;
24716             }
24717             
24718             // style cleanup!?
24719             // class cleanup?
24720             
24721         }
24722         
24723         
24724         this.cleanUpChildren(node);
24725         
24726         
24727     },
24728     
24729     /**
24730      * Clean up MS wordisms...
24731      */
24732     cleanWord : function(node)
24733     {
24734         if (!node) {
24735             this.cleanWord(this.doc.body);
24736             return;
24737         }
24738         
24739         if(
24740                 node.nodeName == 'SPAN' &&
24741                 !node.hasAttributes() &&
24742                 node.childNodes.length == 1 &&
24743                 node.firstChild.nodeName == "#text"  
24744         ) {
24745             var textNode = node.firstChild;
24746             node.removeChild(textNode);
24747             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
24748                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
24749             }
24750             node.parentNode.insertBefore(textNode, node);
24751             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
24752                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
24753             }
24754             node.parentNode.removeChild(node);
24755         }
24756         
24757         if (node.nodeName == "#text") {
24758             // clean up silly Windows -- stuff?
24759             return; 
24760         }
24761         if (node.nodeName == "#comment") {
24762             node.parentNode.removeChild(node);
24763             // clean up silly Windows -- stuff?
24764             return; 
24765         }
24766         
24767         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
24768             node.parentNode.removeChild(node);
24769             return;
24770         }
24771         //Roo.log(node.tagName);
24772         // remove - but keep children..
24773         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
24774             //Roo.log('-- removed');
24775             while (node.childNodes.length) {
24776                 var cn = node.childNodes[0];
24777                 node.removeChild(cn);
24778                 node.parentNode.insertBefore(cn, node);
24779                 // move node to parent - and clean it..
24780                 this.cleanWord(cn);
24781             }
24782             node.parentNode.removeChild(node);
24783             /// no need to iterate chidlren = it's got none..
24784             //this.iterateChildren(node, this.cleanWord);
24785             return;
24786         }
24787         // clean styles
24788         if (node.className.length) {
24789             
24790             var cn = node.className.split(/\W+/);
24791             var cna = [];
24792             Roo.each(cn, function(cls) {
24793                 if (cls.match(/Mso[a-zA-Z]+/)) {
24794                     return;
24795                 }
24796                 cna.push(cls);
24797             });
24798             node.className = cna.length ? cna.join(' ') : '';
24799             if (!cna.length) {
24800                 node.removeAttribute("class");
24801             }
24802         }
24803         
24804         if (node.hasAttribute("lang")) {
24805             node.removeAttribute("lang");
24806         }
24807         
24808         if (node.hasAttribute("style")) {
24809             
24810             var styles = node.getAttribute("style").split(";");
24811             var nstyle = [];
24812             Roo.each(styles, function(s) {
24813                 if (!s.match(/:/)) {
24814                     return;
24815                 }
24816                 var kv = s.split(":");
24817                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
24818                     return;
24819                 }
24820                 // what ever is left... we allow.
24821                 nstyle.push(s);
24822             });
24823             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
24824             if (!nstyle.length) {
24825                 node.removeAttribute('style');
24826             }
24827         }
24828         this.iterateChildren(node, this.cleanWord);
24829         
24830         
24831         
24832     },
24833     /**
24834      * iterateChildren of a Node, calling fn each time, using this as the scole..
24835      * @param {DomNode} node node to iterate children of.
24836      * @param {Function} fn method of this class to call on each item.
24837      */
24838     iterateChildren : function(node, fn)
24839     {
24840         if (!node.childNodes.length) {
24841                 return;
24842         }
24843         for (var i = node.childNodes.length-1; i > -1 ; i--) {
24844            fn.call(this, node.childNodes[i])
24845         }
24846     },
24847     
24848     
24849     /**
24850      * cleanTableWidths.
24851      *
24852      * Quite often pasting from word etc.. results in tables with column and widths.
24853      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
24854      *
24855      */
24856     cleanTableWidths : function(node)
24857     {
24858          
24859          
24860         if (!node) {
24861             this.cleanTableWidths(this.doc.body);
24862             return;
24863         }
24864         
24865         // ignore list...
24866         if (node.nodeName == "#text" || node.nodeName == "#comment") {
24867             return; 
24868         }
24869         Roo.log(node.tagName);
24870         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
24871             this.iterateChildren(node, this.cleanTableWidths);
24872             return;
24873         }
24874         if (node.hasAttribute('width')) {
24875             node.removeAttribute('width');
24876         }
24877         
24878          
24879         if (node.hasAttribute("style")) {
24880             // pretty basic...
24881             
24882             var styles = node.getAttribute("style").split(";");
24883             var nstyle = [];
24884             Roo.each(styles, function(s) {
24885                 if (!s.match(/:/)) {
24886                     return;
24887                 }
24888                 var kv = s.split(":");
24889                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
24890                     return;
24891                 }
24892                 // what ever is left... we allow.
24893                 nstyle.push(s);
24894             });
24895             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
24896             if (!nstyle.length) {
24897                 node.removeAttribute('style');
24898             }
24899         }
24900         
24901         this.iterateChildren(node, this.cleanTableWidths);
24902         
24903         
24904     },
24905     
24906     
24907     
24908     
24909     domToHTML : function(currentElement, depth, nopadtext) {
24910         
24911         depth = depth || 0;
24912         nopadtext = nopadtext || false;
24913     
24914         if (!currentElement) {
24915             return this.domToHTML(this.doc.body);
24916         }
24917         
24918         //Roo.log(currentElement);
24919         var j;
24920         var allText = false;
24921         var nodeName = currentElement.nodeName;
24922         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
24923         
24924         if  (nodeName == '#text') {
24925             
24926             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
24927         }
24928         
24929         
24930         var ret = '';
24931         if (nodeName != 'BODY') {
24932              
24933             var i = 0;
24934             // Prints the node tagName, such as <A>, <IMG>, etc
24935             if (tagName) {
24936                 var attr = [];
24937                 for(i = 0; i < currentElement.attributes.length;i++) {
24938                     // quoting?
24939                     var aname = currentElement.attributes.item(i).name;
24940                     if (!currentElement.attributes.item(i).value.length) {
24941                         continue;
24942                     }
24943                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
24944                 }
24945                 
24946                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
24947             } 
24948             else {
24949                 
24950                 // eack
24951             }
24952         } else {
24953             tagName = false;
24954         }
24955         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
24956             return ret;
24957         }
24958         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
24959             nopadtext = true;
24960         }
24961         
24962         
24963         // Traverse the tree
24964         i = 0;
24965         var currentElementChild = currentElement.childNodes.item(i);
24966         var allText = true;
24967         var innerHTML  = '';
24968         lastnode = '';
24969         while (currentElementChild) {
24970             // Formatting code (indent the tree so it looks nice on the screen)
24971             var nopad = nopadtext;
24972             if (lastnode == 'SPAN') {
24973                 nopad  = true;
24974             }
24975             // text
24976             if  (currentElementChild.nodeName == '#text') {
24977                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
24978                 toadd = nopadtext ? toadd : toadd.trim();
24979                 if (!nopad && toadd.length > 80) {
24980                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
24981                 }
24982                 innerHTML  += toadd;
24983                 
24984                 i++;
24985                 currentElementChild = currentElement.childNodes.item(i);
24986                 lastNode = '';
24987                 continue;
24988             }
24989             allText = false;
24990             
24991             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
24992                 
24993             // Recursively traverse the tree structure of the child node
24994             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
24995             lastnode = currentElementChild.nodeName;
24996             i++;
24997             currentElementChild=currentElement.childNodes.item(i);
24998         }
24999         
25000         ret += innerHTML;
25001         
25002         if (!allText) {
25003                 // The remaining code is mostly for formatting the tree
25004             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25005         }
25006         
25007         
25008         if (tagName) {
25009             ret+= "</"+tagName+">";
25010         }
25011         return ret;
25012         
25013     },
25014         
25015     applyBlacklists : function()
25016     {
25017         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25018         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25019         
25020         this.white = [];
25021         this.black = [];
25022         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25023             if (b.indexOf(tag) > -1) {
25024                 return;
25025             }
25026             this.white.push(tag);
25027             
25028         }, this);
25029         
25030         Roo.each(w, function(tag) {
25031             if (b.indexOf(tag) > -1) {
25032                 return;
25033             }
25034             if (this.white.indexOf(tag) > -1) {
25035                 return;
25036             }
25037             this.white.push(tag);
25038             
25039         }, this);
25040         
25041         
25042         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25043             if (w.indexOf(tag) > -1) {
25044                 return;
25045             }
25046             this.black.push(tag);
25047             
25048         }, this);
25049         
25050         Roo.each(b, function(tag) {
25051             if (w.indexOf(tag) > -1) {
25052                 return;
25053             }
25054             if (this.black.indexOf(tag) > -1) {
25055                 return;
25056             }
25057             this.black.push(tag);
25058             
25059         }, this);
25060         
25061         
25062         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25063         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25064         
25065         this.cwhite = [];
25066         this.cblack = [];
25067         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25068             if (b.indexOf(tag) > -1) {
25069                 return;
25070             }
25071             this.cwhite.push(tag);
25072             
25073         }, this);
25074         
25075         Roo.each(w, function(tag) {
25076             if (b.indexOf(tag) > -1) {
25077                 return;
25078             }
25079             if (this.cwhite.indexOf(tag) > -1) {
25080                 return;
25081             }
25082             this.cwhite.push(tag);
25083             
25084         }, this);
25085         
25086         
25087         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25088             if (w.indexOf(tag) > -1) {
25089                 return;
25090             }
25091             this.cblack.push(tag);
25092             
25093         }, this);
25094         
25095         Roo.each(b, function(tag) {
25096             if (w.indexOf(tag) > -1) {
25097                 return;
25098             }
25099             if (this.cblack.indexOf(tag) > -1) {
25100                 return;
25101             }
25102             this.cblack.push(tag);
25103             
25104         }, this);
25105     },
25106     
25107     setStylesheets : function(stylesheets)
25108     {
25109         if(typeof(stylesheets) == 'string'){
25110             Roo.get(this.iframe.contentDocument.head).createChild({
25111                 tag : 'link',
25112                 rel : 'stylesheet',
25113                 type : 'text/css',
25114                 href : stylesheets
25115             });
25116             
25117             return;
25118         }
25119         var _this = this;
25120      
25121         Roo.each(stylesheets, function(s) {
25122             if(!s.length){
25123                 return;
25124             }
25125             
25126             Roo.get(_this.iframe.contentDocument.head).createChild({
25127                 tag : 'link',
25128                 rel : 'stylesheet',
25129                 type : 'text/css',
25130                 href : s
25131             });
25132         });
25133
25134         
25135     },
25136     
25137     removeStylesheets : function()
25138     {
25139         var _this = this;
25140         
25141         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25142             s.remove();
25143         });
25144     },
25145     
25146     setStyle : function(style)
25147     {
25148         Roo.get(this.iframe.contentDocument.head).createChild({
25149             tag : 'style',
25150             type : 'text/css',
25151             html : style
25152         });
25153
25154         return;
25155     }
25156     
25157     // hide stuff that is not compatible
25158     /**
25159      * @event blur
25160      * @hide
25161      */
25162     /**
25163      * @event change
25164      * @hide
25165      */
25166     /**
25167      * @event focus
25168      * @hide
25169      */
25170     /**
25171      * @event specialkey
25172      * @hide
25173      */
25174     /**
25175      * @cfg {String} fieldClass @hide
25176      */
25177     /**
25178      * @cfg {String} focusClass @hide
25179      */
25180     /**
25181      * @cfg {String} autoCreate @hide
25182      */
25183     /**
25184      * @cfg {String} inputType @hide
25185      */
25186     /**
25187      * @cfg {String} invalidClass @hide
25188      */
25189     /**
25190      * @cfg {String} invalidText @hide
25191      */
25192     /**
25193      * @cfg {String} msgFx @hide
25194      */
25195     /**
25196      * @cfg {String} validateOnBlur @hide
25197      */
25198 });
25199
25200 Roo.HtmlEditorCore.white = [
25201         'area', 'br', 'img', 'input', 'hr', 'wbr',
25202         
25203        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25204        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25205        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25206        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25207        'table',   'ul',         'xmp', 
25208        
25209        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25210       'thead',   'tr', 
25211      
25212       'dir', 'menu', 'ol', 'ul', 'dl',
25213        
25214       'embed',  'object'
25215 ];
25216
25217
25218 Roo.HtmlEditorCore.black = [
25219     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25220         'applet', // 
25221         'base',   'basefont', 'bgsound', 'blink',  'body', 
25222         'frame',  'frameset', 'head',    'html',   'ilayer', 
25223         'iframe', 'layer',  'link',     'meta',    'object',   
25224         'script', 'style' ,'title',  'xml' // clean later..
25225 ];
25226 Roo.HtmlEditorCore.clean = [
25227     'script', 'style', 'title', 'xml'
25228 ];
25229 Roo.HtmlEditorCore.remove = [
25230     'font'
25231 ];
25232 // attributes..
25233
25234 Roo.HtmlEditorCore.ablack = [
25235     'on'
25236 ];
25237     
25238 Roo.HtmlEditorCore.aclean = [ 
25239     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
25240 ];
25241
25242 // protocols..
25243 Roo.HtmlEditorCore.pwhite= [
25244         'http',  'https',  'mailto'
25245 ];
25246
25247 // white listed style attributes.
25248 Roo.HtmlEditorCore.cwhite= [
25249       //  'text-align', /// default is to allow most things..
25250       
25251          
25252 //        'font-size'//??
25253 ];
25254
25255 // black listed style attributes.
25256 Roo.HtmlEditorCore.cblack= [
25257       //  'font-size' -- this can be set by the project 
25258 ];
25259
25260
25261 Roo.HtmlEditorCore.swapCodes   =[ 
25262     [    8211, "--" ], 
25263     [    8212, "--" ], 
25264     [    8216,  "'" ],  
25265     [    8217, "'" ],  
25266     [    8220, '"' ],  
25267     [    8221, '"' ],  
25268     [    8226, "*" ],  
25269     [    8230, "..." ]
25270 ]; 
25271
25272     /*
25273  * - LGPL
25274  *
25275  * HtmlEditor
25276  * 
25277  */
25278
25279 /**
25280  * @class Roo.bootstrap.HtmlEditor
25281  * @extends Roo.bootstrap.TextArea
25282  * Bootstrap HtmlEditor class
25283
25284  * @constructor
25285  * Create a new HtmlEditor
25286  * @param {Object} config The config object
25287  */
25288
25289 Roo.bootstrap.HtmlEditor = function(config){
25290     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25291     if (!this.toolbars) {
25292         this.toolbars = [];
25293     }
25294     
25295     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25296     this.addEvents({
25297             /**
25298              * @event initialize
25299              * Fires when the editor is fully initialized (including the iframe)
25300              * @param {HtmlEditor} this
25301              */
25302             initialize: true,
25303             /**
25304              * @event activate
25305              * Fires when the editor is first receives the focus. Any insertion must wait
25306              * until after this event.
25307              * @param {HtmlEditor} this
25308              */
25309             activate: true,
25310              /**
25311              * @event beforesync
25312              * Fires before the textarea is updated with content from the editor iframe. Return false
25313              * to cancel the sync.
25314              * @param {HtmlEditor} this
25315              * @param {String} html
25316              */
25317             beforesync: true,
25318              /**
25319              * @event beforepush
25320              * Fires before the iframe editor is updated with content from the textarea. Return false
25321              * to cancel the push.
25322              * @param {HtmlEditor} this
25323              * @param {String} html
25324              */
25325             beforepush: true,
25326              /**
25327              * @event sync
25328              * Fires when the textarea is updated with content from the editor iframe.
25329              * @param {HtmlEditor} this
25330              * @param {String} html
25331              */
25332             sync: true,
25333              /**
25334              * @event push
25335              * Fires when the iframe editor is updated with content from the textarea.
25336              * @param {HtmlEditor} this
25337              * @param {String} html
25338              */
25339             push: true,
25340              /**
25341              * @event editmodechange
25342              * Fires when the editor switches edit modes
25343              * @param {HtmlEditor} this
25344              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25345              */
25346             editmodechange: true,
25347             /**
25348              * @event editorevent
25349              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25350              * @param {HtmlEditor} this
25351              */
25352             editorevent: true,
25353             /**
25354              * @event firstfocus
25355              * Fires when on first focus - needed by toolbars..
25356              * @param {HtmlEditor} this
25357              */
25358             firstfocus: true,
25359             /**
25360              * @event autosave
25361              * Auto save the htmlEditor value as a file into Events
25362              * @param {HtmlEditor} this
25363              */
25364             autosave: true,
25365             /**
25366              * @event savedpreview
25367              * preview the saved version of htmlEditor
25368              * @param {HtmlEditor} this
25369              */
25370             savedpreview: true
25371         });
25372 };
25373
25374
25375 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
25376     
25377     
25378       /**
25379      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25380      */
25381     toolbars : false,
25382     
25383      /**
25384     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25385     */
25386     btns : [],
25387    
25388      /**
25389      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25390      *                        Roo.resizable.
25391      */
25392     resizable : false,
25393      /**
25394      * @cfg {Number} height (in pixels)
25395      */   
25396     height: 300,
25397    /**
25398      * @cfg {Number} width (in pixels)
25399      */   
25400     width: false,
25401     
25402     /**
25403      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25404      * 
25405      */
25406     stylesheets: false,
25407     
25408     // id of frame..
25409     frameId: false,
25410     
25411     // private properties
25412     validationEvent : false,
25413     deferHeight: true,
25414     initialized : false,
25415     activated : false,
25416     
25417     onFocus : Roo.emptyFn,
25418     iframePad:3,
25419     hideMode:'offsets',
25420     
25421     tbContainer : false,
25422     
25423     bodyCls : '',
25424     
25425     toolbarContainer :function() {
25426         return this.wrap.select('.x-html-editor-tb',true).first();
25427     },
25428
25429     /**
25430      * Protected method that will not generally be called directly. It
25431      * is called when the editor creates its toolbar. Override this method if you need to
25432      * add custom toolbar buttons.
25433      * @param {HtmlEditor} editor
25434      */
25435     createToolbar : function(){
25436         Roo.log('renewing');
25437         Roo.log("create toolbars");
25438         
25439         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25440         this.toolbars[0].render(this.toolbarContainer());
25441         
25442         return;
25443         
25444 //        if (!editor.toolbars || !editor.toolbars.length) {
25445 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25446 //        }
25447 //        
25448 //        for (var i =0 ; i < editor.toolbars.length;i++) {
25449 //            editor.toolbars[i] = Roo.factory(
25450 //                    typeof(editor.toolbars[i]) == 'string' ?
25451 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
25452 //                Roo.bootstrap.HtmlEditor);
25453 //            editor.toolbars[i].init(editor);
25454 //        }
25455     },
25456
25457      
25458     // private
25459     onRender : function(ct, position)
25460     {
25461        // Roo.log("Call onRender: " + this.xtype);
25462         var _t = this;
25463         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25464       
25465         this.wrap = this.inputEl().wrap({
25466             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25467         });
25468         
25469         this.editorcore.onRender(ct, position);
25470          
25471         if (this.resizable) {
25472             this.resizeEl = new Roo.Resizable(this.wrap, {
25473                 pinned : true,
25474                 wrap: true,
25475                 dynamic : true,
25476                 minHeight : this.height,
25477                 height: this.height,
25478                 handles : this.resizable,
25479                 width: this.width,
25480                 listeners : {
25481                     resize : function(r, w, h) {
25482                         _t.onResize(w,h); // -something
25483                     }
25484                 }
25485             });
25486             
25487         }
25488         this.createToolbar(this);
25489        
25490         
25491         if(!this.width && this.resizable){
25492             this.setSize(this.wrap.getSize());
25493         }
25494         if (this.resizeEl) {
25495             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25496             // should trigger onReize..
25497         }
25498         
25499     },
25500
25501     // private
25502     onResize : function(w, h)
25503     {
25504         Roo.log('resize: ' +w + ',' + h );
25505         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
25506         var ew = false;
25507         var eh = false;
25508         
25509         if(this.inputEl() ){
25510             if(typeof w == 'number'){
25511                 var aw = w - this.wrap.getFrameWidth('lr');
25512                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
25513                 ew = aw;
25514             }
25515             if(typeof h == 'number'){
25516                  var tbh = -11;  // fixme it needs to tool bar size!
25517                 for (var i =0; i < this.toolbars.length;i++) {
25518                     // fixme - ask toolbars for heights?
25519                     tbh += this.toolbars[i].el.getHeight();
25520                     //if (this.toolbars[i].footer) {
25521                     //    tbh += this.toolbars[i].footer.el.getHeight();
25522                     //}
25523                 }
25524               
25525                 
25526                 
25527                 
25528                 
25529                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25530                 ah -= 5; // knock a few pixes off for look..
25531                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
25532                 var eh = ah;
25533             }
25534         }
25535         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
25536         this.editorcore.onResize(ew,eh);
25537         
25538     },
25539
25540     /**
25541      * Toggles the editor between standard and source edit mode.
25542      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25543      */
25544     toggleSourceEdit : function(sourceEditMode)
25545     {
25546         this.editorcore.toggleSourceEdit(sourceEditMode);
25547         
25548         if(this.editorcore.sourceEditMode){
25549             Roo.log('editor - showing textarea');
25550             
25551 //            Roo.log('in');
25552 //            Roo.log(this.syncValue());
25553             this.syncValue();
25554             this.inputEl().removeClass(['hide', 'x-hidden']);
25555             this.inputEl().dom.removeAttribute('tabIndex');
25556             this.inputEl().focus();
25557         }else{
25558             Roo.log('editor - hiding textarea');
25559 //            Roo.log('out')
25560 //            Roo.log(this.pushValue()); 
25561             this.pushValue();
25562             
25563             this.inputEl().addClass(['hide', 'x-hidden']);
25564             this.inputEl().dom.setAttribute('tabIndex', -1);
25565             //this.deferFocus();
25566         }
25567          
25568         if(this.resizable){
25569             this.setSize(this.wrap.getSize());
25570         }
25571         
25572         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
25573     },
25574  
25575     // private (for BoxComponent)
25576     adjustSize : Roo.BoxComponent.prototype.adjustSize,
25577
25578     // private (for BoxComponent)
25579     getResizeEl : function(){
25580         return this.wrap;
25581     },
25582
25583     // private (for BoxComponent)
25584     getPositionEl : function(){
25585         return this.wrap;
25586     },
25587
25588     // private
25589     initEvents : function(){
25590         this.originalValue = this.getValue();
25591     },
25592
25593 //    /**
25594 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25595 //     * @method
25596 //     */
25597 //    markInvalid : Roo.emptyFn,
25598 //    /**
25599 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25600 //     * @method
25601 //     */
25602 //    clearInvalid : Roo.emptyFn,
25603
25604     setValue : function(v){
25605         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
25606         this.editorcore.pushValue();
25607     },
25608
25609      
25610     // private
25611     deferFocus : function(){
25612         this.focus.defer(10, this);
25613     },
25614
25615     // doc'ed in Field
25616     focus : function(){
25617         this.editorcore.focus();
25618         
25619     },
25620       
25621
25622     // private
25623     onDestroy : function(){
25624         
25625         
25626         
25627         if(this.rendered){
25628             
25629             for (var i =0; i < this.toolbars.length;i++) {
25630                 // fixme - ask toolbars for heights?
25631                 this.toolbars[i].onDestroy();
25632             }
25633             
25634             this.wrap.dom.innerHTML = '';
25635             this.wrap.remove();
25636         }
25637     },
25638
25639     // private
25640     onFirstFocus : function(){
25641         //Roo.log("onFirstFocus");
25642         this.editorcore.onFirstFocus();
25643          for (var i =0; i < this.toolbars.length;i++) {
25644             this.toolbars[i].onFirstFocus();
25645         }
25646         
25647     },
25648     
25649     // private
25650     syncValue : function()
25651     {   
25652         this.editorcore.syncValue();
25653     },
25654     
25655     pushValue : function()
25656     {   
25657         this.editorcore.pushValue();
25658     }
25659      
25660     
25661     // hide stuff that is not compatible
25662     /**
25663      * @event blur
25664      * @hide
25665      */
25666     /**
25667      * @event change
25668      * @hide
25669      */
25670     /**
25671      * @event focus
25672      * @hide
25673      */
25674     /**
25675      * @event specialkey
25676      * @hide
25677      */
25678     /**
25679      * @cfg {String} fieldClass @hide
25680      */
25681     /**
25682      * @cfg {String} focusClass @hide
25683      */
25684     /**
25685      * @cfg {String} autoCreate @hide
25686      */
25687     /**
25688      * @cfg {String} inputType @hide
25689      */
25690      
25691     /**
25692      * @cfg {String} invalidText @hide
25693      */
25694     /**
25695      * @cfg {String} msgFx @hide
25696      */
25697     /**
25698      * @cfg {String} validateOnBlur @hide
25699      */
25700 });
25701  
25702     
25703    
25704    
25705    
25706       
25707 Roo.namespace('Roo.bootstrap.htmleditor');
25708 /**
25709  * @class Roo.bootstrap.HtmlEditorToolbar1
25710  * Basic Toolbar
25711  * 
25712  * @example
25713  * Usage:
25714  *
25715  new Roo.bootstrap.HtmlEditor({
25716     ....
25717     toolbars : [
25718         new Roo.bootstrap.HtmlEditorToolbar1({
25719             disable : { fonts: 1 , format: 1, ..., ... , ...],
25720             btns : [ .... ]
25721         })
25722     }
25723      
25724  * 
25725  * @cfg {Object} disable List of elements to disable..
25726  * @cfg {Array} btns List of additional buttons.
25727  * 
25728  * 
25729  * NEEDS Extra CSS? 
25730  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
25731  */
25732  
25733 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
25734 {
25735     
25736     Roo.apply(this, config);
25737     
25738     // default disabled, based on 'good practice'..
25739     this.disable = this.disable || {};
25740     Roo.applyIf(this.disable, {
25741         fontSize : true,
25742         colors : true,
25743         specialElements : true
25744     });
25745     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
25746     
25747     this.editor = config.editor;
25748     this.editorcore = config.editor.editorcore;
25749     
25750     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
25751     
25752     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25753     // dont call parent... till later.
25754 }
25755 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
25756      
25757     bar : true,
25758     
25759     editor : false,
25760     editorcore : false,
25761     
25762     
25763     formats : [
25764         "p" ,  
25765         "h1","h2","h3","h4","h5","h6", 
25766         "pre", "code", 
25767         "abbr", "acronym", "address", "cite", "samp", "var",
25768         'div','span'
25769     ],
25770     
25771     onRender : function(ct, position)
25772     {
25773        // Roo.log("Call onRender: " + this.xtype);
25774         
25775        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
25776        Roo.log(this.el);
25777        this.el.dom.style.marginBottom = '0';
25778        var _this = this;
25779        var editorcore = this.editorcore;
25780        var editor= this.editor;
25781        
25782        var children = [];
25783        var btn = function(id,cmd , toggle, handler, html){
25784        
25785             var  event = toggle ? 'toggle' : 'click';
25786        
25787             var a = {
25788                 size : 'sm',
25789                 xtype: 'Button',
25790                 xns: Roo.bootstrap,
25791                 //glyphicon : id,
25792                 fa: id,
25793                 cmd : id || cmd,
25794                 enableToggle:toggle !== false,
25795                 html : html || '',
25796                 pressed : toggle ? false : null,
25797                 listeners : {}
25798             };
25799             a.listeners[toggle ? 'toggle' : 'click'] = function() {
25800                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
25801             };
25802             children.push(a);
25803             return a;
25804        }
25805        
25806     //    var cb_box = function...
25807         
25808         var style = {
25809                 xtype: 'Button',
25810                 size : 'sm',
25811                 xns: Roo.bootstrap,
25812                 fa : 'font',
25813                 //html : 'submit'
25814                 menu : {
25815                     xtype: 'Menu',
25816                     xns: Roo.bootstrap,
25817                     items:  []
25818                 }
25819         };
25820         Roo.each(this.formats, function(f) {
25821             style.menu.items.push({
25822                 xtype :'MenuItem',
25823                 xns: Roo.bootstrap,
25824                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
25825                 tagname : f,
25826                 listeners : {
25827                     click : function()
25828                     {
25829                         editorcore.insertTag(this.tagname);
25830                         editor.focus();
25831                     }
25832                 }
25833                 
25834             });
25835         });
25836         children.push(style);   
25837         
25838         btn('bold',false,true);
25839         btn('italic',false,true);
25840         btn('align-left', 'justifyleft',true);
25841         btn('align-center', 'justifycenter',true);
25842         btn('align-right' , 'justifyright',true);
25843         btn('link', false, false, function(btn) {
25844             //Roo.log("create link?");
25845             var url = prompt(this.createLinkText, this.defaultLinkValue);
25846             if(url && url != 'http:/'+'/'){
25847                 this.editorcore.relayCmd('createlink', url);
25848             }
25849         }),
25850         btn('list','insertunorderedlist',true);
25851         btn('pencil', false,true, function(btn){
25852                 Roo.log(this);
25853                 this.toggleSourceEdit(btn.pressed);
25854         });
25855         
25856         if (this.editor.btns.length > 0) {
25857             for (var i = 0; i<this.editor.btns.length; i++) {
25858                 children.push(this.editor.btns[i]);
25859             }
25860         }
25861         
25862         /*
25863         var cog = {
25864                 xtype: 'Button',
25865                 size : 'sm',
25866                 xns: Roo.bootstrap,
25867                 glyphicon : 'cog',
25868                 //html : 'submit'
25869                 menu : {
25870                     xtype: 'Menu',
25871                     xns: Roo.bootstrap,
25872                     items:  []
25873                 }
25874         };
25875         
25876         cog.menu.items.push({
25877             xtype :'MenuItem',
25878             xns: Roo.bootstrap,
25879             html : Clean styles,
25880             tagname : f,
25881             listeners : {
25882                 click : function()
25883                 {
25884                     editorcore.insertTag(this.tagname);
25885                     editor.focus();
25886                 }
25887             }
25888             
25889         });
25890        */
25891         
25892          
25893        this.xtype = 'NavSimplebar';
25894         
25895         for(var i=0;i< children.length;i++) {
25896             
25897             this.buttons.add(this.addxtypeChild(children[i]));
25898             
25899         }
25900         
25901         editor.on('editorevent', this.updateToolbar, this);
25902     },
25903     onBtnClick : function(id)
25904     {
25905        this.editorcore.relayCmd(id);
25906        this.editorcore.focus();
25907     },
25908     
25909     /**
25910      * Protected method that will not generally be called directly. It triggers
25911      * a toolbar update by reading the markup state of the current selection in the editor.
25912      */
25913     updateToolbar: function(){
25914
25915         if(!this.editorcore.activated){
25916             this.editor.onFirstFocus(); // is this neeed?
25917             return;
25918         }
25919
25920         var btns = this.buttons; 
25921         var doc = this.editorcore.doc;
25922         btns.get('bold').setActive(doc.queryCommandState('bold'));
25923         btns.get('italic').setActive(doc.queryCommandState('italic'));
25924         //btns.get('underline').setActive(doc.queryCommandState('underline'));
25925         
25926         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
25927         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
25928         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
25929         
25930         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
25931         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
25932          /*
25933         
25934         var ans = this.editorcore.getAllAncestors();
25935         if (this.formatCombo) {
25936             
25937             
25938             var store = this.formatCombo.store;
25939             this.formatCombo.setValue("");
25940             for (var i =0; i < ans.length;i++) {
25941                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
25942                     // select it..
25943                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
25944                     break;
25945                 }
25946             }
25947         }
25948         
25949         
25950         
25951         // hides menus... - so this cant be on a menu...
25952         Roo.bootstrap.MenuMgr.hideAll();
25953         */
25954         Roo.bootstrap.MenuMgr.hideAll();
25955         //this.editorsyncValue();
25956     },
25957     onFirstFocus: function() {
25958         this.buttons.each(function(item){
25959            item.enable();
25960         });
25961     },
25962     toggleSourceEdit : function(sourceEditMode){
25963         
25964           
25965         if(sourceEditMode){
25966             Roo.log("disabling buttons");
25967            this.buttons.each( function(item){
25968                 if(item.cmd != 'pencil'){
25969                     item.disable();
25970                 }
25971             });
25972           
25973         }else{
25974             Roo.log("enabling buttons");
25975             if(this.editorcore.initialized){
25976                 this.buttons.each( function(item){
25977                     item.enable();
25978                 });
25979             }
25980             
25981         }
25982         Roo.log("calling toggole on editor");
25983         // tell the editor that it's been pressed..
25984         this.editor.toggleSourceEdit(sourceEditMode);
25985        
25986     }
25987 });
25988
25989
25990
25991
25992
25993 /**
25994  * @class Roo.bootstrap.Table.AbstractSelectionModel
25995  * @extends Roo.util.Observable
25996  * Abstract base class for grid SelectionModels.  It provides the interface that should be
25997  * implemented by descendant classes.  This class should not be directly instantiated.
25998  * @constructor
25999  */
26000 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26001     this.locked = false;
26002     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26003 };
26004
26005
26006 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26007     /** @ignore Called by the grid automatically. Do not call directly. */
26008     init : function(grid){
26009         this.grid = grid;
26010         this.initEvents();
26011     },
26012
26013     /**
26014      * Locks the selections.
26015      */
26016     lock : function(){
26017         this.locked = true;
26018     },
26019
26020     /**
26021      * Unlocks the selections.
26022      */
26023     unlock : function(){
26024         this.locked = false;
26025     },
26026
26027     /**
26028      * Returns true if the selections are locked.
26029      * @return {Boolean}
26030      */
26031     isLocked : function(){
26032         return this.locked;
26033     },
26034     
26035     
26036     initEvents : function ()
26037     {
26038         
26039     }
26040 });
26041 /**
26042  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26043  * @class Roo.bootstrap.Table.RowSelectionModel
26044  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26045  * It supports multiple selections and keyboard selection/navigation. 
26046  * @constructor
26047  * @param {Object} config
26048  */
26049
26050 Roo.bootstrap.Table.RowSelectionModel = function(config){
26051     Roo.apply(this, config);
26052     this.selections = new Roo.util.MixedCollection(false, function(o){
26053         return o.id;
26054     });
26055
26056     this.last = false;
26057     this.lastActive = false;
26058
26059     this.addEvents({
26060         /**
26061              * @event selectionchange
26062              * Fires when the selection changes
26063              * @param {SelectionModel} this
26064              */
26065             "selectionchange" : true,
26066         /**
26067              * @event afterselectionchange
26068              * Fires after the selection changes (eg. by key press or clicking)
26069              * @param {SelectionModel} this
26070              */
26071             "afterselectionchange" : true,
26072         /**
26073              * @event beforerowselect
26074              * Fires when a row is selected being selected, return false to cancel.
26075              * @param {SelectionModel} this
26076              * @param {Number} rowIndex The selected index
26077              * @param {Boolean} keepExisting False if other selections will be cleared
26078              */
26079             "beforerowselect" : true,
26080         /**
26081              * @event rowselect
26082              * Fires when a row is selected.
26083              * @param {SelectionModel} this
26084              * @param {Number} rowIndex The selected index
26085              * @param {Roo.data.Record} r The record
26086              */
26087             "rowselect" : true,
26088         /**
26089              * @event rowdeselect
26090              * Fires when a row is deselected.
26091              * @param {SelectionModel} this
26092              * @param {Number} rowIndex The selected index
26093              */
26094         "rowdeselect" : true
26095     });
26096     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26097     this.locked = false;
26098  };
26099
26100 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
26101     /**
26102      * @cfg {Boolean} singleSelect
26103      * True to allow selection of only one row at a time (defaults to false)
26104      */
26105     singleSelect : false,
26106
26107     // private
26108     initEvents : function()
26109     {
26110
26111         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26112         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
26113         //}else{ // allow click to work like normal
26114          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
26115         //}
26116         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26117         this.grid.on("rowclick", this.handleMouseDown, this);
26118         
26119         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26120             "up" : function(e){
26121                 if(!e.shiftKey){
26122                     this.selectPrevious(e.shiftKey);
26123                 }else if(this.last !== false && this.lastActive !== false){
26124                     var last = this.last;
26125                     this.selectRange(this.last,  this.lastActive-1);
26126                     this.grid.getView().focusRow(this.lastActive);
26127                     if(last !== false){
26128                         this.last = last;
26129                     }
26130                 }else{
26131                     this.selectFirstRow();
26132                 }
26133                 this.fireEvent("afterselectionchange", this);
26134             },
26135             "down" : function(e){
26136                 if(!e.shiftKey){
26137                     this.selectNext(e.shiftKey);
26138                 }else if(this.last !== false && this.lastActive !== false){
26139                     var last = this.last;
26140                     this.selectRange(this.last,  this.lastActive+1);
26141                     this.grid.getView().focusRow(this.lastActive);
26142                     if(last !== false){
26143                         this.last = last;
26144                     }
26145                 }else{
26146                     this.selectFirstRow();
26147                 }
26148                 this.fireEvent("afterselectionchange", this);
26149             },
26150             scope: this
26151         });
26152         this.grid.store.on('load', function(){
26153             this.selections.clear();
26154         },this);
26155         /*
26156         var view = this.grid.view;
26157         view.on("refresh", this.onRefresh, this);
26158         view.on("rowupdated", this.onRowUpdated, this);
26159         view.on("rowremoved", this.onRemove, this);
26160         */
26161     },
26162
26163     // private
26164     onRefresh : function()
26165     {
26166         var ds = this.grid.store, i, v = this.grid.view;
26167         var s = this.selections;
26168         s.each(function(r){
26169             if((i = ds.indexOfId(r.id)) != -1){
26170                 v.onRowSelect(i);
26171             }else{
26172                 s.remove(r);
26173             }
26174         });
26175     },
26176
26177     // private
26178     onRemove : function(v, index, r){
26179         this.selections.remove(r);
26180     },
26181
26182     // private
26183     onRowUpdated : function(v, index, r){
26184         if(this.isSelected(r)){
26185             v.onRowSelect(index);
26186         }
26187     },
26188
26189     /**
26190      * Select records.
26191      * @param {Array} records The records to select
26192      * @param {Boolean} keepExisting (optional) True to keep existing selections
26193      */
26194     selectRecords : function(records, keepExisting)
26195     {
26196         if(!keepExisting){
26197             this.clearSelections();
26198         }
26199             var ds = this.grid.store;
26200         for(var i = 0, len = records.length; i < len; i++){
26201             this.selectRow(ds.indexOf(records[i]), true);
26202         }
26203     },
26204
26205     /**
26206      * Gets the number of selected rows.
26207      * @return {Number}
26208      */
26209     getCount : function(){
26210         return this.selections.length;
26211     },
26212
26213     /**
26214      * Selects the first row in the grid.
26215      */
26216     selectFirstRow : function(){
26217         this.selectRow(0);
26218     },
26219
26220     /**
26221      * Select the last row.
26222      * @param {Boolean} keepExisting (optional) True to keep existing selections
26223      */
26224     selectLastRow : function(keepExisting){
26225         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26226         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26227     },
26228
26229     /**
26230      * Selects the row immediately following the last selected row.
26231      * @param {Boolean} keepExisting (optional) True to keep existing selections
26232      */
26233     selectNext : function(keepExisting)
26234     {
26235             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26236             this.selectRow(this.last+1, keepExisting);
26237             this.grid.getView().focusRow(this.last);
26238         }
26239     },
26240
26241     /**
26242      * Selects the row that precedes the last selected row.
26243      * @param {Boolean} keepExisting (optional) True to keep existing selections
26244      */
26245     selectPrevious : function(keepExisting){
26246         if(this.last){
26247             this.selectRow(this.last-1, keepExisting);
26248             this.grid.getView().focusRow(this.last);
26249         }
26250     },
26251
26252     /**
26253      * Returns the selected records
26254      * @return {Array} Array of selected records
26255      */
26256     getSelections : function(){
26257         return [].concat(this.selections.items);
26258     },
26259
26260     /**
26261      * Returns the first selected record.
26262      * @return {Record}
26263      */
26264     getSelected : function(){
26265         return this.selections.itemAt(0);
26266     },
26267
26268
26269     /**
26270      * Clears all selections.
26271      */
26272     clearSelections : function(fast)
26273     {
26274         if(this.locked) {
26275             return;
26276         }
26277         if(fast !== true){
26278                 var ds = this.grid.store;
26279             var s = this.selections;
26280             s.each(function(r){
26281                 this.deselectRow(ds.indexOfId(r.id));
26282             }, this);
26283             s.clear();
26284         }else{
26285             this.selections.clear();
26286         }
26287         this.last = false;
26288     },
26289
26290
26291     /**
26292      * Selects all rows.
26293      */
26294     selectAll : function(){
26295         if(this.locked) {
26296             return;
26297         }
26298         this.selections.clear();
26299         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26300             this.selectRow(i, true);
26301         }
26302     },
26303
26304     /**
26305      * Returns True if there is a selection.
26306      * @return {Boolean}
26307      */
26308     hasSelection : function(){
26309         return this.selections.length > 0;
26310     },
26311
26312     /**
26313      * Returns True if the specified row is selected.
26314      * @param {Number/Record} record The record or index of the record to check
26315      * @return {Boolean}
26316      */
26317     isSelected : function(index){
26318             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26319         return (r && this.selections.key(r.id) ? true : false);
26320     },
26321
26322     /**
26323      * Returns True if the specified record id is selected.
26324      * @param {String} id The id of record to check
26325      * @return {Boolean}
26326      */
26327     isIdSelected : function(id){
26328         return (this.selections.key(id) ? true : false);
26329     },
26330
26331
26332     // private
26333     handleMouseDBClick : function(e, t){
26334         
26335     },
26336     // private
26337     handleMouseDown : function(e, t)
26338     {
26339             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26340         if(this.isLocked() || rowIndex < 0 ){
26341             return;
26342         };
26343         if(e.shiftKey && this.last !== false){
26344             var last = this.last;
26345             this.selectRange(last, rowIndex, e.ctrlKey);
26346             this.last = last; // reset the last
26347             t.focus();
26348     
26349         }else{
26350             var isSelected = this.isSelected(rowIndex);
26351             //Roo.log("select row:" + rowIndex);
26352             if(isSelected){
26353                 this.deselectRow(rowIndex);
26354             } else {
26355                         this.selectRow(rowIndex, true);
26356             }
26357     
26358             /*
26359                 if(e.button !== 0 && isSelected){
26360                 alert('rowIndex 2: ' + rowIndex);
26361                     view.focusRow(rowIndex);
26362                 }else if(e.ctrlKey && isSelected){
26363                     this.deselectRow(rowIndex);
26364                 }else if(!isSelected){
26365                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26366                     view.focusRow(rowIndex);
26367                 }
26368             */
26369         }
26370         this.fireEvent("afterselectionchange", this);
26371     },
26372     // private
26373     handleDragableRowClick :  function(grid, rowIndex, e) 
26374     {
26375         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26376             this.selectRow(rowIndex, false);
26377             grid.view.focusRow(rowIndex);
26378              this.fireEvent("afterselectionchange", this);
26379         }
26380     },
26381     
26382     /**
26383      * Selects multiple rows.
26384      * @param {Array} rows Array of the indexes of the row to select
26385      * @param {Boolean} keepExisting (optional) True to keep existing selections
26386      */
26387     selectRows : function(rows, keepExisting){
26388         if(!keepExisting){
26389             this.clearSelections();
26390         }
26391         for(var i = 0, len = rows.length; i < len; i++){
26392             this.selectRow(rows[i], true);
26393         }
26394     },
26395
26396     /**
26397      * Selects a range of rows. All rows in between startRow and endRow are also selected.
26398      * @param {Number} startRow The index of the first row in the range
26399      * @param {Number} endRow The index of the last row in the range
26400      * @param {Boolean} keepExisting (optional) True to retain existing selections
26401      */
26402     selectRange : function(startRow, endRow, keepExisting){
26403         if(this.locked) {
26404             return;
26405         }
26406         if(!keepExisting){
26407             this.clearSelections();
26408         }
26409         if(startRow <= endRow){
26410             for(var i = startRow; i <= endRow; i++){
26411                 this.selectRow(i, true);
26412             }
26413         }else{
26414             for(var i = startRow; i >= endRow; i--){
26415                 this.selectRow(i, true);
26416             }
26417         }
26418     },
26419
26420     /**
26421      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
26422      * @param {Number} startRow The index of the first row in the range
26423      * @param {Number} endRow The index of the last row in the range
26424      */
26425     deselectRange : function(startRow, endRow, preventViewNotify){
26426         if(this.locked) {
26427             return;
26428         }
26429         for(var i = startRow; i <= endRow; i++){
26430             this.deselectRow(i, preventViewNotify);
26431         }
26432     },
26433
26434     /**
26435      * Selects a row.
26436      * @param {Number} row The index of the row to select
26437      * @param {Boolean} keepExisting (optional) True to keep existing selections
26438      */
26439     selectRow : function(index, keepExisting, preventViewNotify)
26440     {
26441             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
26442             return;
26443         }
26444         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
26445             if(!keepExisting || this.singleSelect){
26446                 this.clearSelections();
26447             }
26448             
26449             var r = this.grid.store.getAt(index);
26450             //console.log('selectRow - record id :' + r.id);
26451             
26452             this.selections.add(r);
26453             this.last = this.lastActive = index;
26454             if(!preventViewNotify){
26455                 var proxy = new Roo.Element(
26456                                 this.grid.getRowDom(index)
26457                 );
26458                 proxy.addClass('bg-info info');
26459             }
26460             this.fireEvent("rowselect", this, index, r);
26461             this.fireEvent("selectionchange", this);
26462         }
26463     },
26464
26465     /**
26466      * Deselects a row.
26467      * @param {Number} row The index of the row to deselect
26468      */
26469     deselectRow : function(index, preventViewNotify)
26470     {
26471         if(this.locked) {
26472             return;
26473         }
26474         if(this.last == index){
26475             this.last = false;
26476         }
26477         if(this.lastActive == index){
26478             this.lastActive = false;
26479         }
26480         
26481         var r = this.grid.store.getAt(index);
26482         if (!r) {
26483             return;
26484         }
26485         
26486         this.selections.remove(r);
26487         //.console.log('deselectRow - record id :' + r.id);
26488         if(!preventViewNotify){
26489         
26490             var proxy = new Roo.Element(
26491                 this.grid.getRowDom(index)
26492             );
26493             proxy.removeClass('bg-info info');
26494         }
26495         this.fireEvent("rowdeselect", this, index);
26496         this.fireEvent("selectionchange", this);
26497     },
26498
26499     // private
26500     restoreLast : function(){
26501         if(this._last){
26502             this.last = this._last;
26503         }
26504     },
26505
26506     // private
26507     acceptsNav : function(row, col, cm){
26508         return !cm.isHidden(col) && cm.isCellEditable(col, row);
26509     },
26510
26511     // private
26512     onEditorKey : function(field, e){
26513         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
26514         if(k == e.TAB){
26515             e.stopEvent();
26516             ed.completeEdit();
26517             if(e.shiftKey){
26518                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
26519             }else{
26520                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
26521             }
26522         }else if(k == e.ENTER && !e.ctrlKey){
26523             e.stopEvent();
26524             ed.completeEdit();
26525             if(e.shiftKey){
26526                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
26527             }else{
26528                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
26529             }
26530         }else if(k == e.ESC){
26531             ed.cancelEdit();
26532         }
26533         if(newCell){
26534             g.startEditing(newCell[0], newCell[1]);
26535         }
26536     }
26537 });
26538 /*
26539  * Based on:
26540  * Ext JS Library 1.1.1
26541  * Copyright(c) 2006-2007, Ext JS, LLC.
26542  *
26543  * Originally Released Under LGPL - original licence link has changed is not relivant.
26544  *
26545  * Fork - LGPL
26546  * <script type="text/javascript">
26547  */
26548  
26549 /**
26550  * @class Roo.bootstrap.PagingToolbar
26551  * @extends Roo.bootstrap.NavSimplebar
26552  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
26553  * @constructor
26554  * Create a new PagingToolbar
26555  * @param {Object} config The config object
26556  * @param {Roo.data.Store} store
26557  */
26558 Roo.bootstrap.PagingToolbar = function(config)
26559 {
26560     // old args format still supported... - xtype is prefered..
26561         // created from xtype...
26562     
26563     this.ds = config.dataSource;
26564     
26565     if (config.store && !this.ds) {
26566         this.store= Roo.factory(config.store, Roo.data);
26567         this.ds = this.store;
26568         this.ds.xmodule = this.xmodule || false;
26569     }
26570     
26571     this.toolbarItems = [];
26572     if (config.items) {
26573         this.toolbarItems = config.items;
26574     }
26575     
26576     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
26577     
26578     this.cursor = 0;
26579     
26580     if (this.ds) { 
26581         this.bind(this.ds);
26582     }
26583     
26584     if (Roo.bootstrap.version == 4) {
26585         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
26586     } else {
26587         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
26588     }
26589     
26590 };
26591
26592 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
26593     /**
26594      * @cfg {Roo.data.Store} dataSource
26595      * The underlying data store providing the paged data
26596      */
26597     /**
26598      * @cfg {String/HTMLElement/Element} container
26599      * container The id or element that will contain the toolbar
26600      */
26601     /**
26602      * @cfg {Boolean} displayInfo
26603      * True to display the displayMsg (defaults to false)
26604      */
26605     /**
26606      * @cfg {Number} pageSize
26607      * The number of records to display per page (defaults to 20)
26608      */
26609     pageSize: 20,
26610     /**
26611      * @cfg {String} displayMsg
26612      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
26613      */
26614     displayMsg : 'Displaying {0} - {1} of {2}',
26615     /**
26616      * @cfg {String} emptyMsg
26617      * The message to display when no records are found (defaults to "No data to display")
26618      */
26619     emptyMsg : 'No data to display',
26620     /**
26621      * Customizable piece of the default paging text (defaults to "Page")
26622      * @type String
26623      */
26624     beforePageText : "Page",
26625     /**
26626      * Customizable piece of the default paging text (defaults to "of %0")
26627      * @type String
26628      */
26629     afterPageText : "of {0}",
26630     /**
26631      * Customizable piece of the default paging text (defaults to "First Page")
26632      * @type String
26633      */
26634     firstText : "First Page",
26635     /**
26636      * Customizable piece of the default paging text (defaults to "Previous Page")
26637      * @type String
26638      */
26639     prevText : "Previous Page",
26640     /**
26641      * Customizable piece of the default paging text (defaults to "Next Page")
26642      * @type String
26643      */
26644     nextText : "Next Page",
26645     /**
26646      * Customizable piece of the default paging text (defaults to "Last Page")
26647      * @type String
26648      */
26649     lastText : "Last Page",
26650     /**
26651      * Customizable piece of the default paging text (defaults to "Refresh")
26652      * @type String
26653      */
26654     refreshText : "Refresh",
26655
26656     buttons : false,
26657     // private
26658     onRender : function(ct, position) 
26659     {
26660         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
26661         this.navgroup.parentId = this.id;
26662         this.navgroup.onRender(this.el, null);
26663         // add the buttons to the navgroup
26664         
26665         if(this.displayInfo){
26666             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
26667             this.displayEl = this.el.select('.x-paging-info', true).first();
26668 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
26669 //            this.displayEl = navel.el.select('span',true).first();
26670         }
26671         
26672         var _this = this;
26673         
26674         if(this.buttons){
26675             Roo.each(_this.buttons, function(e){ // this might need to use render????
26676                Roo.factory(e).render(_this.el);
26677             });
26678         }
26679             
26680         Roo.each(_this.toolbarItems, function(e) {
26681             _this.navgroup.addItem(e);
26682         });
26683         
26684         
26685         this.first = this.navgroup.addItem({
26686             tooltip: this.firstText,
26687             cls: "prev btn-outline-secondary",
26688             html : ' <i class="fa fa-step-backward"></i>',
26689             disabled: true,
26690             preventDefault: true,
26691             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
26692         });
26693         
26694         this.prev =  this.navgroup.addItem({
26695             tooltip: this.prevText,
26696             cls: "prev btn-outline-secondary",
26697             html : ' <i class="fa fa-backward"></i>',
26698             disabled: true,
26699             preventDefault: true,
26700             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
26701         });
26702     //this.addSeparator();
26703         
26704         
26705         var field = this.navgroup.addItem( {
26706             tagtype : 'span',
26707             cls : 'x-paging-position  btn-outline-secondary',
26708              disabled: true,
26709             html : this.beforePageText  +
26710                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
26711                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
26712          } ); //?? escaped?
26713         
26714         this.field = field.el.select('input', true).first();
26715         this.field.on("keydown", this.onPagingKeydown, this);
26716         this.field.on("focus", function(){this.dom.select();});
26717     
26718     
26719         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
26720         //this.field.setHeight(18);
26721         //this.addSeparator();
26722         this.next = this.navgroup.addItem({
26723             tooltip: this.nextText,
26724             cls: "next btn-outline-secondary",
26725             html : ' <i class="fa fa-forward"></i>',
26726             disabled: true,
26727             preventDefault: true,
26728             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
26729         });
26730         this.last = this.navgroup.addItem({
26731             tooltip: this.lastText,
26732             html : ' <i class="fa fa-step-forward"></i>',
26733             cls: "next btn-outline-secondary",
26734             disabled: true,
26735             preventDefault: true,
26736             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
26737         });
26738     //this.addSeparator();
26739         this.loading = this.navgroup.addItem({
26740             tooltip: this.refreshText,
26741             cls: "btn-outline-secondary",
26742             html : ' <i class="fa fa-refresh"></i>',
26743             preventDefault: true,
26744             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
26745         });
26746         
26747     },
26748
26749     // private
26750     updateInfo : function(){
26751         if(this.displayEl){
26752             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
26753             var msg = count == 0 ?
26754                 this.emptyMsg :
26755                 String.format(
26756                     this.displayMsg,
26757                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
26758                 );
26759             this.displayEl.update(msg);
26760         }
26761     },
26762
26763     // private
26764     onLoad : function(ds, r, o)
26765     {
26766         this.cursor = o.params.start ? o.params.start : 0;
26767         
26768         var d = this.getPageData(),
26769             ap = d.activePage,
26770             ps = d.pages;
26771         
26772         
26773         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
26774         this.field.dom.value = ap;
26775         this.first.setDisabled(ap == 1);
26776         this.prev.setDisabled(ap == 1);
26777         this.next.setDisabled(ap == ps);
26778         this.last.setDisabled(ap == ps);
26779         this.loading.enable();
26780         this.updateInfo();
26781     },
26782
26783     // private
26784     getPageData : function(){
26785         var total = this.ds.getTotalCount();
26786         return {
26787             total : total,
26788             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
26789             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
26790         };
26791     },
26792
26793     // private
26794     onLoadError : function(){
26795         this.loading.enable();
26796     },
26797
26798     // private
26799     onPagingKeydown : function(e){
26800         var k = e.getKey();
26801         var d = this.getPageData();
26802         if(k == e.RETURN){
26803             var v = this.field.dom.value, pageNum;
26804             if(!v || isNaN(pageNum = parseInt(v, 10))){
26805                 this.field.dom.value = d.activePage;
26806                 return;
26807             }
26808             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
26809             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
26810             e.stopEvent();
26811         }
26812         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))
26813         {
26814           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
26815           this.field.dom.value = pageNum;
26816           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
26817           e.stopEvent();
26818         }
26819         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
26820         {
26821           var v = this.field.dom.value, pageNum; 
26822           var increment = (e.shiftKey) ? 10 : 1;
26823           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
26824                 increment *= -1;
26825           }
26826           if(!v || isNaN(pageNum = parseInt(v, 10))) {
26827             this.field.dom.value = d.activePage;
26828             return;
26829           }
26830           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
26831           {
26832             this.field.dom.value = parseInt(v, 10) + increment;
26833             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
26834             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
26835           }
26836           e.stopEvent();
26837         }
26838     },
26839
26840     // private
26841     beforeLoad : function(){
26842         if(this.loading){
26843             this.loading.disable();
26844         }
26845     },
26846
26847     // private
26848     onClick : function(which){
26849         
26850         var ds = this.ds;
26851         if (!ds) {
26852             return;
26853         }
26854         
26855         switch(which){
26856             case "first":
26857                 ds.load({params:{start: 0, limit: this.pageSize}});
26858             break;
26859             case "prev":
26860                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
26861             break;
26862             case "next":
26863                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
26864             break;
26865             case "last":
26866                 var total = ds.getTotalCount();
26867                 var extra = total % this.pageSize;
26868                 var lastStart = extra ? (total - extra) : total-this.pageSize;
26869                 ds.load({params:{start: lastStart, limit: this.pageSize}});
26870             break;
26871             case "refresh":
26872                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
26873             break;
26874         }
26875     },
26876
26877     /**
26878      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
26879      * @param {Roo.data.Store} store The data store to unbind
26880      */
26881     unbind : function(ds){
26882         ds.un("beforeload", this.beforeLoad, this);
26883         ds.un("load", this.onLoad, this);
26884         ds.un("loadexception", this.onLoadError, this);
26885         ds.un("remove", this.updateInfo, this);
26886         ds.un("add", this.updateInfo, this);
26887         this.ds = undefined;
26888     },
26889
26890     /**
26891      * Binds the paging toolbar to the specified {@link Roo.data.Store}
26892      * @param {Roo.data.Store} store The data store to bind
26893      */
26894     bind : function(ds){
26895         ds.on("beforeload", this.beforeLoad, this);
26896         ds.on("load", this.onLoad, this);
26897         ds.on("loadexception", this.onLoadError, this);
26898         ds.on("remove", this.updateInfo, this);
26899         ds.on("add", this.updateInfo, this);
26900         this.ds = ds;
26901     }
26902 });/*
26903  * - LGPL
26904  *
26905  * element
26906  * 
26907  */
26908
26909 /**
26910  * @class Roo.bootstrap.MessageBar
26911  * @extends Roo.bootstrap.Component
26912  * Bootstrap MessageBar class
26913  * @cfg {String} html contents of the MessageBar
26914  * @cfg {String} weight (info | success | warning | danger) default info
26915  * @cfg {String} beforeClass insert the bar before the given class
26916  * @cfg {Boolean} closable (true | false) default false
26917  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
26918  * 
26919  * @constructor
26920  * Create a new Element
26921  * @param {Object} config The config object
26922  */
26923
26924 Roo.bootstrap.MessageBar = function(config){
26925     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
26926 };
26927
26928 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
26929     
26930     html: '',
26931     weight: 'info',
26932     closable: false,
26933     fixed: false,
26934     beforeClass: 'bootstrap-sticky-wrap',
26935     
26936     getAutoCreate : function(){
26937         
26938         var cfg = {
26939             tag: 'div',
26940             cls: 'alert alert-dismissable alert-' + this.weight,
26941             cn: [
26942                 {
26943                     tag: 'span',
26944                     cls: 'message',
26945                     html: this.html || ''
26946                 }
26947             ]
26948         };
26949         
26950         if(this.fixed){
26951             cfg.cls += ' alert-messages-fixed';
26952         }
26953         
26954         if(this.closable){
26955             cfg.cn.push({
26956                 tag: 'button',
26957                 cls: 'close',
26958                 html: 'x'
26959             });
26960         }
26961         
26962         return cfg;
26963     },
26964     
26965     onRender : function(ct, position)
26966     {
26967         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
26968         
26969         if(!this.el){
26970             var cfg = Roo.apply({},  this.getAutoCreate());
26971             cfg.id = Roo.id();
26972             
26973             if (this.cls) {
26974                 cfg.cls += ' ' + this.cls;
26975             }
26976             if (this.style) {
26977                 cfg.style = this.style;
26978             }
26979             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
26980             
26981             this.el.setVisibilityMode(Roo.Element.DISPLAY);
26982         }
26983         
26984         this.el.select('>button.close').on('click', this.hide, this);
26985         
26986     },
26987     
26988     show : function()
26989     {
26990         if (!this.rendered) {
26991             this.render();
26992         }
26993         
26994         this.el.show();
26995         
26996         this.fireEvent('show', this);
26997         
26998     },
26999     
27000     hide : function()
27001     {
27002         if (!this.rendered) {
27003             this.render();
27004         }
27005         
27006         this.el.hide();
27007         
27008         this.fireEvent('hide', this);
27009     },
27010     
27011     update : function()
27012     {
27013 //        var e = this.el.dom.firstChild;
27014 //        
27015 //        if(this.closable){
27016 //            e = e.nextSibling;
27017 //        }
27018 //        
27019 //        e.data = this.html || '';
27020
27021         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27022     }
27023    
27024 });
27025
27026  
27027
27028      /*
27029  * - LGPL
27030  *
27031  * Graph
27032  * 
27033  */
27034
27035
27036 /**
27037  * @class Roo.bootstrap.Graph
27038  * @extends Roo.bootstrap.Component
27039  * Bootstrap Graph class
27040 > Prameters
27041  -sm {number} sm 4
27042  -md {number} md 5
27043  @cfg {String} graphtype  bar | vbar | pie
27044  @cfg {number} g_x coodinator | centre x (pie)
27045  @cfg {number} g_y coodinator | centre y (pie)
27046  @cfg {number} g_r radius (pie)
27047  @cfg {number} g_height height of the chart (respected by all elements in the set)
27048  @cfg {number} g_width width of the chart (respected by all elements in the set)
27049  @cfg {Object} title The title of the chart
27050     
27051  -{Array}  values
27052  -opts (object) options for the chart 
27053      o {
27054      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27055      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27056      o vgutter (number)
27057      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.
27058      o stacked (boolean) whether or not to tread values as in a stacked bar chart
27059      o to
27060      o stretch (boolean)
27061      o }
27062  -opts (object) options for the pie
27063      o{
27064      o cut
27065      o startAngle (number)
27066      o endAngle (number)
27067      } 
27068  *
27069  * @constructor
27070  * Create a new Input
27071  * @param {Object} config The config object
27072  */
27073
27074 Roo.bootstrap.Graph = function(config){
27075     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27076     
27077     this.addEvents({
27078         // img events
27079         /**
27080          * @event click
27081          * The img click event for the img.
27082          * @param {Roo.EventObject} e
27083          */
27084         "click" : true
27085     });
27086 };
27087
27088 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
27089     
27090     sm: 4,
27091     md: 5,
27092     graphtype: 'bar',
27093     g_height: 250,
27094     g_width: 400,
27095     g_x: 50,
27096     g_y: 50,
27097     g_r: 30,
27098     opts:{
27099         //g_colors: this.colors,
27100         g_type: 'soft',
27101         g_gutter: '20%'
27102
27103     },
27104     title : false,
27105
27106     getAutoCreate : function(){
27107         
27108         var cfg = {
27109             tag: 'div',
27110             html : null
27111         };
27112         
27113         
27114         return  cfg;
27115     },
27116
27117     onRender : function(ct,position){
27118         
27119         
27120         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27121         
27122         if (typeof(Raphael) == 'undefined') {
27123             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27124             return;
27125         }
27126         
27127         this.raphael = Raphael(this.el.dom);
27128         
27129                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27130                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27131                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27132                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27133                 /*
27134                 r.text(160, 10, "Single Series Chart").attr(txtattr);
27135                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27136                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27137                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27138                 
27139                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27140                 r.barchart(330, 10, 300, 220, data1);
27141                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27142                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27143                 */
27144                 
27145                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27146                 // r.barchart(30, 30, 560, 250,  xdata, {
27147                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27148                 //     axis : "0 0 1 1",
27149                 //     axisxlabels :  xdata
27150                 //     //yvalues : cols,
27151                    
27152                 // });
27153 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27154 //        
27155 //        this.load(null,xdata,{
27156 //                axis : "0 0 1 1",
27157 //                axisxlabels :  xdata
27158 //                });
27159
27160     },
27161
27162     load : function(graphtype,xdata,opts)
27163     {
27164         this.raphael.clear();
27165         if(!graphtype) {
27166             graphtype = this.graphtype;
27167         }
27168         if(!opts){
27169             opts = this.opts;
27170         }
27171         var r = this.raphael,
27172             fin = function () {
27173                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27174             },
27175             fout = function () {
27176                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27177             },
27178             pfin = function() {
27179                 this.sector.stop();
27180                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27181
27182                 if (this.label) {
27183                     this.label[0].stop();
27184                     this.label[0].attr({ r: 7.5 });
27185                     this.label[1].attr({ "font-weight": 800 });
27186                 }
27187             },
27188             pfout = function() {
27189                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27190
27191                 if (this.label) {
27192                     this.label[0].animate({ r: 5 }, 500, "bounce");
27193                     this.label[1].attr({ "font-weight": 400 });
27194                 }
27195             };
27196
27197         switch(graphtype){
27198             case 'bar':
27199                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27200                 break;
27201             case 'hbar':
27202                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27203                 break;
27204             case 'pie':
27205 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
27206 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27207 //            
27208                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27209                 
27210                 break;
27211
27212         }
27213         
27214         if(this.title){
27215             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27216         }
27217         
27218     },
27219     
27220     setTitle: function(o)
27221     {
27222         this.title = o;
27223     },
27224     
27225     initEvents: function() {
27226         
27227         if(!this.href){
27228             this.el.on('click', this.onClick, this);
27229         }
27230     },
27231     
27232     onClick : function(e)
27233     {
27234         Roo.log('img onclick');
27235         this.fireEvent('click', this, e);
27236     }
27237    
27238 });
27239
27240  
27241 /*
27242  * - LGPL
27243  *
27244  * numberBox
27245  * 
27246  */
27247 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27248
27249 /**
27250  * @class Roo.bootstrap.dash.NumberBox
27251  * @extends Roo.bootstrap.Component
27252  * Bootstrap NumberBox class
27253  * @cfg {String} headline Box headline
27254  * @cfg {String} content Box content
27255  * @cfg {String} icon Box icon
27256  * @cfg {String} footer Footer text
27257  * @cfg {String} fhref Footer href
27258  * 
27259  * @constructor
27260  * Create a new NumberBox
27261  * @param {Object} config The config object
27262  */
27263
27264
27265 Roo.bootstrap.dash.NumberBox = function(config){
27266     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27267     
27268 };
27269
27270 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
27271     
27272     headline : '',
27273     content : '',
27274     icon : '',
27275     footer : '',
27276     fhref : '',
27277     ficon : '',
27278     
27279     getAutoCreate : function(){
27280         
27281         var cfg = {
27282             tag : 'div',
27283             cls : 'small-box ',
27284             cn : [
27285                 {
27286                     tag : 'div',
27287                     cls : 'inner',
27288                     cn :[
27289                         {
27290                             tag : 'h3',
27291                             cls : 'roo-headline',
27292                             html : this.headline
27293                         },
27294                         {
27295                             tag : 'p',
27296                             cls : 'roo-content',
27297                             html : this.content
27298                         }
27299                     ]
27300                 }
27301             ]
27302         };
27303         
27304         if(this.icon){
27305             cfg.cn.push({
27306                 tag : 'div',
27307                 cls : 'icon',
27308                 cn :[
27309                     {
27310                         tag : 'i',
27311                         cls : 'ion ' + this.icon
27312                     }
27313                 ]
27314             });
27315         }
27316         
27317         if(this.footer){
27318             var footer = {
27319                 tag : 'a',
27320                 cls : 'small-box-footer',
27321                 href : this.fhref || '#',
27322                 html : this.footer
27323             };
27324             
27325             cfg.cn.push(footer);
27326             
27327         }
27328         
27329         return  cfg;
27330     },
27331
27332     onRender : function(ct,position){
27333         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27334
27335
27336        
27337                 
27338     },
27339
27340     setHeadline: function (value)
27341     {
27342         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27343     },
27344     
27345     setFooter: function (value, href)
27346     {
27347         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27348         
27349         if(href){
27350             this.el.select('a.small-box-footer',true).first().attr('href', href);
27351         }
27352         
27353     },
27354
27355     setContent: function (value)
27356     {
27357         this.el.select('.roo-content',true).first().dom.innerHTML = value;
27358     },
27359
27360     initEvents: function() 
27361     {   
27362         
27363     }
27364     
27365 });
27366
27367  
27368 /*
27369  * - LGPL
27370  *
27371  * TabBox
27372  * 
27373  */
27374 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27375
27376 /**
27377  * @class Roo.bootstrap.dash.TabBox
27378  * @extends Roo.bootstrap.Component
27379  * Bootstrap TabBox class
27380  * @cfg {String} title Title of the TabBox
27381  * @cfg {String} icon Icon of the TabBox
27382  * @cfg {Boolean} showtabs (true|false) show the tabs default true
27383  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
27384  * 
27385  * @constructor
27386  * Create a new TabBox
27387  * @param {Object} config The config object
27388  */
27389
27390
27391 Roo.bootstrap.dash.TabBox = function(config){
27392     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
27393     this.addEvents({
27394         // raw events
27395         /**
27396          * @event addpane
27397          * When a pane is added
27398          * @param {Roo.bootstrap.dash.TabPane} pane
27399          */
27400         "addpane" : true,
27401         /**
27402          * @event activatepane
27403          * When a pane is activated
27404          * @param {Roo.bootstrap.dash.TabPane} pane
27405          */
27406         "activatepane" : true
27407         
27408          
27409     });
27410     
27411     this.panes = [];
27412 };
27413
27414 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
27415
27416     title : '',
27417     icon : false,
27418     showtabs : true,
27419     tabScrollable : false,
27420     
27421     getChildContainer : function()
27422     {
27423         return this.el.select('.tab-content', true).first();
27424     },
27425     
27426     getAutoCreate : function(){
27427         
27428         var header = {
27429             tag: 'li',
27430             cls: 'pull-left header',
27431             html: this.title,
27432             cn : []
27433         };
27434         
27435         if(this.icon){
27436             header.cn.push({
27437                 tag: 'i',
27438                 cls: 'fa ' + this.icon
27439             });
27440         }
27441         
27442         var h = {
27443             tag: 'ul',
27444             cls: 'nav nav-tabs pull-right',
27445             cn: [
27446                 header
27447             ]
27448         };
27449         
27450         if(this.tabScrollable){
27451             h = {
27452                 tag: 'div',
27453                 cls: 'tab-header',
27454                 cn: [
27455                     {
27456                         tag: 'ul',
27457                         cls: 'nav nav-tabs pull-right',
27458                         cn: [
27459                             header
27460                         ]
27461                     }
27462                 ]
27463             };
27464         }
27465         
27466         var cfg = {
27467             tag: 'div',
27468             cls: 'nav-tabs-custom',
27469             cn: [
27470                 h,
27471                 {
27472                     tag: 'div',
27473                     cls: 'tab-content no-padding',
27474                     cn: []
27475                 }
27476             ]
27477         };
27478
27479         return  cfg;
27480     },
27481     initEvents : function()
27482     {
27483         //Roo.log('add add pane handler');
27484         this.on('addpane', this.onAddPane, this);
27485     },
27486      /**
27487      * Updates the box title
27488      * @param {String} html to set the title to.
27489      */
27490     setTitle : function(value)
27491     {
27492         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
27493     },
27494     onAddPane : function(pane)
27495     {
27496         this.panes.push(pane);
27497         //Roo.log('addpane');
27498         //Roo.log(pane);
27499         // tabs are rendere left to right..
27500         if(!this.showtabs){
27501             return;
27502         }
27503         
27504         var ctr = this.el.select('.nav-tabs', true).first();
27505          
27506          
27507         var existing = ctr.select('.nav-tab',true);
27508         var qty = existing.getCount();;
27509         
27510         
27511         var tab = ctr.createChild({
27512             tag : 'li',
27513             cls : 'nav-tab' + (qty ? '' : ' active'),
27514             cn : [
27515                 {
27516                     tag : 'a',
27517                     href:'#',
27518                     html : pane.title
27519                 }
27520             ]
27521         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
27522         pane.tab = tab;
27523         
27524         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
27525         if (!qty) {
27526             pane.el.addClass('active');
27527         }
27528         
27529                 
27530     },
27531     onTabClick : function(ev,un,ob,pane)
27532     {
27533         //Roo.log('tab - prev default');
27534         ev.preventDefault();
27535         
27536         
27537         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
27538         pane.tab.addClass('active');
27539         //Roo.log(pane.title);
27540         this.getChildContainer().select('.tab-pane',true).removeClass('active');
27541         // technically we should have a deactivate event.. but maybe add later.
27542         // and it should not de-activate the selected tab...
27543         this.fireEvent('activatepane', pane);
27544         pane.el.addClass('active');
27545         pane.fireEvent('activate');
27546         
27547         
27548     },
27549     
27550     getActivePane : function()
27551     {
27552         var r = false;
27553         Roo.each(this.panes, function(p) {
27554             if(p.el.hasClass('active')){
27555                 r = p;
27556                 return false;
27557             }
27558             
27559             return;
27560         });
27561         
27562         return r;
27563     }
27564     
27565     
27566 });
27567
27568  
27569 /*
27570  * - LGPL
27571  *
27572  * Tab pane
27573  * 
27574  */
27575 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27576 /**
27577  * @class Roo.bootstrap.TabPane
27578  * @extends Roo.bootstrap.Component
27579  * Bootstrap TabPane class
27580  * @cfg {Boolean} active (false | true) Default false
27581  * @cfg {String} title title of panel
27582
27583  * 
27584  * @constructor
27585  * Create a new TabPane
27586  * @param {Object} config The config object
27587  */
27588
27589 Roo.bootstrap.dash.TabPane = function(config){
27590     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
27591     
27592     this.addEvents({
27593         // raw events
27594         /**
27595          * @event activate
27596          * When a pane is activated
27597          * @param {Roo.bootstrap.dash.TabPane} pane
27598          */
27599         "activate" : true
27600          
27601     });
27602 };
27603
27604 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
27605     
27606     active : false,
27607     title : '',
27608     
27609     // the tabBox that this is attached to.
27610     tab : false,
27611      
27612     getAutoCreate : function() 
27613     {
27614         var cfg = {
27615             tag: 'div',
27616             cls: 'tab-pane'
27617         };
27618         
27619         if(this.active){
27620             cfg.cls += ' active';
27621         }
27622         
27623         return cfg;
27624     },
27625     initEvents  : function()
27626     {
27627         //Roo.log('trigger add pane handler');
27628         this.parent().fireEvent('addpane', this)
27629     },
27630     
27631      /**
27632      * Updates the tab title 
27633      * @param {String} html to set the title to.
27634      */
27635     setTitle: function(str)
27636     {
27637         if (!this.tab) {
27638             return;
27639         }
27640         this.title = str;
27641         this.tab.select('a', true).first().dom.innerHTML = str;
27642         
27643     }
27644     
27645     
27646     
27647 });
27648
27649  
27650
27651
27652  /*
27653  * - LGPL
27654  *
27655  * menu
27656  * 
27657  */
27658 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
27659
27660 /**
27661  * @class Roo.bootstrap.menu.Menu
27662  * @extends Roo.bootstrap.Component
27663  * Bootstrap Menu class - container for Menu
27664  * @cfg {String} html Text of the menu
27665  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
27666  * @cfg {String} icon Font awesome icon
27667  * @cfg {String} pos Menu align to (top | bottom) default bottom
27668  * 
27669  * 
27670  * @constructor
27671  * Create a new Menu
27672  * @param {Object} config The config object
27673  */
27674
27675
27676 Roo.bootstrap.menu.Menu = function(config){
27677     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
27678     
27679     this.addEvents({
27680         /**
27681          * @event beforeshow
27682          * Fires before this menu is displayed
27683          * @param {Roo.bootstrap.menu.Menu} this
27684          */
27685         beforeshow : true,
27686         /**
27687          * @event beforehide
27688          * Fires before this menu is hidden
27689          * @param {Roo.bootstrap.menu.Menu} this
27690          */
27691         beforehide : true,
27692         /**
27693          * @event show
27694          * Fires after this menu is displayed
27695          * @param {Roo.bootstrap.menu.Menu} this
27696          */
27697         show : true,
27698         /**
27699          * @event hide
27700          * Fires after this menu is hidden
27701          * @param {Roo.bootstrap.menu.Menu} this
27702          */
27703         hide : true,
27704         /**
27705          * @event click
27706          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
27707          * @param {Roo.bootstrap.menu.Menu} this
27708          * @param {Roo.EventObject} e
27709          */
27710         click : true
27711     });
27712     
27713 };
27714
27715 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
27716     
27717     submenu : false,
27718     html : '',
27719     weight : 'default',
27720     icon : false,
27721     pos : 'bottom',
27722     
27723     
27724     getChildContainer : function() {
27725         if(this.isSubMenu){
27726             return this.el;
27727         }
27728         
27729         return this.el.select('ul.dropdown-menu', true).first();  
27730     },
27731     
27732     getAutoCreate : function()
27733     {
27734         var text = [
27735             {
27736                 tag : 'span',
27737                 cls : 'roo-menu-text',
27738                 html : this.html
27739             }
27740         ];
27741         
27742         if(this.icon){
27743             text.unshift({
27744                 tag : 'i',
27745                 cls : 'fa ' + this.icon
27746             })
27747         }
27748         
27749         
27750         var cfg = {
27751             tag : 'div',
27752             cls : 'btn-group',
27753             cn : [
27754                 {
27755                     tag : 'button',
27756                     cls : 'dropdown-button btn btn-' + this.weight,
27757                     cn : text
27758                 },
27759                 {
27760                     tag : 'button',
27761                     cls : 'dropdown-toggle btn btn-' + this.weight,
27762                     cn : [
27763                         {
27764                             tag : 'span',
27765                             cls : 'caret'
27766                         }
27767                     ]
27768                 },
27769                 {
27770                     tag : 'ul',
27771                     cls : 'dropdown-menu'
27772                 }
27773             ]
27774             
27775         };
27776         
27777         if(this.pos == 'top'){
27778             cfg.cls += ' dropup';
27779         }
27780         
27781         if(this.isSubMenu){
27782             cfg = {
27783                 tag : 'ul',
27784                 cls : 'dropdown-menu'
27785             }
27786         }
27787         
27788         return cfg;
27789     },
27790     
27791     onRender : function(ct, position)
27792     {
27793         this.isSubMenu = ct.hasClass('dropdown-submenu');
27794         
27795         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
27796     },
27797     
27798     initEvents : function() 
27799     {
27800         if(this.isSubMenu){
27801             return;
27802         }
27803         
27804         this.hidden = true;
27805         
27806         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
27807         this.triggerEl.on('click', this.onTriggerPress, this);
27808         
27809         this.buttonEl = this.el.select('button.dropdown-button', true).first();
27810         this.buttonEl.on('click', this.onClick, this);
27811         
27812     },
27813     
27814     list : function()
27815     {
27816         if(this.isSubMenu){
27817             return this.el;
27818         }
27819         
27820         return this.el.select('ul.dropdown-menu', true).first();
27821     },
27822     
27823     onClick : function(e)
27824     {
27825         this.fireEvent("click", this, e);
27826     },
27827     
27828     onTriggerPress  : function(e)
27829     {   
27830         if (this.isVisible()) {
27831             this.hide();
27832         } else {
27833             this.show();
27834         }
27835     },
27836     
27837     isVisible : function(){
27838         return !this.hidden;
27839     },
27840     
27841     show : function()
27842     {
27843         this.fireEvent("beforeshow", this);
27844         
27845         this.hidden = false;
27846         this.el.addClass('open');
27847         
27848         Roo.get(document).on("mouseup", this.onMouseUp, this);
27849         
27850         this.fireEvent("show", this);
27851         
27852         
27853     },
27854     
27855     hide : function()
27856     {
27857         this.fireEvent("beforehide", this);
27858         
27859         this.hidden = true;
27860         this.el.removeClass('open');
27861         
27862         Roo.get(document).un("mouseup", this.onMouseUp);
27863         
27864         this.fireEvent("hide", this);
27865     },
27866     
27867     onMouseUp : function()
27868     {
27869         this.hide();
27870     }
27871     
27872 });
27873
27874  
27875  /*
27876  * - LGPL
27877  *
27878  * menu item
27879  * 
27880  */
27881 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
27882
27883 /**
27884  * @class Roo.bootstrap.menu.Item
27885  * @extends Roo.bootstrap.Component
27886  * Bootstrap MenuItem class
27887  * @cfg {Boolean} submenu (true | false) default false
27888  * @cfg {String} html text of the item
27889  * @cfg {String} href the link
27890  * @cfg {Boolean} disable (true | false) default false
27891  * @cfg {Boolean} preventDefault (true | false) default true
27892  * @cfg {String} icon Font awesome icon
27893  * @cfg {String} pos Submenu align to (left | right) default right 
27894  * 
27895  * 
27896  * @constructor
27897  * Create a new Item
27898  * @param {Object} config The config object
27899  */
27900
27901
27902 Roo.bootstrap.menu.Item = function(config){
27903     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
27904     this.addEvents({
27905         /**
27906          * @event mouseover
27907          * Fires when the mouse is hovering over this menu
27908          * @param {Roo.bootstrap.menu.Item} this
27909          * @param {Roo.EventObject} e
27910          */
27911         mouseover : true,
27912         /**
27913          * @event mouseout
27914          * Fires when the mouse exits this menu
27915          * @param {Roo.bootstrap.menu.Item} this
27916          * @param {Roo.EventObject} e
27917          */
27918         mouseout : true,
27919         // raw events
27920         /**
27921          * @event click
27922          * The raw click event for the entire grid.
27923          * @param {Roo.EventObject} e
27924          */
27925         click : true
27926     });
27927 };
27928
27929 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
27930     
27931     submenu : false,
27932     href : '',
27933     html : '',
27934     preventDefault: true,
27935     disable : false,
27936     icon : false,
27937     pos : 'right',
27938     
27939     getAutoCreate : function()
27940     {
27941         var text = [
27942             {
27943                 tag : 'span',
27944                 cls : 'roo-menu-item-text',
27945                 html : this.html
27946             }
27947         ];
27948         
27949         if(this.icon){
27950             text.unshift({
27951                 tag : 'i',
27952                 cls : 'fa ' + this.icon
27953             })
27954         }
27955         
27956         var cfg = {
27957             tag : 'li',
27958             cn : [
27959                 {
27960                     tag : 'a',
27961                     href : this.href || '#',
27962                     cn : text
27963                 }
27964             ]
27965         };
27966         
27967         if(this.disable){
27968             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
27969         }
27970         
27971         if(this.submenu){
27972             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
27973             
27974             if(this.pos == 'left'){
27975                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
27976             }
27977         }
27978         
27979         return cfg;
27980     },
27981     
27982     initEvents : function() 
27983     {
27984         this.el.on('mouseover', this.onMouseOver, this);
27985         this.el.on('mouseout', this.onMouseOut, this);
27986         
27987         this.el.select('a', true).first().on('click', this.onClick, this);
27988         
27989     },
27990     
27991     onClick : function(e)
27992     {
27993         if(this.preventDefault){
27994             e.preventDefault();
27995         }
27996         
27997         this.fireEvent("click", this, e);
27998     },
27999     
28000     onMouseOver : function(e)
28001     {
28002         if(this.submenu && this.pos == 'left'){
28003             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28004         }
28005         
28006         this.fireEvent("mouseover", this, e);
28007     },
28008     
28009     onMouseOut : function(e)
28010     {
28011         this.fireEvent("mouseout", this, e);
28012     }
28013 });
28014
28015  
28016
28017  /*
28018  * - LGPL
28019  *
28020  * menu separator
28021  * 
28022  */
28023 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28024
28025 /**
28026  * @class Roo.bootstrap.menu.Separator
28027  * @extends Roo.bootstrap.Component
28028  * Bootstrap Separator class
28029  * 
28030  * @constructor
28031  * Create a new Separator
28032  * @param {Object} config The config object
28033  */
28034
28035
28036 Roo.bootstrap.menu.Separator = function(config){
28037     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28038 };
28039
28040 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28041     
28042     getAutoCreate : function(){
28043         var cfg = {
28044             tag : 'li',
28045             cls: 'divider'
28046         };
28047         
28048         return cfg;
28049     }
28050    
28051 });
28052
28053  
28054
28055  /*
28056  * - LGPL
28057  *
28058  * Tooltip
28059  * 
28060  */
28061
28062 /**
28063  * @class Roo.bootstrap.Tooltip
28064  * Bootstrap Tooltip class
28065  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28066  * to determine which dom element triggers the tooltip.
28067  * 
28068  * It needs to add support for additional attributes like tooltip-position
28069  * 
28070  * @constructor
28071  * Create a new Toolti
28072  * @param {Object} config The config object
28073  */
28074
28075 Roo.bootstrap.Tooltip = function(config){
28076     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28077     
28078     this.alignment = Roo.bootstrap.Tooltip.alignment;
28079     
28080     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28081         this.alignment = config.alignment;
28082     }
28083     
28084 };
28085
28086 Roo.apply(Roo.bootstrap.Tooltip, {
28087     /**
28088      * @function init initialize tooltip monitoring.
28089      * @static
28090      */
28091     currentEl : false,
28092     currentTip : false,
28093     currentRegion : false,
28094     
28095     //  init : delay?
28096     
28097     init : function()
28098     {
28099         Roo.get(document).on('mouseover', this.enter ,this);
28100         Roo.get(document).on('mouseout', this.leave, this);
28101          
28102         
28103         this.currentTip = new Roo.bootstrap.Tooltip();
28104     },
28105     
28106     enter : function(ev)
28107     {
28108         var dom = ev.getTarget();
28109         
28110         //Roo.log(['enter',dom]);
28111         var el = Roo.fly(dom);
28112         if (this.currentEl) {
28113             //Roo.log(dom);
28114             //Roo.log(this.currentEl);
28115             //Roo.log(this.currentEl.contains(dom));
28116             if (this.currentEl == el) {
28117                 return;
28118             }
28119             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28120                 return;
28121             }
28122
28123         }
28124         
28125         if (this.currentTip.el) {
28126             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28127         }    
28128         //Roo.log(ev);
28129         
28130         if(!el || el.dom == document){
28131             return;
28132         }
28133         
28134         var bindEl = el;
28135         
28136         // you can not look for children, as if el is the body.. then everythign is the child..
28137         if (!el.attr('tooltip')) { //
28138             if (!el.select("[tooltip]").elements.length) {
28139                 return;
28140             }
28141             // is the mouse over this child...?
28142             bindEl = el.select("[tooltip]").first();
28143             var xy = ev.getXY();
28144             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28145                 //Roo.log("not in region.");
28146                 return;
28147             }
28148             //Roo.log("child element over..");
28149             
28150         }
28151         this.currentEl = bindEl;
28152         this.currentTip.bind(bindEl);
28153         this.currentRegion = Roo.lib.Region.getRegion(dom);
28154         this.currentTip.enter();
28155         
28156     },
28157     leave : function(ev)
28158     {
28159         var dom = ev.getTarget();
28160         //Roo.log(['leave',dom]);
28161         if (!this.currentEl) {
28162             return;
28163         }
28164         
28165         
28166         if (dom != this.currentEl.dom) {
28167             return;
28168         }
28169         var xy = ev.getXY();
28170         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
28171             return;
28172         }
28173         // only activate leave if mouse cursor is outside... bounding box..
28174         
28175         
28176         
28177         
28178         if (this.currentTip) {
28179             this.currentTip.leave();
28180         }
28181         //Roo.log('clear currentEl');
28182         this.currentEl = false;
28183         
28184         
28185     },
28186     alignment : {
28187         'left' : ['r-l', [-2,0], 'right'],
28188         'right' : ['l-r', [2,0], 'left'],
28189         'bottom' : ['t-b', [0,2], 'top'],
28190         'top' : [ 'b-t', [0,-2], 'bottom']
28191     }
28192     
28193 });
28194
28195
28196 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
28197     
28198     
28199     bindEl : false,
28200     
28201     delay : null, // can be { show : 300 , hide: 500}
28202     
28203     timeout : null,
28204     
28205     hoverState : null, //???
28206     
28207     placement : 'bottom', 
28208     
28209     alignment : false,
28210     
28211     getAutoCreate : function(){
28212     
28213         var cfg = {
28214            cls : 'tooltip',
28215            role : 'tooltip',
28216            cn : [
28217                 {
28218                     cls : 'tooltip-arrow'
28219                 },
28220                 {
28221                     cls : 'tooltip-inner'
28222                 }
28223            ]
28224         };
28225         
28226         return cfg;
28227     },
28228     bind : function(el)
28229     {
28230         this.bindEl = el;
28231     },
28232       
28233     
28234     enter : function () {
28235        
28236         if (this.timeout != null) {
28237             clearTimeout(this.timeout);
28238         }
28239         
28240         this.hoverState = 'in';
28241          //Roo.log("enter - show");
28242         if (!this.delay || !this.delay.show) {
28243             this.show();
28244             return;
28245         }
28246         var _t = this;
28247         this.timeout = setTimeout(function () {
28248             if (_t.hoverState == 'in') {
28249                 _t.show();
28250             }
28251         }, this.delay.show);
28252     },
28253     leave : function()
28254     {
28255         clearTimeout(this.timeout);
28256     
28257         this.hoverState = 'out';
28258          if (!this.delay || !this.delay.hide) {
28259             this.hide();
28260             return;
28261         }
28262        
28263         var _t = this;
28264         this.timeout = setTimeout(function () {
28265             //Roo.log("leave - timeout");
28266             
28267             if (_t.hoverState == 'out') {
28268                 _t.hide();
28269                 Roo.bootstrap.Tooltip.currentEl = false;
28270             }
28271         }, delay);
28272     },
28273     
28274     show : function (msg)
28275     {
28276         if (!this.el) {
28277             this.render(document.body);
28278         }
28279         // set content.
28280         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28281         
28282         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28283         
28284         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28285         
28286         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
28287         
28288         var placement = typeof this.placement == 'function' ?
28289             this.placement.call(this, this.el, on_el) :
28290             this.placement;
28291             
28292         var autoToken = /\s?auto?\s?/i;
28293         var autoPlace = autoToken.test(placement);
28294         if (autoPlace) {
28295             placement = placement.replace(autoToken, '') || 'top';
28296         }
28297         
28298         //this.el.detach()
28299         //this.el.setXY([0,0]);
28300         this.el.show();
28301         //this.el.dom.style.display='block';
28302         
28303         //this.el.appendTo(on_el);
28304         
28305         var p = this.getPosition();
28306         var box = this.el.getBox();
28307         
28308         if (autoPlace) {
28309             // fixme..
28310         }
28311         
28312         var align = this.alignment[placement];
28313         
28314         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28315         
28316         if(placement == 'top' || placement == 'bottom'){
28317             if(xy[0] < 0){
28318                 placement = 'right';
28319             }
28320             
28321             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28322                 placement = 'left';
28323             }
28324             
28325             var scroll = Roo.select('body', true).first().getScroll();
28326             
28327             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28328                 placement = 'top';
28329             }
28330             
28331             align = this.alignment[placement];
28332         }
28333         
28334         this.el.alignTo(this.bindEl, align[0],align[1]);
28335         //var arrow = this.el.select('.arrow',true).first();
28336         //arrow.set(align[2], 
28337         
28338         this.el.addClass(placement);
28339         
28340         this.el.addClass('in fade');
28341         
28342         this.hoverState = null;
28343         
28344         if (this.el.hasClass('fade')) {
28345             // fade it?
28346         }
28347         
28348     },
28349     hide : function()
28350     {
28351          
28352         if (!this.el) {
28353             return;
28354         }
28355         //this.el.setXY([0,0]);
28356         this.el.removeClass('in');
28357         //this.el.hide();
28358         
28359     }
28360     
28361 });
28362  
28363
28364  /*
28365  * - LGPL
28366  *
28367  * Location Picker
28368  * 
28369  */
28370
28371 /**
28372  * @class Roo.bootstrap.LocationPicker
28373  * @extends Roo.bootstrap.Component
28374  * Bootstrap LocationPicker class
28375  * @cfg {Number} latitude Position when init default 0
28376  * @cfg {Number} longitude Position when init default 0
28377  * @cfg {Number} zoom default 15
28378  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
28379  * @cfg {Boolean} mapTypeControl default false
28380  * @cfg {Boolean} disableDoubleClickZoom default false
28381  * @cfg {Boolean} scrollwheel default true
28382  * @cfg {Boolean} streetViewControl default false
28383  * @cfg {Number} radius default 0
28384  * @cfg {String} locationName
28385  * @cfg {Boolean} draggable default true
28386  * @cfg {Boolean} enableAutocomplete default false
28387  * @cfg {Boolean} enableReverseGeocode default true
28388  * @cfg {String} markerTitle
28389  * 
28390  * @constructor
28391  * Create a new LocationPicker
28392  * @param {Object} config The config object
28393  */
28394
28395
28396 Roo.bootstrap.LocationPicker = function(config){
28397     
28398     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
28399     
28400     this.addEvents({
28401         /**
28402          * @event initial
28403          * Fires when the picker initialized.
28404          * @param {Roo.bootstrap.LocationPicker} this
28405          * @param {Google Location} location
28406          */
28407         initial : true,
28408         /**
28409          * @event positionchanged
28410          * Fires when the picker position changed.
28411          * @param {Roo.bootstrap.LocationPicker} this
28412          * @param {Google Location} location
28413          */
28414         positionchanged : true,
28415         /**
28416          * @event resize
28417          * Fires when the map resize.
28418          * @param {Roo.bootstrap.LocationPicker} this
28419          */
28420         resize : true,
28421         /**
28422          * @event show
28423          * Fires when the map show.
28424          * @param {Roo.bootstrap.LocationPicker} this
28425          */
28426         show : true,
28427         /**
28428          * @event hide
28429          * Fires when the map hide.
28430          * @param {Roo.bootstrap.LocationPicker} this
28431          */
28432         hide : true,
28433         /**
28434          * @event mapClick
28435          * Fires when click the map.
28436          * @param {Roo.bootstrap.LocationPicker} this
28437          * @param {Map event} e
28438          */
28439         mapClick : true,
28440         /**
28441          * @event mapRightClick
28442          * Fires when right click the map.
28443          * @param {Roo.bootstrap.LocationPicker} this
28444          * @param {Map event} e
28445          */
28446         mapRightClick : true,
28447         /**
28448          * @event markerClick
28449          * Fires when click the marker.
28450          * @param {Roo.bootstrap.LocationPicker} this
28451          * @param {Map event} e
28452          */
28453         markerClick : true,
28454         /**
28455          * @event markerRightClick
28456          * Fires when right click the marker.
28457          * @param {Roo.bootstrap.LocationPicker} this
28458          * @param {Map event} e
28459          */
28460         markerRightClick : true,
28461         /**
28462          * @event OverlayViewDraw
28463          * Fires when OverlayView Draw
28464          * @param {Roo.bootstrap.LocationPicker} this
28465          */
28466         OverlayViewDraw : true,
28467         /**
28468          * @event OverlayViewOnAdd
28469          * Fires when OverlayView Draw
28470          * @param {Roo.bootstrap.LocationPicker} this
28471          */
28472         OverlayViewOnAdd : true,
28473         /**
28474          * @event OverlayViewOnRemove
28475          * Fires when OverlayView Draw
28476          * @param {Roo.bootstrap.LocationPicker} this
28477          */
28478         OverlayViewOnRemove : true,
28479         /**
28480          * @event OverlayViewShow
28481          * Fires when OverlayView Draw
28482          * @param {Roo.bootstrap.LocationPicker} this
28483          * @param {Pixel} cpx
28484          */
28485         OverlayViewShow : true,
28486         /**
28487          * @event OverlayViewHide
28488          * Fires when OverlayView Draw
28489          * @param {Roo.bootstrap.LocationPicker} this
28490          */
28491         OverlayViewHide : true,
28492         /**
28493          * @event loadexception
28494          * Fires when load google lib failed.
28495          * @param {Roo.bootstrap.LocationPicker} this
28496          */
28497         loadexception : true
28498     });
28499         
28500 };
28501
28502 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
28503     
28504     gMapContext: false,
28505     
28506     latitude: 0,
28507     longitude: 0,
28508     zoom: 15,
28509     mapTypeId: false,
28510     mapTypeControl: false,
28511     disableDoubleClickZoom: false,
28512     scrollwheel: true,
28513     streetViewControl: false,
28514     radius: 0,
28515     locationName: '',
28516     draggable: true,
28517     enableAutocomplete: false,
28518     enableReverseGeocode: true,
28519     markerTitle: '',
28520     
28521     getAutoCreate: function()
28522     {
28523
28524         var cfg = {
28525             tag: 'div',
28526             cls: 'roo-location-picker'
28527         };
28528         
28529         return cfg
28530     },
28531     
28532     initEvents: function(ct, position)
28533     {       
28534         if(!this.el.getWidth() || this.isApplied()){
28535             return;
28536         }
28537         
28538         this.el.setVisibilityMode(Roo.Element.DISPLAY);
28539         
28540         this.initial();
28541     },
28542     
28543     initial: function()
28544     {
28545         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
28546             this.fireEvent('loadexception', this);
28547             return;
28548         }
28549         
28550         if(!this.mapTypeId){
28551             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
28552         }
28553         
28554         this.gMapContext = this.GMapContext();
28555         
28556         this.initOverlayView();
28557         
28558         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
28559         
28560         var _this = this;
28561                 
28562         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
28563             _this.setPosition(_this.gMapContext.marker.position);
28564         });
28565         
28566         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
28567             _this.fireEvent('mapClick', this, event);
28568             
28569         });
28570
28571         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
28572             _this.fireEvent('mapRightClick', this, event);
28573             
28574         });
28575         
28576         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
28577             _this.fireEvent('markerClick', this, event);
28578             
28579         });
28580
28581         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
28582             _this.fireEvent('markerRightClick', this, event);
28583             
28584         });
28585         
28586         this.setPosition(this.gMapContext.location);
28587         
28588         this.fireEvent('initial', this, this.gMapContext.location);
28589     },
28590     
28591     initOverlayView: function()
28592     {
28593         var _this = this;
28594         
28595         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
28596             
28597             draw: function()
28598             {
28599                 _this.fireEvent('OverlayViewDraw', _this);
28600             },
28601             
28602             onAdd: function()
28603             {
28604                 _this.fireEvent('OverlayViewOnAdd', _this);
28605             },
28606             
28607             onRemove: function()
28608             {
28609                 _this.fireEvent('OverlayViewOnRemove', _this);
28610             },
28611             
28612             show: function(cpx)
28613             {
28614                 _this.fireEvent('OverlayViewShow', _this, cpx);
28615             },
28616             
28617             hide: function()
28618             {
28619                 _this.fireEvent('OverlayViewHide', _this);
28620             }
28621             
28622         });
28623     },
28624     
28625     fromLatLngToContainerPixel: function(event)
28626     {
28627         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
28628     },
28629     
28630     isApplied: function() 
28631     {
28632         return this.getGmapContext() == false ? false : true;
28633     },
28634     
28635     getGmapContext: function() 
28636     {
28637         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
28638     },
28639     
28640     GMapContext: function() 
28641     {
28642         var position = new google.maps.LatLng(this.latitude, this.longitude);
28643         
28644         var _map = new google.maps.Map(this.el.dom, {
28645             center: position,
28646             zoom: this.zoom,
28647             mapTypeId: this.mapTypeId,
28648             mapTypeControl: this.mapTypeControl,
28649             disableDoubleClickZoom: this.disableDoubleClickZoom,
28650             scrollwheel: this.scrollwheel,
28651             streetViewControl: this.streetViewControl,
28652             locationName: this.locationName,
28653             draggable: this.draggable,
28654             enableAutocomplete: this.enableAutocomplete,
28655             enableReverseGeocode: this.enableReverseGeocode
28656         });
28657         
28658         var _marker = new google.maps.Marker({
28659             position: position,
28660             map: _map,
28661             title: this.markerTitle,
28662             draggable: this.draggable
28663         });
28664         
28665         return {
28666             map: _map,
28667             marker: _marker,
28668             circle: null,
28669             location: position,
28670             radius: this.radius,
28671             locationName: this.locationName,
28672             addressComponents: {
28673                 formatted_address: null,
28674                 addressLine1: null,
28675                 addressLine2: null,
28676                 streetName: null,
28677                 streetNumber: null,
28678                 city: null,
28679                 district: null,
28680                 state: null,
28681                 stateOrProvince: null
28682             },
28683             settings: this,
28684             domContainer: this.el.dom,
28685             geodecoder: new google.maps.Geocoder()
28686         };
28687     },
28688     
28689     drawCircle: function(center, radius, options) 
28690     {
28691         if (this.gMapContext.circle != null) {
28692             this.gMapContext.circle.setMap(null);
28693         }
28694         if (radius > 0) {
28695             radius *= 1;
28696             options = Roo.apply({}, options, {
28697                 strokeColor: "#0000FF",
28698                 strokeOpacity: .35,
28699                 strokeWeight: 2,
28700                 fillColor: "#0000FF",
28701                 fillOpacity: .2
28702             });
28703             
28704             options.map = this.gMapContext.map;
28705             options.radius = radius;
28706             options.center = center;
28707             this.gMapContext.circle = new google.maps.Circle(options);
28708             return this.gMapContext.circle;
28709         }
28710         
28711         return null;
28712     },
28713     
28714     setPosition: function(location) 
28715     {
28716         this.gMapContext.location = location;
28717         this.gMapContext.marker.setPosition(location);
28718         this.gMapContext.map.panTo(location);
28719         this.drawCircle(location, this.gMapContext.radius, {});
28720         
28721         var _this = this;
28722         
28723         if (this.gMapContext.settings.enableReverseGeocode) {
28724             this.gMapContext.geodecoder.geocode({
28725                 latLng: this.gMapContext.location
28726             }, function(results, status) {
28727                 
28728                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
28729                     _this.gMapContext.locationName = results[0].formatted_address;
28730                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
28731                     
28732                     _this.fireEvent('positionchanged', this, location);
28733                 }
28734             });
28735             
28736             return;
28737         }
28738         
28739         this.fireEvent('positionchanged', this, location);
28740     },
28741     
28742     resize: function()
28743     {
28744         google.maps.event.trigger(this.gMapContext.map, "resize");
28745         
28746         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
28747         
28748         this.fireEvent('resize', this);
28749     },
28750     
28751     setPositionByLatLng: function(latitude, longitude)
28752     {
28753         this.setPosition(new google.maps.LatLng(latitude, longitude));
28754     },
28755     
28756     getCurrentPosition: function() 
28757     {
28758         return {
28759             latitude: this.gMapContext.location.lat(),
28760             longitude: this.gMapContext.location.lng()
28761         };
28762     },
28763     
28764     getAddressName: function() 
28765     {
28766         return this.gMapContext.locationName;
28767     },
28768     
28769     getAddressComponents: function() 
28770     {
28771         return this.gMapContext.addressComponents;
28772     },
28773     
28774     address_component_from_google_geocode: function(address_components) 
28775     {
28776         var result = {};
28777         
28778         for (var i = 0; i < address_components.length; i++) {
28779             var component = address_components[i];
28780             if (component.types.indexOf("postal_code") >= 0) {
28781                 result.postalCode = component.short_name;
28782             } else if (component.types.indexOf("street_number") >= 0) {
28783                 result.streetNumber = component.short_name;
28784             } else if (component.types.indexOf("route") >= 0) {
28785                 result.streetName = component.short_name;
28786             } else if (component.types.indexOf("neighborhood") >= 0) {
28787                 result.city = component.short_name;
28788             } else if (component.types.indexOf("locality") >= 0) {
28789                 result.city = component.short_name;
28790             } else if (component.types.indexOf("sublocality") >= 0) {
28791                 result.district = component.short_name;
28792             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
28793                 result.stateOrProvince = component.short_name;
28794             } else if (component.types.indexOf("country") >= 0) {
28795                 result.country = component.short_name;
28796             }
28797         }
28798         
28799         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
28800         result.addressLine2 = "";
28801         return result;
28802     },
28803     
28804     setZoomLevel: function(zoom)
28805     {
28806         this.gMapContext.map.setZoom(zoom);
28807     },
28808     
28809     show: function()
28810     {
28811         if(!this.el){
28812             return;
28813         }
28814         
28815         this.el.show();
28816         
28817         this.resize();
28818         
28819         this.fireEvent('show', this);
28820     },
28821     
28822     hide: function()
28823     {
28824         if(!this.el){
28825             return;
28826         }
28827         
28828         this.el.hide();
28829         
28830         this.fireEvent('hide', this);
28831     }
28832     
28833 });
28834
28835 Roo.apply(Roo.bootstrap.LocationPicker, {
28836     
28837     OverlayView : function(map, options)
28838     {
28839         options = options || {};
28840         
28841         this.setMap(map);
28842     }
28843     
28844     
28845 });/**
28846  * @class Roo.bootstrap.Alert
28847  * @extends Roo.bootstrap.Component
28848  * Bootstrap Alert class - shows an alert area box
28849  * eg
28850  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
28851   Enter a valid email address
28852 </div>
28853  * @licence LGPL
28854  * @cfg {String} title The title of alert
28855  * @cfg {String} html The content of alert
28856  * @cfg {String} weight (  success | info | warning | danger )
28857  * @cfg {String} faicon font-awesomeicon
28858  * 
28859  * @constructor
28860  * Create a new alert
28861  * @param {Object} config The config object
28862  */
28863
28864
28865 Roo.bootstrap.Alert = function(config){
28866     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
28867     
28868 };
28869
28870 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
28871     
28872     title: '',
28873     html: '',
28874     weight: false,
28875     faicon: false,
28876     
28877     getAutoCreate : function()
28878     {
28879         
28880         var cfg = {
28881             tag : 'div',
28882             cls : 'alert',
28883             cn : [
28884                 {
28885                     tag : 'i',
28886                     cls : 'roo-alert-icon'
28887                     
28888                 },
28889                 {
28890                     tag : 'b',
28891                     cls : 'roo-alert-title',
28892                     html : this.title
28893                 },
28894                 {
28895                     tag : 'span',
28896                     cls : 'roo-alert-text',
28897                     html : this.html
28898                 }
28899             ]
28900         };
28901         
28902         if(this.faicon){
28903             cfg.cn[0].cls += ' fa ' + this.faicon;
28904         }
28905         
28906         if(this.weight){
28907             cfg.cls += ' alert-' + this.weight;
28908         }
28909         
28910         return cfg;
28911     },
28912     
28913     initEvents: function() 
28914     {
28915         this.el.setVisibilityMode(Roo.Element.DISPLAY);
28916     },
28917     
28918     setTitle : function(str)
28919     {
28920         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
28921     },
28922     
28923     setText : function(str)
28924     {
28925         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
28926     },
28927     
28928     setWeight : function(weight)
28929     {
28930         if(this.weight){
28931             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
28932         }
28933         
28934         this.weight = weight;
28935         
28936         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
28937     },
28938     
28939     setIcon : function(icon)
28940     {
28941         if(this.faicon){
28942             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
28943         }
28944         
28945         this.faicon = icon;
28946         
28947         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
28948     },
28949     
28950     hide: function() 
28951     {
28952         this.el.hide();   
28953     },
28954     
28955     show: function() 
28956     {  
28957         this.el.show();   
28958     }
28959     
28960 });
28961
28962  
28963 /*
28964 * Licence: LGPL
28965 */
28966
28967 /**
28968  * @class Roo.bootstrap.UploadCropbox
28969  * @extends Roo.bootstrap.Component
28970  * Bootstrap UploadCropbox class
28971  * @cfg {String} emptyText show when image has been loaded
28972  * @cfg {String} rotateNotify show when image too small to rotate
28973  * @cfg {Number} errorTimeout default 3000
28974  * @cfg {Number} minWidth default 300
28975  * @cfg {Number} minHeight default 300
28976  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
28977  * @cfg {Boolean} isDocument (true|false) default false
28978  * @cfg {String} url action url
28979  * @cfg {String} paramName default 'imageUpload'
28980  * @cfg {String} method default POST
28981  * @cfg {Boolean} loadMask (true|false) default true
28982  * @cfg {Boolean} loadingText default 'Loading...'
28983  * 
28984  * @constructor
28985  * Create a new UploadCropbox
28986  * @param {Object} config The config object
28987  */
28988
28989 Roo.bootstrap.UploadCropbox = function(config){
28990     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
28991     
28992     this.addEvents({
28993         /**
28994          * @event beforeselectfile
28995          * Fire before select file
28996          * @param {Roo.bootstrap.UploadCropbox} this
28997          */
28998         "beforeselectfile" : true,
28999         /**
29000          * @event initial
29001          * Fire after initEvent
29002          * @param {Roo.bootstrap.UploadCropbox} this
29003          */
29004         "initial" : true,
29005         /**
29006          * @event crop
29007          * Fire after initEvent
29008          * @param {Roo.bootstrap.UploadCropbox} this
29009          * @param {String} data
29010          */
29011         "crop" : true,
29012         /**
29013          * @event prepare
29014          * Fire when preparing the file data
29015          * @param {Roo.bootstrap.UploadCropbox} this
29016          * @param {Object} file
29017          */
29018         "prepare" : true,
29019         /**
29020          * @event exception
29021          * Fire when get exception
29022          * @param {Roo.bootstrap.UploadCropbox} this
29023          * @param {XMLHttpRequest} xhr
29024          */
29025         "exception" : true,
29026         /**
29027          * @event beforeloadcanvas
29028          * Fire before load the canvas
29029          * @param {Roo.bootstrap.UploadCropbox} this
29030          * @param {String} src
29031          */
29032         "beforeloadcanvas" : true,
29033         /**
29034          * @event trash
29035          * Fire when trash image
29036          * @param {Roo.bootstrap.UploadCropbox} this
29037          */
29038         "trash" : true,
29039         /**
29040          * @event download
29041          * Fire when download the image
29042          * @param {Roo.bootstrap.UploadCropbox} this
29043          */
29044         "download" : true,
29045         /**
29046          * @event footerbuttonclick
29047          * Fire when footerbuttonclick
29048          * @param {Roo.bootstrap.UploadCropbox} this
29049          * @param {String} type
29050          */
29051         "footerbuttonclick" : true,
29052         /**
29053          * @event resize
29054          * Fire when resize
29055          * @param {Roo.bootstrap.UploadCropbox} this
29056          */
29057         "resize" : true,
29058         /**
29059          * @event rotate
29060          * Fire when rotate the image
29061          * @param {Roo.bootstrap.UploadCropbox} this
29062          * @param {String} pos
29063          */
29064         "rotate" : true,
29065         /**
29066          * @event inspect
29067          * Fire when inspect the file
29068          * @param {Roo.bootstrap.UploadCropbox} this
29069          * @param {Object} file
29070          */
29071         "inspect" : true,
29072         /**
29073          * @event upload
29074          * Fire when xhr upload the file
29075          * @param {Roo.bootstrap.UploadCropbox} this
29076          * @param {Object} data
29077          */
29078         "upload" : true,
29079         /**
29080          * @event arrange
29081          * Fire when arrange the file data
29082          * @param {Roo.bootstrap.UploadCropbox} this
29083          * @param {Object} formData
29084          */
29085         "arrange" : true
29086     });
29087     
29088     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29089 };
29090
29091 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
29092     
29093     emptyText : 'Click to upload image',
29094     rotateNotify : 'Image is too small to rotate',
29095     errorTimeout : 3000,
29096     scale : 0,
29097     baseScale : 1,
29098     rotate : 0,
29099     dragable : false,
29100     pinching : false,
29101     mouseX : 0,
29102     mouseY : 0,
29103     cropData : false,
29104     minWidth : 300,
29105     minHeight : 300,
29106     file : false,
29107     exif : {},
29108     baseRotate : 1,
29109     cropType : 'image/jpeg',
29110     buttons : false,
29111     canvasLoaded : false,
29112     isDocument : false,
29113     method : 'POST',
29114     paramName : 'imageUpload',
29115     loadMask : true,
29116     loadingText : 'Loading...',
29117     maskEl : false,
29118     
29119     getAutoCreate : function()
29120     {
29121         var cfg = {
29122             tag : 'div',
29123             cls : 'roo-upload-cropbox',
29124             cn : [
29125                 {
29126                     tag : 'input',
29127                     cls : 'roo-upload-cropbox-selector',
29128                     type : 'file'
29129                 },
29130                 {
29131                     tag : 'div',
29132                     cls : 'roo-upload-cropbox-body',
29133                     style : 'cursor:pointer',
29134                     cn : [
29135                         {
29136                             tag : 'div',
29137                             cls : 'roo-upload-cropbox-preview'
29138                         },
29139                         {
29140                             tag : 'div',
29141                             cls : 'roo-upload-cropbox-thumb'
29142                         },
29143                         {
29144                             tag : 'div',
29145                             cls : 'roo-upload-cropbox-empty-notify',
29146                             html : this.emptyText
29147                         },
29148                         {
29149                             tag : 'div',
29150                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29151                             html : this.rotateNotify
29152                         }
29153                     ]
29154                 },
29155                 {
29156                     tag : 'div',
29157                     cls : 'roo-upload-cropbox-footer',
29158                     cn : {
29159                         tag : 'div',
29160                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29161                         cn : []
29162                     }
29163                 }
29164             ]
29165         };
29166         
29167         return cfg;
29168     },
29169     
29170     onRender : function(ct, position)
29171     {
29172         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29173         
29174         if (this.buttons.length) {
29175             
29176             Roo.each(this.buttons, function(bb) {
29177                 
29178                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29179                 
29180                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29181                 
29182             }, this);
29183         }
29184         
29185         if(this.loadMask){
29186             this.maskEl = this.el;
29187         }
29188     },
29189     
29190     initEvents : function()
29191     {
29192         this.urlAPI = (window.createObjectURL && window) || 
29193                                 (window.URL && URL.revokeObjectURL && URL) || 
29194                                 (window.webkitURL && webkitURL);
29195                         
29196         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29197         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29198         
29199         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29200         this.selectorEl.hide();
29201         
29202         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29203         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29204         
29205         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29206         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29207         this.thumbEl.hide();
29208         
29209         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29210         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29211         
29212         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29213         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29214         this.errorEl.hide();
29215         
29216         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29217         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29218         this.footerEl.hide();
29219         
29220         this.setThumbBoxSize();
29221         
29222         this.bind();
29223         
29224         this.resize();
29225         
29226         this.fireEvent('initial', this);
29227     },
29228
29229     bind : function()
29230     {
29231         var _this = this;
29232         
29233         window.addEventListener("resize", function() { _this.resize(); } );
29234         
29235         this.bodyEl.on('click', this.beforeSelectFile, this);
29236         
29237         if(Roo.isTouch){
29238             this.bodyEl.on('touchstart', this.onTouchStart, this);
29239             this.bodyEl.on('touchmove', this.onTouchMove, this);
29240             this.bodyEl.on('touchend', this.onTouchEnd, this);
29241         }
29242         
29243         if(!Roo.isTouch){
29244             this.bodyEl.on('mousedown', this.onMouseDown, this);
29245             this.bodyEl.on('mousemove', this.onMouseMove, this);
29246             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29247             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29248             Roo.get(document).on('mouseup', this.onMouseUp, this);
29249         }
29250         
29251         this.selectorEl.on('change', this.onFileSelected, this);
29252     },
29253     
29254     reset : function()
29255     {    
29256         this.scale = 0;
29257         this.baseScale = 1;
29258         this.rotate = 0;
29259         this.baseRotate = 1;
29260         this.dragable = false;
29261         this.pinching = false;
29262         this.mouseX = 0;
29263         this.mouseY = 0;
29264         this.cropData = false;
29265         this.notifyEl.dom.innerHTML = this.emptyText;
29266         
29267         this.selectorEl.dom.value = '';
29268         
29269     },
29270     
29271     resize : function()
29272     {
29273         if(this.fireEvent('resize', this) != false){
29274             this.setThumbBoxPosition();
29275             this.setCanvasPosition();
29276         }
29277     },
29278     
29279     onFooterButtonClick : function(e, el, o, type)
29280     {
29281         switch (type) {
29282             case 'rotate-left' :
29283                 this.onRotateLeft(e);
29284                 break;
29285             case 'rotate-right' :
29286                 this.onRotateRight(e);
29287                 break;
29288             case 'picture' :
29289                 this.beforeSelectFile(e);
29290                 break;
29291             case 'trash' :
29292                 this.trash(e);
29293                 break;
29294             case 'crop' :
29295                 this.crop(e);
29296                 break;
29297             case 'download' :
29298                 this.download(e);
29299                 break;
29300             default :
29301                 break;
29302         }
29303         
29304         this.fireEvent('footerbuttonclick', this, type);
29305     },
29306     
29307     beforeSelectFile : function(e)
29308     {
29309         e.preventDefault();
29310         
29311         if(this.fireEvent('beforeselectfile', this) != false){
29312             this.selectorEl.dom.click();
29313         }
29314     },
29315     
29316     onFileSelected : function(e)
29317     {
29318         e.preventDefault();
29319         
29320         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29321             return;
29322         }
29323         
29324         var file = this.selectorEl.dom.files[0];
29325         
29326         if(this.fireEvent('inspect', this, file) != false){
29327             this.prepare(file);
29328         }
29329         
29330     },
29331     
29332     trash : function(e)
29333     {
29334         this.fireEvent('trash', this);
29335     },
29336     
29337     download : function(e)
29338     {
29339         this.fireEvent('download', this);
29340     },
29341     
29342     loadCanvas : function(src)
29343     {   
29344         if(this.fireEvent('beforeloadcanvas', this, src) != false){
29345             
29346             this.reset();
29347             
29348             this.imageEl = document.createElement('img');
29349             
29350             var _this = this;
29351             
29352             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29353             
29354             this.imageEl.src = src;
29355         }
29356     },
29357     
29358     onLoadCanvas : function()
29359     {   
29360         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29361         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29362         
29363         this.bodyEl.un('click', this.beforeSelectFile, this);
29364         
29365         this.notifyEl.hide();
29366         this.thumbEl.show();
29367         this.footerEl.show();
29368         
29369         this.baseRotateLevel();
29370         
29371         if(this.isDocument){
29372             this.setThumbBoxSize();
29373         }
29374         
29375         this.setThumbBoxPosition();
29376         
29377         this.baseScaleLevel();
29378         
29379         this.draw();
29380         
29381         this.resize();
29382         
29383         this.canvasLoaded = true;
29384         
29385         if(this.loadMask){
29386             this.maskEl.unmask();
29387         }
29388         
29389     },
29390     
29391     setCanvasPosition : function()
29392     {   
29393         if(!this.canvasEl){
29394             return;
29395         }
29396         
29397         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
29398         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
29399         
29400         this.previewEl.setLeft(pw);
29401         this.previewEl.setTop(ph);
29402         
29403     },
29404     
29405     onMouseDown : function(e)
29406     {   
29407         e.stopEvent();
29408         
29409         this.dragable = true;
29410         this.pinching = false;
29411         
29412         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
29413             this.dragable = false;
29414             return;
29415         }
29416         
29417         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29418         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29419         
29420     },
29421     
29422     onMouseMove : function(e)
29423     {   
29424         e.stopEvent();
29425         
29426         if(!this.canvasLoaded){
29427             return;
29428         }
29429         
29430         if (!this.dragable){
29431             return;
29432         }
29433         
29434         var minX = Math.ceil(this.thumbEl.getLeft(true));
29435         var minY = Math.ceil(this.thumbEl.getTop(true));
29436         
29437         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
29438         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
29439         
29440         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29441         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29442         
29443         x = x - this.mouseX;
29444         y = y - this.mouseY;
29445         
29446         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
29447         var bgY = Math.ceil(y + this.previewEl.getTop(true));
29448         
29449         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
29450         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
29451         
29452         this.previewEl.setLeft(bgX);
29453         this.previewEl.setTop(bgY);
29454         
29455         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29456         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29457     },
29458     
29459     onMouseUp : function(e)
29460     {   
29461         e.stopEvent();
29462         
29463         this.dragable = false;
29464     },
29465     
29466     onMouseWheel : function(e)
29467     {   
29468         e.stopEvent();
29469         
29470         this.startScale = this.scale;
29471         
29472         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
29473         
29474         if(!this.zoomable()){
29475             this.scale = this.startScale;
29476             return;
29477         }
29478         
29479         this.draw();
29480         
29481         return;
29482     },
29483     
29484     zoomable : function()
29485     {
29486         var minScale = this.thumbEl.getWidth() / this.minWidth;
29487         
29488         if(this.minWidth < this.minHeight){
29489             minScale = this.thumbEl.getHeight() / this.minHeight;
29490         }
29491         
29492         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
29493         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
29494         
29495         if(
29496                 this.isDocument &&
29497                 (this.rotate == 0 || this.rotate == 180) && 
29498                 (
29499                     width > this.imageEl.OriginWidth || 
29500                     height > this.imageEl.OriginHeight ||
29501                     (width < this.minWidth && height < this.minHeight)
29502                 )
29503         ){
29504             return false;
29505         }
29506         
29507         if(
29508                 this.isDocument &&
29509                 (this.rotate == 90 || this.rotate == 270) && 
29510                 (
29511                     width > this.imageEl.OriginWidth || 
29512                     height > this.imageEl.OriginHeight ||
29513                     (width < this.minHeight && height < this.minWidth)
29514                 )
29515         ){
29516             return false;
29517         }
29518         
29519         if(
29520                 !this.isDocument &&
29521                 (this.rotate == 0 || this.rotate == 180) && 
29522                 (
29523                     width < this.minWidth || 
29524                     width > this.imageEl.OriginWidth || 
29525                     height < this.minHeight || 
29526                     height > this.imageEl.OriginHeight
29527                 )
29528         ){
29529             return false;
29530         }
29531         
29532         if(
29533                 !this.isDocument &&
29534                 (this.rotate == 90 || this.rotate == 270) && 
29535                 (
29536                     width < this.minHeight || 
29537                     width > this.imageEl.OriginWidth || 
29538                     height < this.minWidth || 
29539                     height > this.imageEl.OriginHeight
29540                 )
29541         ){
29542             return false;
29543         }
29544         
29545         return true;
29546         
29547     },
29548     
29549     onRotateLeft : function(e)
29550     {   
29551         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29552             
29553             var minScale = this.thumbEl.getWidth() / this.minWidth;
29554             
29555             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29556             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29557             
29558             this.startScale = this.scale;
29559             
29560             while (this.getScaleLevel() < minScale){
29561             
29562                 this.scale = this.scale + 1;
29563                 
29564                 if(!this.zoomable()){
29565                     break;
29566                 }
29567                 
29568                 if(
29569                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29570                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29571                 ){
29572                     continue;
29573                 }
29574                 
29575                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29576
29577                 this.draw();
29578                 
29579                 return;
29580             }
29581             
29582             this.scale = this.startScale;
29583             
29584             this.onRotateFail();
29585             
29586             return false;
29587         }
29588         
29589         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29590
29591         if(this.isDocument){
29592             this.setThumbBoxSize();
29593             this.setThumbBoxPosition();
29594             this.setCanvasPosition();
29595         }
29596         
29597         this.draw();
29598         
29599         this.fireEvent('rotate', this, 'left');
29600         
29601     },
29602     
29603     onRotateRight : function(e)
29604     {
29605         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29606             
29607             var minScale = this.thumbEl.getWidth() / this.minWidth;
29608         
29609             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29610             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29611             
29612             this.startScale = this.scale;
29613             
29614             while (this.getScaleLevel() < minScale){
29615             
29616                 this.scale = this.scale + 1;
29617                 
29618                 if(!this.zoomable()){
29619                     break;
29620                 }
29621                 
29622                 if(
29623                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29624                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29625                 ){
29626                     continue;
29627                 }
29628                 
29629                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29630
29631                 this.draw();
29632                 
29633                 return;
29634             }
29635             
29636             this.scale = this.startScale;
29637             
29638             this.onRotateFail();
29639             
29640             return false;
29641         }
29642         
29643         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29644
29645         if(this.isDocument){
29646             this.setThumbBoxSize();
29647             this.setThumbBoxPosition();
29648             this.setCanvasPosition();
29649         }
29650         
29651         this.draw();
29652         
29653         this.fireEvent('rotate', this, 'right');
29654     },
29655     
29656     onRotateFail : function()
29657     {
29658         this.errorEl.show(true);
29659         
29660         var _this = this;
29661         
29662         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
29663     },
29664     
29665     draw : function()
29666     {
29667         this.previewEl.dom.innerHTML = '';
29668         
29669         var canvasEl = document.createElement("canvas");
29670         
29671         var contextEl = canvasEl.getContext("2d");
29672         
29673         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29674         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29675         var center = this.imageEl.OriginWidth / 2;
29676         
29677         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
29678             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29679             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29680             center = this.imageEl.OriginHeight / 2;
29681         }
29682         
29683         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
29684         
29685         contextEl.translate(center, center);
29686         contextEl.rotate(this.rotate * Math.PI / 180);
29687
29688         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
29689         
29690         this.canvasEl = document.createElement("canvas");
29691         
29692         this.contextEl = this.canvasEl.getContext("2d");
29693         
29694         switch (this.rotate) {
29695             case 0 :
29696                 
29697                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29698                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29699                 
29700                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29701                 
29702                 break;
29703             case 90 : 
29704                 
29705                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29706                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29707                 
29708                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29709                     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);
29710                     break;
29711                 }
29712                 
29713                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29714                 
29715                 break;
29716             case 180 :
29717                 
29718                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29719                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29720                 
29721                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29722                     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);
29723                     break;
29724                 }
29725                 
29726                 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);
29727                 
29728                 break;
29729             case 270 :
29730                 
29731                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29732                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29733         
29734                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29735                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29736                     break;
29737                 }
29738                 
29739                 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);
29740                 
29741                 break;
29742             default : 
29743                 break;
29744         }
29745         
29746         this.previewEl.appendChild(this.canvasEl);
29747         
29748         this.setCanvasPosition();
29749     },
29750     
29751     crop : function()
29752     {
29753         if(!this.canvasLoaded){
29754             return;
29755         }
29756         
29757         var imageCanvas = document.createElement("canvas");
29758         
29759         var imageContext = imageCanvas.getContext("2d");
29760         
29761         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
29762         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
29763         
29764         var center = imageCanvas.width / 2;
29765         
29766         imageContext.translate(center, center);
29767         
29768         imageContext.rotate(this.rotate * Math.PI / 180);
29769         
29770         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
29771         
29772         var canvas = document.createElement("canvas");
29773         
29774         var context = canvas.getContext("2d");
29775                 
29776         canvas.width = this.minWidth;
29777         canvas.height = this.minHeight;
29778
29779         switch (this.rotate) {
29780             case 0 :
29781                 
29782                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
29783                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
29784                 
29785                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29786                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29787                 
29788                 var targetWidth = this.minWidth - 2 * x;
29789                 var targetHeight = this.minHeight - 2 * y;
29790                 
29791                 var scale = 1;
29792                 
29793                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29794                     scale = targetWidth / width;
29795                 }
29796                 
29797                 if(x > 0 && y == 0){
29798                     scale = targetHeight / height;
29799                 }
29800                 
29801                 if(x > 0 && y > 0){
29802                     scale = targetWidth / width;
29803                     
29804                     if(width < height){
29805                         scale = targetHeight / height;
29806                     }
29807                 }
29808                 
29809                 context.scale(scale, scale);
29810                 
29811                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29812                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29813
29814                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29815                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29816
29817                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29818                 
29819                 break;
29820             case 90 : 
29821                 
29822                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
29823                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
29824                 
29825                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29826                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29827                 
29828                 var targetWidth = this.minWidth - 2 * x;
29829                 var targetHeight = this.minHeight - 2 * y;
29830                 
29831                 var scale = 1;
29832                 
29833                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29834                     scale = targetWidth / width;
29835                 }
29836                 
29837                 if(x > 0 && y == 0){
29838                     scale = targetHeight / height;
29839                 }
29840                 
29841                 if(x > 0 && y > 0){
29842                     scale = targetWidth / width;
29843                     
29844                     if(width < height){
29845                         scale = targetHeight / height;
29846                     }
29847                 }
29848                 
29849                 context.scale(scale, scale);
29850                 
29851                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29852                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29853
29854                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29855                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29856                 
29857                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
29858                 
29859                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29860                 
29861                 break;
29862             case 180 :
29863                 
29864                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
29865                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
29866                 
29867                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29868                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29869                 
29870                 var targetWidth = this.minWidth - 2 * x;
29871                 var targetHeight = this.minHeight - 2 * y;
29872                 
29873                 var scale = 1;
29874                 
29875                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29876                     scale = targetWidth / width;
29877                 }
29878                 
29879                 if(x > 0 && y == 0){
29880                     scale = targetHeight / height;
29881                 }
29882                 
29883                 if(x > 0 && y > 0){
29884                     scale = targetWidth / width;
29885                     
29886                     if(width < height){
29887                         scale = targetHeight / height;
29888                     }
29889                 }
29890                 
29891                 context.scale(scale, scale);
29892                 
29893                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29894                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29895
29896                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29897                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29898
29899                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
29900                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
29901                 
29902                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29903                 
29904                 break;
29905             case 270 :
29906                 
29907                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
29908                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
29909                 
29910                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29911                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29912                 
29913                 var targetWidth = this.minWidth - 2 * x;
29914                 var targetHeight = this.minHeight - 2 * y;
29915                 
29916                 var scale = 1;
29917                 
29918                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29919                     scale = targetWidth / width;
29920                 }
29921                 
29922                 if(x > 0 && y == 0){
29923                     scale = targetHeight / height;
29924                 }
29925                 
29926                 if(x > 0 && y > 0){
29927                     scale = targetWidth / width;
29928                     
29929                     if(width < height){
29930                         scale = targetHeight / height;
29931                     }
29932                 }
29933                 
29934                 context.scale(scale, scale);
29935                 
29936                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29937                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29938
29939                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29940                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29941                 
29942                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
29943                 
29944                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29945                 
29946                 break;
29947             default : 
29948                 break;
29949         }
29950         
29951         this.cropData = canvas.toDataURL(this.cropType);
29952         
29953         if(this.fireEvent('crop', this, this.cropData) !== false){
29954             this.process(this.file, this.cropData);
29955         }
29956         
29957         return;
29958         
29959     },
29960     
29961     setThumbBoxSize : function()
29962     {
29963         var width, height;
29964         
29965         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
29966             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
29967             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
29968             
29969             this.minWidth = width;
29970             this.minHeight = height;
29971             
29972             if(this.rotate == 90 || this.rotate == 270){
29973                 this.minWidth = height;
29974                 this.minHeight = width;
29975             }
29976         }
29977         
29978         height = 300;
29979         width = Math.ceil(this.minWidth * height / this.minHeight);
29980         
29981         if(this.minWidth > this.minHeight){
29982             width = 300;
29983             height = Math.ceil(this.minHeight * width / this.minWidth);
29984         }
29985         
29986         this.thumbEl.setStyle({
29987             width : width + 'px',
29988             height : height + 'px'
29989         });
29990
29991         return;
29992             
29993     },
29994     
29995     setThumbBoxPosition : function()
29996     {
29997         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
29998         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
29999         
30000         this.thumbEl.setLeft(x);
30001         this.thumbEl.setTop(y);
30002         
30003     },
30004     
30005     baseRotateLevel : function()
30006     {
30007         this.baseRotate = 1;
30008         
30009         if(
30010                 typeof(this.exif) != 'undefined' &&
30011                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30012                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30013         ){
30014             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30015         }
30016         
30017         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30018         
30019     },
30020     
30021     baseScaleLevel : function()
30022     {
30023         var width, height;
30024         
30025         if(this.isDocument){
30026             
30027             if(this.baseRotate == 6 || this.baseRotate == 8){
30028             
30029                 height = this.thumbEl.getHeight();
30030                 this.baseScale = height / this.imageEl.OriginWidth;
30031
30032                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30033                     width = this.thumbEl.getWidth();
30034                     this.baseScale = width / this.imageEl.OriginHeight;
30035                 }
30036
30037                 return;
30038             }
30039
30040             height = this.thumbEl.getHeight();
30041             this.baseScale = height / this.imageEl.OriginHeight;
30042
30043             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30044                 width = this.thumbEl.getWidth();
30045                 this.baseScale = width / this.imageEl.OriginWidth;
30046             }
30047
30048             return;
30049         }
30050         
30051         if(this.baseRotate == 6 || this.baseRotate == 8){
30052             
30053             width = this.thumbEl.getHeight();
30054             this.baseScale = width / this.imageEl.OriginHeight;
30055             
30056             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30057                 height = this.thumbEl.getWidth();
30058                 this.baseScale = height / this.imageEl.OriginHeight;
30059             }
30060             
30061             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30062                 height = this.thumbEl.getWidth();
30063                 this.baseScale = height / this.imageEl.OriginHeight;
30064                 
30065                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30066                     width = this.thumbEl.getHeight();
30067                     this.baseScale = width / this.imageEl.OriginWidth;
30068                 }
30069             }
30070             
30071             return;
30072         }
30073         
30074         width = this.thumbEl.getWidth();
30075         this.baseScale = width / this.imageEl.OriginWidth;
30076         
30077         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30078             height = this.thumbEl.getHeight();
30079             this.baseScale = height / this.imageEl.OriginHeight;
30080         }
30081         
30082         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30083             
30084             height = this.thumbEl.getHeight();
30085             this.baseScale = height / this.imageEl.OriginHeight;
30086             
30087             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30088                 width = this.thumbEl.getWidth();
30089                 this.baseScale = width / this.imageEl.OriginWidth;
30090             }
30091             
30092         }
30093         
30094         return;
30095     },
30096     
30097     getScaleLevel : function()
30098     {
30099         return this.baseScale * Math.pow(1.1, this.scale);
30100     },
30101     
30102     onTouchStart : function(e)
30103     {
30104         if(!this.canvasLoaded){
30105             this.beforeSelectFile(e);
30106             return;
30107         }
30108         
30109         var touches = e.browserEvent.touches;
30110         
30111         if(!touches){
30112             return;
30113         }
30114         
30115         if(touches.length == 1){
30116             this.onMouseDown(e);
30117             return;
30118         }
30119         
30120         if(touches.length != 2){
30121             return;
30122         }
30123         
30124         var coords = [];
30125         
30126         for(var i = 0, finger; finger = touches[i]; i++){
30127             coords.push(finger.pageX, finger.pageY);
30128         }
30129         
30130         var x = Math.pow(coords[0] - coords[2], 2);
30131         var y = Math.pow(coords[1] - coords[3], 2);
30132         
30133         this.startDistance = Math.sqrt(x + y);
30134         
30135         this.startScale = this.scale;
30136         
30137         this.pinching = true;
30138         this.dragable = false;
30139         
30140     },
30141     
30142     onTouchMove : function(e)
30143     {
30144         if(!this.pinching && !this.dragable){
30145             return;
30146         }
30147         
30148         var touches = e.browserEvent.touches;
30149         
30150         if(!touches){
30151             return;
30152         }
30153         
30154         if(this.dragable){
30155             this.onMouseMove(e);
30156             return;
30157         }
30158         
30159         var coords = [];
30160         
30161         for(var i = 0, finger; finger = touches[i]; i++){
30162             coords.push(finger.pageX, finger.pageY);
30163         }
30164         
30165         var x = Math.pow(coords[0] - coords[2], 2);
30166         var y = Math.pow(coords[1] - coords[3], 2);
30167         
30168         this.endDistance = Math.sqrt(x + y);
30169         
30170         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30171         
30172         if(!this.zoomable()){
30173             this.scale = this.startScale;
30174             return;
30175         }
30176         
30177         this.draw();
30178         
30179     },
30180     
30181     onTouchEnd : function(e)
30182     {
30183         this.pinching = false;
30184         this.dragable = false;
30185         
30186     },
30187     
30188     process : function(file, crop)
30189     {
30190         if(this.loadMask){
30191             this.maskEl.mask(this.loadingText);
30192         }
30193         
30194         this.xhr = new XMLHttpRequest();
30195         
30196         file.xhr = this.xhr;
30197
30198         this.xhr.open(this.method, this.url, true);
30199         
30200         var headers = {
30201             "Accept": "application/json",
30202             "Cache-Control": "no-cache",
30203             "X-Requested-With": "XMLHttpRequest"
30204         };
30205         
30206         for (var headerName in headers) {
30207             var headerValue = headers[headerName];
30208             if (headerValue) {
30209                 this.xhr.setRequestHeader(headerName, headerValue);
30210             }
30211         }
30212         
30213         var _this = this;
30214         
30215         this.xhr.onload = function()
30216         {
30217             _this.xhrOnLoad(_this.xhr);
30218         }
30219         
30220         this.xhr.onerror = function()
30221         {
30222             _this.xhrOnError(_this.xhr);
30223         }
30224         
30225         var formData = new FormData();
30226
30227         formData.append('returnHTML', 'NO');
30228         
30229         if(crop){
30230             formData.append('crop', crop);
30231         }
30232         
30233         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30234             formData.append(this.paramName, file, file.name);
30235         }
30236         
30237         if(typeof(file.filename) != 'undefined'){
30238             formData.append('filename', file.filename);
30239         }
30240         
30241         if(typeof(file.mimetype) != 'undefined'){
30242             formData.append('mimetype', file.mimetype);
30243         }
30244         
30245         if(this.fireEvent('arrange', this, formData) != false){
30246             this.xhr.send(formData);
30247         };
30248     },
30249     
30250     xhrOnLoad : function(xhr)
30251     {
30252         if(this.loadMask){
30253             this.maskEl.unmask();
30254         }
30255         
30256         if (xhr.readyState !== 4) {
30257             this.fireEvent('exception', this, xhr);
30258             return;
30259         }
30260
30261         var response = Roo.decode(xhr.responseText);
30262         
30263         if(!response.success){
30264             this.fireEvent('exception', this, xhr);
30265             return;
30266         }
30267         
30268         var response = Roo.decode(xhr.responseText);
30269         
30270         this.fireEvent('upload', this, response);
30271         
30272     },
30273     
30274     xhrOnError : function()
30275     {
30276         if(this.loadMask){
30277             this.maskEl.unmask();
30278         }
30279         
30280         Roo.log('xhr on error');
30281         
30282         var response = Roo.decode(xhr.responseText);
30283           
30284         Roo.log(response);
30285         
30286     },
30287     
30288     prepare : function(file)
30289     {   
30290         if(this.loadMask){
30291             this.maskEl.mask(this.loadingText);
30292         }
30293         
30294         this.file = false;
30295         this.exif = {};
30296         
30297         if(typeof(file) === 'string'){
30298             this.loadCanvas(file);
30299             return;
30300         }
30301         
30302         if(!file || !this.urlAPI){
30303             return;
30304         }
30305         
30306         this.file = file;
30307         this.cropType = file.type;
30308         
30309         var _this = this;
30310         
30311         if(this.fireEvent('prepare', this, this.file) != false){
30312             
30313             var reader = new FileReader();
30314             
30315             reader.onload = function (e) {
30316                 if (e.target.error) {
30317                     Roo.log(e.target.error);
30318                     return;
30319                 }
30320                 
30321                 var buffer = e.target.result,
30322                     dataView = new DataView(buffer),
30323                     offset = 2,
30324                     maxOffset = dataView.byteLength - 4,
30325                     markerBytes,
30326                     markerLength;
30327                 
30328                 if (dataView.getUint16(0) === 0xffd8) {
30329                     while (offset < maxOffset) {
30330                         markerBytes = dataView.getUint16(offset);
30331                         
30332                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30333                             markerLength = dataView.getUint16(offset + 2) + 2;
30334                             if (offset + markerLength > dataView.byteLength) {
30335                                 Roo.log('Invalid meta data: Invalid segment size.');
30336                                 break;
30337                             }
30338                             
30339                             if(markerBytes == 0xffe1){
30340                                 _this.parseExifData(
30341                                     dataView,
30342                                     offset,
30343                                     markerLength
30344                                 );
30345                             }
30346                             
30347                             offset += markerLength;
30348                             
30349                             continue;
30350                         }
30351                         
30352                         break;
30353                     }
30354                     
30355                 }
30356                 
30357                 var url = _this.urlAPI.createObjectURL(_this.file);
30358                 
30359                 _this.loadCanvas(url);
30360                 
30361                 return;
30362             }
30363             
30364             reader.readAsArrayBuffer(this.file);
30365             
30366         }
30367         
30368     },
30369     
30370     parseExifData : function(dataView, offset, length)
30371     {
30372         var tiffOffset = offset + 10,
30373             littleEndian,
30374             dirOffset;
30375     
30376         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30377             // No Exif data, might be XMP data instead
30378             return;
30379         }
30380         
30381         // Check for the ASCII code for "Exif" (0x45786966):
30382         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30383             // No Exif data, might be XMP data instead
30384             return;
30385         }
30386         if (tiffOffset + 8 > dataView.byteLength) {
30387             Roo.log('Invalid Exif data: Invalid segment size.');
30388             return;
30389         }
30390         // Check for the two null bytes:
30391         if (dataView.getUint16(offset + 8) !== 0x0000) {
30392             Roo.log('Invalid Exif data: Missing byte alignment offset.');
30393             return;
30394         }
30395         // Check the byte alignment:
30396         switch (dataView.getUint16(tiffOffset)) {
30397         case 0x4949:
30398             littleEndian = true;
30399             break;
30400         case 0x4D4D:
30401             littleEndian = false;
30402             break;
30403         default:
30404             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
30405             return;
30406         }
30407         // Check for the TIFF tag marker (0x002A):
30408         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
30409             Roo.log('Invalid Exif data: Missing TIFF marker.');
30410             return;
30411         }
30412         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
30413         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
30414         
30415         this.parseExifTags(
30416             dataView,
30417             tiffOffset,
30418             tiffOffset + dirOffset,
30419             littleEndian
30420         );
30421     },
30422     
30423     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
30424     {
30425         var tagsNumber,
30426             dirEndOffset,
30427             i;
30428         if (dirOffset + 6 > dataView.byteLength) {
30429             Roo.log('Invalid Exif data: Invalid directory offset.');
30430             return;
30431         }
30432         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
30433         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
30434         if (dirEndOffset + 4 > dataView.byteLength) {
30435             Roo.log('Invalid Exif data: Invalid directory size.');
30436             return;
30437         }
30438         for (i = 0; i < tagsNumber; i += 1) {
30439             this.parseExifTag(
30440                 dataView,
30441                 tiffOffset,
30442                 dirOffset + 2 + 12 * i, // tag offset
30443                 littleEndian
30444             );
30445         }
30446         // Return the offset to the next directory:
30447         return dataView.getUint32(dirEndOffset, littleEndian);
30448     },
30449     
30450     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
30451     {
30452         var tag = dataView.getUint16(offset, littleEndian);
30453         
30454         this.exif[tag] = this.getExifValue(
30455             dataView,
30456             tiffOffset,
30457             offset,
30458             dataView.getUint16(offset + 2, littleEndian), // tag type
30459             dataView.getUint32(offset + 4, littleEndian), // tag length
30460             littleEndian
30461         );
30462     },
30463     
30464     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
30465     {
30466         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
30467             tagSize,
30468             dataOffset,
30469             values,
30470             i,
30471             str,
30472             c;
30473     
30474         if (!tagType) {
30475             Roo.log('Invalid Exif data: Invalid tag type.');
30476             return;
30477         }
30478         
30479         tagSize = tagType.size * length;
30480         // Determine if the value is contained in the dataOffset bytes,
30481         // or if the value at the dataOffset is a pointer to the actual data:
30482         dataOffset = tagSize > 4 ?
30483                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
30484         if (dataOffset + tagSize > dataView.byteLength) {
30485             Roo.log('Invalid Exif data: Invalid data offset.');
30486             return;
30487         }
30488         if (length === 1) {
30489             return tagType.getValue(dataView, dataOffset, littleEndian);
30490         }
30491         values = [];
30492         for (i = 0; i < length; i += 1) {
30493             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
30494         }
30495         
30496         if (tagType.ascii) {
30497             str = '';
30498             // Concatenate the chars:
30499             for (i = 0; i < values.length; i += 1) {
30500                 c = values[i];
30501                 // Ignore the terminating NULL byte(s):
30502                 if (c === '\u0000') {
30503                     break;
30504                 }
30505                 str += c;
30506             }
30507             return str;
30508         }
30509         return values;
30510     }
30511     
30512 });
30513
30514 Roo.apply(Roo.bootstrap.UploadCropbox, {
30515     tags : {
30516         'Orientation': 0x0112
30517     },
30518     
30519     Orientation: {
30520             1: 0, //'top-left',
30521 //            2: 'top-right',
30522             3: 180, //'bottom-right',
30523 //            4: 'bottom-left',
30524 //            5: 'left-top',
30525             6: 90, //'right-top',
30526 //            7: 'right-bottom',
30527             8: 270 //'left-bottom'
30528     },
30529     
30530     exifTagTypes : {
30531         // byte, 8-bit unsigned int:
30532         1: {
30533             getValue: function (dataView, dataOffset) {
30534                 return dataView.getUint8(dataOffset);
30535             },
30536             size: 1
30537         },
30538         // ascii, 8-bit byte:
30539         2: {
30540             getValue: function (dataView, dataOffset) {
30541                 return String.fromCharCode(dataView.getUint8(dataOffset));
30542             },
30543             size: 1,
30544             ascii: true
30545         },
30546         // short, 16 bit int:
30547         3: {
30548             getValue: function (dataView, dataOffset, littleEndian) {
30549                 return dataView.getUint16(dataOffset, littleEndian);
30550             },
30551             size: 2
30552         },
30553         // long, 32 bit int:
30554         4: {
30555             getValue: function (dataView, dataOffset, littleEndian) {
30556                 return dataView.getUint32(dataOffset, littleEndian);
30557             },
30558             size: 4
30559         },
30560         // rational = two long values, first is numerator, second is denominator:
30561         5: {
30562             getValue: function (dataView, dataOffset, littleEndian) {
30563                 return dataView.getUint32(dataOffset, littleEndian) /
30564                     dataView.getUint32(dataOffset + 4, littleEndian);
30565             },
30566             size: 8
30567         },
30568         // slong, 32 bit signed int:
30569         9: {
30570             getValue: function (dataView, dataOffset, littleEndian) {
30571                 return dataView.getInt32(dataOffset, littleEndian);
30572             },
30573             size: 4
30574         },
30575         // srational, two slongs, first is numerator, second is denominator:
30576         10: {
30577             getValue: function (dataView, dataOffset, littleEndian) {
30578                 return dataView.getInt32(dataOffset, littleEndian) /
30579                     dataView.getInt32(dataOffset + 4, littleEndian);
30580             },
30581             size: 8
30582         }
30583     },
30584     
30585     footer : {
30586         STANDARD : [
30587             {
30588                 tag : 'div',
30589                 cls : 'btn-group roo-upload-cropbox-rotate-left',
30590                 action : 'rotate-left',
30591                 cn : [
30592                     {
30593                         tag : 'button',
30594                         cls : 'btn btn-default',
30595                         html : '<i class="fa fa-undo"></i>'
30596                     }
30597                 ]
30598             },
30599             {
30600                 tag : 'div',
30601                 cls : 'btn-group roo-upload-cropbox-picture',
30602                 action : 'picture',
30603                 cn : [
30604                     {
30605                         tag : 'button',
30606                         cls : 'btn btn-default',
30607                         html : '<i class="fa fa-picture-o"></i>'
30608                     }
30609                 ]
30610             },
30611             {
30612                 tag : 'div',
30613                 cls : 'btn-group roo-upload-cropbox-rotate-right',
30614                 action : 'rotate-right',
30615                 cn : [
30616                     {
30617                         tag : 'button',
30618                         cls : 'btn btn-default',
30619                         html : '<i class="fa fa-repeat"></i>'
30620                     }
30621                 ]
30622             }
30623         ],
30624         DOCUMENT : [
30625             {
30626                 tag : 'div',
30627                 cls : 'btn-group roo-upload-cropbox-rotate-left',
30628                 action : 'rotate-left',
30629                 cn : [
30630                     {
30631                         tag : 'button',
30632                         cls : 'btn btn-default',
30633                         html : '<i class="fa fa-undo"></i>'
30634                     }
30635                 ]
30636             },
30637             {
30638                 tag : 'div',
30639                 cls : 'btn-group roo-upload-cropbox-download',
30640                 action : 'download',
30641                 cn : [
30642                     {
30643                         tag : 'button',
30644                         cls : 'btn btn-default',
30645                         html : '<i class="fa fa-download"></i>'
30646                     }
30647                 ]
30648             },
30649             {
30650                 tag : 'div',
30651                 cls : 'btn-group roo-upload-cropbox-crop',
30652                 action : 'crop',
30653                 cn : [
30654                     {
30655                         tag : 'button',
30656                         cls : 'btn btn-default',
30657                         html : '<i class="fa fa-crop"></i>'
30658                     }
30659                 ]
30660             },
30661             {
30662                 tag : 'div',
30663                 cls : 'btn-group roo-upload-cropbox-trash',
30664                 action : 'trash',
30665                 cn : [
30666                     {
30667                         tag : 'button',
30668                         cls : 'btn btn-default',
30669                         html : '<i class="fa fa-trash"></i>'
30670                     }
30671                 ]
30672             },
30673             {
30674                 tag : 'div',
30675                 cls : 'btn-group roo-upload-cropbox-rotate-right',
30676                 action : 'rotate-right',
30677                 cn : [
30678                     {
30679                         tag : 'button',
30680                         cls : 'btn btn-default',
30681                         html : '<i class="fa fa-repeat"></i>'
30682                     }
30683                 ]
30684             }
30685         ],
30686         ROTATOR : [
30687             {
30688                 tag : 'div',
30689                 cls : 'btn-group roo-upload-cropbox-rotate-left',
30690                 action : 'rotate-left',
30691                 cn : [
30692                     {
30693                         tag : 'button',
30694                         cls : 'btn btn-default',
30695                         html : '<i class="fa fa-undo"></i>'
30696                     }
30697                 ]
30698             },
30699             {
30700                 tag : 'div',
30701                 cls : 'btn-group roo-upload-cropbox-rotate-right',
30702                 action : 'rotate-right',
30703                 cn : [
30704                     {
30705                         tag : 'button',
30706                         cls : 'btn btn-default',
30707                         html : '<i class="fa fa-repeat"></i>'
30708                     }
30709                 ]
30710             }
30711         ]
30712     }
30713 });
30714
30715 /*
30716 * Licence: LGPL
30717 */
30718
30719 /**
30720  * @class Roo.bootstrap.DocumentManager
30721  * @extends Roo.bootstrap.Component
30722  * Bootstrap DocumentManager class
30723  * @cfg {String} paramName default 'imageUpload'
30724  * @cfg {String} toolTipName default 'filename'
30725  * @cfg {String} method default POST
30726  * @cfg {String} url action url
30727  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
30728  * @cfg {Boolean} multiple multiple upload default true
30729  * @cfg {Number} thumbSize default 300
30730  * @cfg {String} fieldLabel
30731  * @cfg {Number} labelWidth default 4
30732  * @cfg {String} labelAlign (left|top) default left
30733  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
30734 * @cfg {Number} labellg set the width of label (1-12)
30735  * @cfg {Number} labelmd set the width of label (1-12)
30736  * @cfg {Number} labelsm set the width of label (1-12)
30737  * @cfg {Number} labelxs set the width of label (1-12)
30738  * 
30739  * @constructor
30740  * Create a new DocumentManager
30741  * @param {Object} config The config object
30742  */
30743
30744 Roo.bootstrap.DocumentManager = function(config){
30745     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
30746     
30747     this.files = [];
30748     this.delegates = [];
30749     
30750     this.addEvents({
30751         /**
30752          * @event initial
30753          * Fire when initial the DocumentManager
30754          * @param {Roo.bootstrap.DocumentManager} this
30755          */
30756         "initial" : true,
30757         /**
30758          * @event inspect
30759          * inspect selected file
30760          * @param {Roo.bootstrap.DocumentManager} this
30761          * @param {File} file
30762          */
30763         "inspect" : true,
30764         /**
30765          * @event exception
30766          * Fire when xhr load exception
30767          * @param {Roo.bootstrap.DocumentManager} this
30768          * @param {XMLHttpRequest} xhr
30769          */
30770         "exception" : true,
30771         /**
30772          * @event afterupload
30773          * Fire when xhr load exception
30774          * @param {Roo.bootstrap.DocumentManager} this
30775          * @param {XMLHttpRequest} xhr
30776          */
30777         "afterupload" : true,
30778         /**
30779          * @event prepare
30780          * prepare the form data
30781          * @param {Roo.bootstrap.DocumentManager} this
30782          * @param {Object} formData
30783          */
30784         "prepare" : true,
30785         /**
30786          * @event remove
30787          * Fire when remove the file
30788          * @param {Roo.bootstrap.DocumentManager} this
30789          * @param {Object} file
30790          */
30791         "remove" : true,
30792         /**
30793          * @event refresh
30794          * Fire after refresh the file
30795          * @param {Roo.bootstrap.DocumentManager} this
30796          */
30797         "refresh" : true,
30798         /**
30799          * @event click
30800          * Fire after click the image
30801          * @param {Roo.bootstrap.DocumentManager} this
30802          * @param {Object} file
30803          */
30804         "click" : true,
30805         /**
30806          * @event edit
30807          * Fire when upload a image and editable set to true
30808          * @param {Roo.bootstrap.DocumentManager} this
30809          * @param {Object} file
30810          */
30811         "edit" : true,
30812         /**
30813          * @event beforeselectfile
30814          * Fire before select file
30815          * @param {Roo.bootstrap.DocumentManager} this
30816          */
30817         "beforeselectfile" : true,
30818         /**
30819          * @event process
30820          * Fire before process file
30821          * @param {Roo.bootstrap.DocumentManager} this
30822          * @param {Object} file
30823          */
30824         "process" : true,
30825         /**
30826          * @event previewrendered
30827          * Fire when preview rendered
30828          * @param {Roo.bootstrap.DocumentManager} this
30829          * @param {Object} file
30830          */
30831         "previewrendered" : true,
30832         /**
30833          */
30834         "previewResize" : true
30835         
30836     });
30837 };
30838
30839 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
30840     
30841     boxes : 0,
30842     inputName : '',
30843     thumbSize : 300,
30844     multiple : true,
30845     files : false,
30846     method : 'POST',
30847     url : '',
30848     paramName : 'imageUpload',
30849     toolTipName : 'filename',
30850     fieldLabel : '',
30851     labelWidth : 4,
30852     labelAlign : 'left',
30853     editable : true,
30854     delegates : false,
30855     xhr : false, 
30856     
30857     labellg : 0,
30858     labelmd : 0,
30859     labelsm : 0,
30860     labelxs : 0,
30861     
30862     getAutoCreate : function()
30863     {   
30864         var managerWidget = {
30865             tag : 'div',
30866             cls : 'roo-document-manager',
30867             cn : [
30868                 {
30869                     tag : 'input',
30870                     cls : 'roo-document-manager-selector',
30871                     type : 'file'
30872                 },
30873                 {
30874                     tag : 'div',
30875                     cls : 'roo-document-manager-uploader',
30876                     cn : [
30877                         {
30878                             tag : 'div',
30879                             cls : 'roo-document-manager-upload-btn',
30880                             html : '<i class="fa fa-plus"></i>'
30881                         }
30882                     ]
30883                     
30884                 }
30885             ]
30886         };
30887         
30888         var content = [
30889             {
30890                 tag : 'div',
30891                 cls : 'column col-md-12',
30892                 cn : managerWidget
30893             }
30894         ];
30895         
30896         if(this.fieldLabel.length){
30897             
30898             content = [
30899                 {
30900                     tag : 'div',
30901                     cls : 'column col-md-12',
30902                     html : this.fieldLabel
30903                 },
30904                 {
30905                     tag : 'div',
30906                     cls : 'column col-md-12',
30907                     cn : managerWidget
30908                 }
30909             ];
30910
30911             if(this.labelAlign == 'left'){
30912                 content = [
30913                     {
30914                         tag : 'div',
30915                         cls : 'column',
30916                         html : this.fieldLabel
30917                     },
30918                     {
30919                         tag : 'div',
30920                         cls : 'column',
30921                         cn : managerWidget
30922                     }
30923                 ];
30924                 
30925                 if(this.labelWidth > 12){
30926                     content[0].style = "width: " + this.labelWidth + 'px';
30927                 }
30928
30929                 if(this.labelWidth < 13 && this.labelmd == 0){
30930                     this.labelmd = this.labelWidth;
30931                 }
30932
30933                 if(this.labellg > 0){
30934                     content[0].cls += ' col-lg-' + this.labellg;
30935                     content[1].cls += ' col-lg-' + (12 - this.labellg);
30936                 }
30937
30938                 if(this.labelmd > 0){
30939                     content[0].cls += ' col-md-' + this.labelmd;
30940                     content[1].cls += ' col-md-' + (12 - this.labelmd);
30941                 }
30942
30943                 if(this.labelsm > 0){
30944                     content[0].cls += ' col-sm-' + this.labelsm;
30945                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
30946                 }
30947
30948                 if(this.labelxs > 0){
30949                     content[0].cls += ' col-xs-' + this.labelxs;
30950                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
30951                 }
30952                 
30953             }
30954         }
30955         
30956         var cfg = {
30957             tag : 'div',
30958             cls : 'row clearfix',
30959             cn : content
30960         };
30961         
30962         return cfg;
30963         
30964     },
30965     
30966     initEvents : function()
30967     {
30968         this.managerEl = this.el.select('.roo-document-manager', true).first();
30969         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30970         
30971         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
30972         this.selectorEl.hide();
30973         
30974         if(this.multiple){
30975             this.selectorEl.attr('multiple', 'multiple');
30976         }
30977         
30978         this.selectorEl.on('change', this.onFileSelected, this);
30979         
30980         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
30981         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30982         
30983         this.uploader.on('click', this.onUploaderClick, this);
30984         
30985         this.renderProgressDialog();
30986         
30987         var _this = this;
30988         
30989         window.addEventListener("resize", function() { _this.refresh(); } );
30990         
30991         this.fireEvent('initial', this);
30992     },
30993     
30994     renderProgressDialog : function()
30995     {
30996         var _this = this;
30997         
30998         this.progressDialog = new Roo.bootstrap.Modal({
30999             cls : 'roo-document-manager-progress-dialog',
31000             allow_close : false,
31001             animate : false,
31002             title : '',
31003             buttons : [
31004                 {
31005                     name  :'cancel',
31006                     weight : 'danger',
31007                     html : 'Cancel'
31008                 }
31009             ], 
31010             listeners : { 
31011                 btnclick : function() {
31012                     _this.uploadCancel();
31013                     this.hide();
31014                 }
31015             }
31016         });
31017          
31018         this.progressDialog.render(Roo.get(document.body));
31019          
31020         this.progress = new Roo.bootstrap.Progress({
31021             cls : 'roo-document-manager-progress',
31022             active : true,
31023             striped : true
31024         });
31025         
31026         this.progress.render(this.progressDialog.getChildContainer());
31027         
31028         this.progressBar = new Roo.bootstrap.ProgressBar({
31029             cls : 'roo-document-manager-progress-bar',
31030             aria_valuenow : 0,
31031             aria_valuemin : 0,
31032             aria_valuemax : 12,
31033             panel : 'success'
31034         });
31035         
31036         this.progressBar.render(this.progress.getChildContainer());
31037     },
31038     
31039     onUploaderClick : function(e)
31040     {
31041         e.preventDefault();
31042      
31043         if(this.fireEvent('beforeselectfile', this) != false){
31044             this.selectorEl.dom.click();
31045         }
31046         
31047     },
31048     
31049     onFileSelected : function(e)
31050     {
31051         e.preventDefault();
31052         
31053         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31054             return;
31055         }
31056         
31057         Roo.each(this.selectorEl.dom.files, function(file){
31058             if(this.fireEvent('inspect', this, file) != false){
31059                 this.files.push(file);
31060             }
31061         }, this);
31062         
31063         this.queue();
31064         
31065     },
31066     
31067     queue : function()
31068     {
31069         this.selectorEl.dom.value = '';
31070         
31071         if(!this.files || !this.files.length){
31072             return;
31073         }
31074         
31075         if(this.boxes > 0 && this.files.length > this.boxes){
31076             this.files = this.files.slice(0, this.boxes);
31077         }
31078         
31079         this.uploader.show();
31080         
31081         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31082             this.uploader.hide();
31083         }
31084         
31085         var _this = this;
31086         
31087         var files = [];
31088         
31089         var docs = [];
31090         
31091         Roo.each(this.files, function(file){
31092             
31093             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31094                 var f = this.renderPreview(file);
31095                 files.push(f);
31096                 return;
31097             }
31098             
31099             if(file.type.indexOf('image') != -1){
31100                 this.delegates.push(
31101                     (function(){
31102                         _this.process(file);
31103                     }).createDelegate(this)
31104                 );
31105         
31106                 return;
31107             }
31108             
31109             docs.push(
31110                 (function(){
31111                     _this.process(file);
31112                 }).createDelegate(this)
31113             );
31114             
31115         }, this);
31116         
31117         this.files = files;
31118         
31119         this.delegates = this.delegates.concat(docs);
31120         
31121         if(!this.delegates.length){
31122             this.refresh();
31123             return;
31124         }
31125         
31126         this.progressBar.aria_valuemax = this.delegates.length;
31127         
31128         this.arrange();
31129         
31130         return;
31131     },
31132     
31133     arrange : function()
31134     {
31135         if(!this.delegates.length){
31136             this.progressDialog.hide();
31137             this.refresh();
31138             return;
31139         }
31140         
31141         var delegate = this.delegates.shift();
31142         
31143         this.progressDialog.show();
31144         
31145         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31146         
31147         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31148         
31149         delegate();
31150     },
31151     
31152     refresh : function()
31153     {
31154         this.uploader.show();
31155         
31156         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31157             this.uploader.hide();
31158         }
31159         
31160         Roo.isTouch ? this.closable(false) : this.closable(true);
31161         
31162         this.fireEvent('refresh', this);
31163     },
31164     
31165     onRemove : function(e, el, o)
31166     {
31167         e.preventDefault();
31168         
31169         this.fireEvent('remove', this, o);
31170         
31171     },
31172     
31173     remove : function(o)
31174     {
31175         var files = [];
31176         
31177         Roo.each(this.files, function(file){
31178             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31179                 files.push(file);
31180                 return;
31181             }
31182
31183             o.target.remove();
31184
31185         }, this);
31186         
31187         this.files = files;
31188         
31189         this.refresh();
31190     },
31191     
31192     clear : function()
31193     {
31194         Roo.each(this.files, function(file){
31195             if(!file.target){
31196                 return;
31197             }
31198             
31199             file.target.remove();
31200
31201         }, this);
31202         
31203         this.files = [];
31204         
31205         this.refresh();
31206     },
31207     
31208     onClick : function(e, el, o)
31209     {
31210         e.preventDefault();
31211         
31212         this.fireEvent('click', this, o);
31213         
31214     },
31215     
31216     closable : function(closable)
31217     {
31218         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31219             
31220             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31221             
31222             if(closable){
31223                 el.show();
31224                 return;
31225             }
31226             
31227             el.hide();
31228             
31229         }, this);
31230     },
31231     
31232     xhrOnLoad : function(xhr)
31233     {
31234         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31235             el.remove();
31236         }, this);
31237         
31238         if (xhr.readyState !== 4) {
31239             this.arrange();
31240             this.fireEvent('exception', this, xhr);
31241             return;
31242         }
31243
31244         var response = Roo.decode(xhr.responseText);
31245         
31246         if(!response.success){
31247             this.arrange();
31248             this.fireEvent('exception', this, xhr);
31249             return;
31250         }
31251         
31252         var file = this.renderPreview(response.data);
31253         
31254         this.files.push(file);
31255         
31256         this.arrange();
31257         
31258         this.fireEvent('afterupload', this, xhr);
31259         
31260     },
31261     
31262     xhrOnError : function(xhr)
31263     {
31264         Roo.log('xhr on error');
31265         
31266         var response = Roo.decode(xhr.responseText);
31267           
31268         Roo.log(response);
31269         
31270         this.arrange();
31271     },
31272     
31273     process : function(file)
31274     {
31275         if(this.fireEvent('process', this, file) !== false){
31276             if(this.editable && file.type.indexOf('image') != -1){
31277                 this.fireEvent('edit', this, file);
31278                 return;
31279             }
31280
31281             this.uploadStart(file, false);
31282
31283             return;
31284         }
31285         
31286     },
31287     
31288     uploadStart : function(file, crop)
31289     {
31290         this.xhr = new XMLHttpRequest();
31291         
31292         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31293             this.arrange();
31294             return;
31295         }
31296         
31297         file.xhr = this.xhr;
31298             
31299         this.managerEl.createChild({
31300             tag : 'div',
31301             cls : 'roo-document-manager-loading',
31302             cn : [
31303                 {
31304                     tag : 'div',
31305                     tooltip : file.name,
31306                     cls : 'roo-document-manager-thumb',
31307                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31308                 }
31309             ]
31310
31311         });
31312
31313         this.xhr.open(this.method, this.url, true);
31314         
31315         var headers = {
31316             "Accept": "application/json",
31317             "Cache-Control": "no-cache",
31318             "X-Requested-With": "XMLHttpRequest"
31319         };
31320         
31321         for (var headerName in headers) {
31322             var headerValue = headers[headerName];
31323             if (headerValue) {
31324                 this.xhr.setRequestHeader(headerName, headerValue);
31325             }
31326         }
31327         
31328         var _this = this;
31329         
31330         this.xhr.onload = function()
31331         {
31332             _this.xhrOnLoad(_this.xhr);
31333         }
31334         
31335         this.xhr.onerror = function()
31336         {
31337             _this.xhrOnError(_this.xhr);
31338         }
31339         
31340         var formData = new FormData();
31341
31342         formData.append('returnHTML', 'NO');
31343         
31344         if(crop){
31345             formData.append('crop', crop);
31346         }
31347         
31348         formData.append(this.paramName, file, file.name);
31349         
31350         var options = {
31351             file : file, 
31352             manually : false
31353         };
31354         
31355         if(this.fireEvent('prepare', this, formData, options) != false){
31356             
31357             if(options.manually){
31358                 return;
31359             }
31360             
31361             this.xhr.send(formData);
31362             return;
31363         };
31364         
31365         this.uploadCancel();
31366     },
31367     
31368     uploadCancel : function()
31369     {
31370         if (this.xhr) {
31371             this.xhr.abort();
31372         }
31373         
31374         this.delegates = [];
31375         
31376         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31377             el.remove();
31378         }, this);
31379         
31380         this.arrange();
31381     },
31382     
31383     renderPreview : function(file)
31384     {
31385         if(typeof(file.target) != 'undefined' && file.target){
31386             return file;
31387         }
31388         
31389         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
31390         
31391         var previewEl = this.managerEl.createChild({
31392             tag : 'div',
31393             cls : 'roo-document-manager-preview',
31394             cn : [
31395                 {
31396                     tag : 'div',
31397                     tooltip : file[this.toolTipName],
31398                     cls : 'roo-document-manager-thumb',
31399                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
31400                 },
31401                 {
31402                     tag : 'button',
31403                     cls : 'close',
31404                     html : '<i class="fa fa-times-circle"></i>'
31405                 }
31406             ]
31407         });
31408
31409         var close = previewEl.select('button.close', true).first();
31410
31411         close.on('click', this.onRemove, this, file);
31412
31413         file.target = previewEl;
31414
31415         var image = previewEl.select('img', true).first();
31416         
31417         var _this = this;
31418         
31419         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
31420         
31421         image.on('click', this.onClick, this, file);
31422         
31423         this.fireEvent('previewrendered', this, file);
31424         
31425         return file;
31426         
31427     },
31428     
31429     onPreviewLoad : function(file, image)
31430     {
31431         if(typeof(file.target) == 'undefined' || !file.target){
31432             return;
31433         }
31434         
31435         var width = image.dom.naturalWidth || image.dom.width;
31436         var height = image.dom.naturalHeight || image.dom.height;
31437         
31438         if(!this.previewResize) {
31439             return;
31440         }
31441         
31442         if(width > height){
31443             file.target.addClass('wide');
31444             return;
31445         }
31446         
31447         file.target.addClass('tall');
31448         return;
31449         
31450     },
31451     
31452     uploadFromSource : function(file, crop)
31453     {
31454         this.xhr = new XMLHttpRequest();
31455         
31456         this.managerEl.createChild({
31457             tag : 'div',
31458             cls : 'roo-document-manager-loading',
31459             cn : [
31460                 {
31461                     tag : 'div',
31462                     tooltip : file.name,
31463                     cls : 'roo-document-manager-thumb',
31464                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31465                 }
31466             ]
31467
31468         });
31469
31470         this.xhr.open(this.method, this.url, true);
31471         
31472         var headers = {
31473             "Accept": "application/json",
31474             "Cache-Control": "no-cache",
31475             "X-Requested-With": "XMLHttpRequest"
31476         };
31477         
31478         for (var headerName in headers) {
31479             var headerValue = headers[headerName];
31480             if (headerValue) {
31481                 this.xhr.setRequestHeader(headerName, headerValue);
31482             }
31483         }
31484         
31485         var _this = this;
31486         
31487         this.xhr.onload = function()
31488         {
31489             _this.xhrOnLoad(_this.xhr);
31490         }
31491         
31492         this.xhr.onerror = function()
31493         {
31494             _this.xhrOnError(_this.xhr);
31495         }
31496         
31497         var formData = new FormData();
31498
31499         formData.append('returnHTML', 'NO');
31500         
31501         formData.append('crop', crop);
31502         
31503         if(typeof(file.filename) != 'undefined'){
31504             formData.append('filename', file.filename);
31505         }
31506         
31507         if(typeof(file.mimetype) != 'undefined'){
31508             formData.append('mimetype', file.mimetype);
31509         }
31510         
31511         Roo.log(formData);
31512         
31513         if(this.fireEvent('prepare', this, formData) != false){
31514             this.xhr.send(formData);
31515         };
31516     }
31517 });
31518
31519 /*
31520 * Licence: LGPL
31521 */
31522
31523 /**
31524  * @class Roo.bootstrap.DocumentViewer
31525  * @extends Roo.bootstrap.Component
31526  * Bootstrap DocumentViewer class
31527  * @cfg {Boolean} showDownload (true|false) show download button (default true)
31528  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
31529  * 
31530  * @constructor
31531  * Create a new DocumentViewer
31532  * @param {Object} config The config object
31533  */
31534
31535 Roo.bootstrap.DocumentViewer = function(config){
31536     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
31537     
31538     this.addEvents({
31539         /**
31540          * @event initial
31541          * Fire after initEvent
31542          * @param {Roo.bootstrap.DocumentViewer} this
31543          */
31544         "initial" : true,
31545         /**
31546          * @event click
31547          * Fire after click
31548          * @param {Roo.bootstrap.DocumentViewer} this
31549          */
31550         "click" : true,
31551         /**
31552          * @event download
31553          * Fire after download button
31554          * @param {Roo.bootstrap.DocumentViewer} this
31555          */
31556         "download" : true,
31557         /**
31558          * @event trash
31559          * Fire after trash button
31560          * @param {Roo.bootstrap.DocumentViewer} this
31561          */
31562         "trash" : true
31563         
31564     });
31565 };
31566
31567 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
31568     
31569     showDownload : true,
31570     
31571     showTrash : true,
31572     
31573     getAutoCreate : function()
31574     {
31575         var cfg = {
31576             tag : 'div',
31577             cls : 'roo-document-viewer',
31578             cn : [
31579                 {
31580                     tag : 'div',
31581                     cls : 'roo-document-viewer-body',
31582                     cn : [
31583                         {
31584                             tag : 'div',
31585                             cls : 'roo-document-viewer-thumb',
31586                             cn : [
31587                                 {
31588                                     tag : 'img',
31589                                     cls : 'roo-document-viewer-image'
31590                                 }
31591                             ]
31592                         }
31593                     ]
31594                 },
31595                 {
31596                     tag : 'div',
31597                     cls : 'roo-document-viewer-footer',
31598                     cn : {
31599                         tag : 'div',
31600                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
31601                         cn : [
31602                             {
31603                                 tag : 'div',
31604                                 cls : 'btn-group roo-document-viewer-download',
31605                                 cn : [
31606                                     {
31607                                         tag : 'button',
31608                                         cls : 'btn btn-default',
31609                                         html : '<i class="fa fa-download"></i>'
31610                                     }
31611                                 ]
31612                             },
31613                             {
31614                                 tag : 'div',
31615                                 cls : 'btn-group roo-document-viewer-trash',
31616                                 cn : [
31617                                     {
31618                                         tag : 'button',
31619                                         cls : 'btn btn-default',
31620                                         html : '<i class="fa fa-trash"></i>'
31621                                     }
31622                                 ]
31623                             }
31624                         ]
31625                     }
31626                 }
31627             ]
31628         };
31629         
31630         return cfg;
31631     },
31632     
31633     initEvents : function()
31634     {
31635         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
31636         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
31637         
31638         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
31639         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
31640         
31641         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
31642         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
31643         
31644         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
31645         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
31646         
31647         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
31648         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
31649         
31650         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
31651         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
31652         
31653         this.bodyEl.on('click', this.onClick, this);
31654         this.downloadBtn.on('click', this.onDownload, this);
31655         this.trashBtn.on('click', this.onTrash, this);
31656         
31657         this.downloadBtn.hide();
31658         this.trashBtn.hide();
31659         
31660         if(this.showDownload){
31661             this.downloadBtn.show();
31662         }
31663         
31664         if(this.showTrash){
31665             this.trashBtn.show();
31666         }
31667         
31668         if(!this.showDownload && !this.showTrash) {
31669             this.footerEl.hide();
31670         }
31671         
31672     },
31673     
31674     initial : function()
31675     {
31676         this.fireEvent('initial', this);
31677         
31678     },
31679     
31680     onClick : function(e)
31681     {
31682         e.preventDefault();
31683         
31684         this.fireEvent('click', this);
31685     },
31686     
31687     onDownload : function(e)
31688     {
31689         e.preventDefault();
31690         
31691         this.fireEvent('download', this);
31692     },
31693     
31694     onTrash : function(e)
31695     {
31696         e.preventDefault();
31697         
31698         this.fireEvent('trash', this);
31699     }
31700     
31701 });
31702 /*
31703  * - LGPL
31704  *
31705  * nav progress bar
31706  * 
31707  */
31708
31709 /**
31710  * @class Roo.bootstrap.NavProgressBar
31711  * @extends Roo.bootstrap.Component
31712  * Bootstrap NavProgressBar class
31713  * 
31714  * @constructor
31715  * Create a new nav progress bar
31716  * @param {Object} config The config object
31717  */
31718
31719 Roo.bootstrap.NavProgressBar = function(config){
31720     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
31721
31722     this.bullets = this.bullets || [];
31723    
31724 //    Roo.bootstrap.NavProgressBar.register(this);
31725      this.addEvents({
31726         /**
31727              * @event changed
31728              * Fires when the active item changes
31729              * @param {Roo.bootstrap.NavProgressBar} this
31730              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
31731              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
31732          */
31733         'changed': true
31734      });
31735     
31736 };
31737
31738 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
31739     
31740     bullets : [],
31741     barItems : [],
31742     
31743     getAutoCreate : function()
31744     {
31745         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
31746         
31747         cfg = {
31748             tag : 'div',
31749             cls : 'roo-navigation-bar-group',
31750             cn : [
31751                 {
31752                     tag : 'div',
31753                     cls : 'roo-navigation-top-bar'
31754                 },
31755                 {
31756                     tag : 'div',
31757                     cls : 'roo-navigation-bullets-bar',
31758                     cn : [
31759                         {
31760                             tag : 'ul',
31761                             cls : 'roo-navigation-bar'
31762                         }
31763                     ]
31764                 },
31765                 
31766                 {
31767                     tag : 'div',
31768                     cls : 'roo-navigation-bottom-bar'
31769                 }
31770             ]
31771             
31772         };
31773         
31774         return cfg;
31775         
31776     },
31777     
31778     initEvents: function() 
31779     {
31780         
31781     },
31782     
31783     onRender : function(ct, position) 
31784     {
31785         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
31786         
31787         if(this.bullets.length){
31788             Roo.each(this.bullets, function(b){
31789                this.addItem(b);
31790             }, this);
31791         }
31792         
31793         this.format();
31794         
31795     },
31796     
31797     addItem : function(cfg)
31798     {
31799         var item = new Roo.bootstrap.NavProgressItem(cfg);
31800         
31801         item.parentId = this.id;
31802         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
31803         
31804         if(cfg.html){
31805             var top = new Roo.bootstrap.Element({
31806                 tag : 'div',
31807                 cls : 'roo-navigation-bar-text'
31808             });
31809             
31810             var bottom = new Roo.bootstrap.Element({
31811                 tag : 'div',
31812                 cls : 'roo-navigation-bar-text'
31813             });
31814             
31815             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
31816             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
31817             
31818             var topText = new Roo.bootstrap.Element({
31819                 tag : 'span',
31820                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
31821             });
31822             
31823             var bottomText = new Roo.bootstrap.Element({
31824                 tag : 'span',
31825                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
31826             });
31827             
31828             topText.onRender(top.el, null);
31829             bottomText.onRender(bottom.el, null);
31830             
31831             item.topEl = top;
31832             item.bottomEl = bottom;
31833         }
31834         
31835         this.barItems.push(item);
31836         
31837         return item;
31838     },
31839     
31840     getActive : function()
31841     {
31842         var active = false;
31843         
31844         Roo.each(this.barItems, function(v){
31845             
31846             if (!v.isActive()) {
31847                 return;
31848             }
31849             
31850             active = v;
31851             return false;
31852             
31853         });
31854         
31855         return active;
31856     },
31857     
31858     setActiveItem : function(item)
31859     {
31860         var prev = false;
31861         
31862         Roo.each(this.barItems, function(v){
31863             if (v.rid == item.rid) {
31864                 return ;
31865             }
31866             
31867             if (v.isActive()) {
31868                 v.setActive(false);
31869                 prev = v;
31870             }
31871         });
31872
31873         item.setActive(true);
31874         
31875         this.fireEvent('changed', this, item, prev);
31876     },
31877     
31878     getBarItem: function(rid)
31879     {
31880         var ret = false;
31881         
31882         Roo.each(this.barItems, function(e) {
31883             if (e.rid != rid) {
31884                 return;
31885             }
31886             
31887             ret =  e;
31888             return false;
31889         });
31890         
31891         return ret;
31892     },
31893     
31894     indexOfItem : function(item)
31895     {
31896         var index = false;
31897         
31898         Roo.each(this.barItems, function(v, i){
31899             
31900             if (v.rid != item.rid) {
31901                 return;
31902             }
31903             
31904             index = i;
31905             return false
31906         });
31907         
31908         return index;
31909     },
31910     
31911     setActiveNext : function()
31912     {
31913         var i = this.indexOfItem(this.getActive());
31914         
31915         if (i > this.barItems.length) {
31916             return;
31917         }
31918         
31919         this.setActiveItem(this.barItems[i+1]);
31920     },
31921     
31922     setActivePrev : function()
31923     {
31924         var i = this.indexOfItem(this.getActive());
31925         
31926         if (i  < 1) {
31927             return;
31928         }
31929         
31930         this.setActiveItem(this.barItems[i-1]);
31931     },
31932     
31933     format : function()
31934     {
31935         if(!this.barItems.length){
31936             return;
31937         }
31938      
31939         var width = 100 / this.barItems.length;
31940         
31941         Roo.each(this.barItems, function(i){
31942             i.el.setStyle('width', width + '%');
31943             i.topEl.el.setStyle('width', width + '%');
31944             i.bottomEl.el.setStyle('width', width + '%');
31945         }, this);
31946         
31947     }
31948     
31949 });
31950 /*
31951  * - LGPL
31952  *
31953  * Nav Progress Item
31954  * 
31955  */
31956
31957 /**
31958  * @class Roo.bootstrap.NavProgressItem
31959  * @extends Roo.bootstrap.Component
31960  * Bootstrap NavProgressItem class
31961  * @cfg {String} rid the reference id
31962  * @cfg {Boolean} active (true|false) Is item active default false
31963  * @cfg {Boolean} disabled (true|false) Is item active default false
31964  * @cfg {String} html
31965  * @cfg {String} position (top|bottom) text position default bottom
31966  * @cfg {String} icon show icon instead of number
31967  * 
31968  * @constructor
31969  * Create a new NavProgressItem
31970  * @param {Object} config The config object
31971  */
31972 Roo.bootstrap.NavProgressItem = function(config){
31973     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
31974     this.addEvents({
31975         // raw events
31976         /**
31977          * @event click
31978          * The raw click event for the entire grid.
31979          * @param {Roo.bootstrap.NavProgressItem} this
31980          * @param {Roo.EventObject} e
31981          */
31982         "click" : true
31983     });
31984    
31985 };
31986
31987 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
31988     
31989     rid : '',
31990     active : false,
31991     disabled : false,
31992     html : '',
31993     position : 'bottom',
31994     icon : false,
31995     
31996     getAutoCreate : function()
31997     {
31998         var iconCls = 'roo-navigation-bar-item-icon';
31999         
32000         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32001         
32002         var cfg = {
32003             tag: 'li',
32004             cls: 'roo-navigation-bar-item',
32005             cn : [
32006                 {
32007                     tag : 'i',
32008                     cls : iconCls
32009                 }
32010             ]
32011         };
32012         
32013         if(this.active){
32014             cfg.cls += ' active';
32015         }
32016         if(this.disabled){
32017             cfg.cls += ' disabled';
32018         }
32019         
32020         return cfg;
32021     },
32022     
32023     disable : function()
32024     {
32025         this.setDisabled(true);
32026     },
32027     
32028     enable : function()
32029     {
32030         this.setDisabled(false);
32031     },
32032     
32033     initEvents: function() 
32034     {
32035         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32036         
32037         this.iconEl.on('click', this.onClick, this);
32038     },
32039     
32040     onClick : function(e)
32041     {
32042         e.preventDefault();
32043         
32044         if(this.disabled){
32045             return;
32046         }
32047         
32048         if(this.fireEvent('click', this, e) === false){
32049             return;
32050         };
32051         
32052         this.parent().setActiveItem(this);
32053     },
32054     
32055     isActive: function () 
32056     {
32057         return this.active;
32058     },
32059     
32060     setActive : function(state)
32061     {
32062         if(this.active == state){
32063             return;
32064         }
32065         
32066         this.active = state;
32067         
32068         if (state) {
32069             this.el.addClass('active');
32070             return;
32071         }
32072         
32073         this.el.removeClass('active');
32074         
32075         return;
32076     },
32077     
32078     setDisabled : function(state)
32079     {
32080         if(this.disabled == state){
32081             return;
32082         }
32083         
32084         this.disabled = state;
32085         
32086         if (state) {
32087             this.el.addClass('disabled');
32088             return;
32089         }
32090         
32091         this.el.removeClass('disabled');
32092     },
32093     
32094     tooltipEl : function()
32095     {
32096         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32097     }
32098 });
32099  
32100
32101  /*
32102  * - LGPL
32103  *
32104  * FieldLabel
32105  * 
32106  */
32107
32108 /**
32109  * @class Roo.bootstrap.FieldLabel
32110  * @extends Roo.bootstrap.Component
32111  * Bootstrap FieldLabel class
32112  * @cfg {String} html contents of the element
32113  * @cfg {String} tag tag of the element default label
32114  * @cfg {String} cls class of the element
32115  * @cfg {String} target label target 
32116  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32117  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32118  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32119  * @cfg {String} iconTooltip default "This field is required"
32120  * @cfg {String} indicatorpos (left|right) default left
32121  * 
32122  * @constructor
32123  * Create a new FieldLabel
32124  * @param {Object} config The config object
32125  */
32126
32127 Roo.bootstrap.FieldLabel = function(config){
32128     Roo.bootstrap.Element.superclass.constructor.call(this, config);
32129     
32130     this.addEvents({
32131             /**
32132              * @event invalid
32133              * Fires after the field has been marked as invalid.
32134              * @param {Roo.form.FieldLabel} this
32135              * @param {String} msg The validation message
32136              */
32137             invalid : true,
32138             /**
32139              * @event valid
32140              * Fires after the field has been validated with no errors.
32141              * @param {Roo.form.FieldLabel} this
32142              */
32143             valid : true
32144         });
32145 };
32146
32147 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
32148     
32149     tag: 'label',
32150     cls: '',
32151     html: '',
32152     target: '',
32153     allowBlank : true,
32154     invalidClass : 'has-warning',
32155     validClass : 'has-success',
32156     iconTooltip : 'This field is required',
32157     indicatorpos : 'left',
32158     
32159     getAutoCreate : function(){
32160         
32161         var cls = "";
32162         if (!this.allowBlank) {
32163             cls  = "visible";
32164         }
32165         
32166         var cfg = {
32167             tag : this.tag,
32168             cls : 'roo-bootstrap-field-label ' + this.cls,
32169             for : this.target,
32170             cn : [
32171                 {
32172                     tag : 'i',
32173                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32174                     tooltip : this.iconTooltip
32175                 },
32176                 {
32177                     tag : 'span',
32178                     html : this.html
32179                 }
32180             ] 
32181         };
32182         
32183         if(this.indicatorpos == 'right'){
32184             var cfg = {
32185                 tag : this.tag,
32186                 cls : 'roo-bootstrap-field-label ' + this.cls,
32187                 for : this.target,
32188                 cn : [
32189                     {
32190                         tag : 'span',
32191                         html : this.html
32192                     },
32193                     {
32194                         tag : 'i',
32195                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32196                         tooltip : this.iconTooltip
32197                     }
32198                 ] 
32199             };
32200         }
32201         
32202         return cfg;
32203     },
32204     
32205     initEvents: function() 
32206     {
32207         Roo.bootstrap.Element.superclass.initEvents.call(this);
32208         
32209         this.indicator = this.indicatorEl();
32210         
32211         if(this.indicator){
32212             this.indicator.removeClass('visible');
32213             this.indicator.addClass('invisible');
32214         }
32215         
32216         Roo.bootstrap.FieldLabel.register(this);
32217     },
32218     
32219     indicatorEl : function()
32220     {
32221         var indicator = this.el.select('i.roo-required-indicator',true).first();
32222         
32223         if(!indicator){
32224             return false;
32225         }
32226         
32227         return indicator;
32228         
32229     },
32230     
32231     /**
32232      * Mark this field as valid
32233      */
32234     markValid : function()
32235     {
32236         if(this.indicator){
32237             this.indicator.removeClass('visible');
32238             this.indicator.addClass('invisible');
32239         }
32240         if (Roo.bootstrap.version == 3) {
32241             this.el.removeClass(this.invalidClass);
32242             this.el.addClass(this.validClass);
32243         } else {
32244             this.el.removeClass('is-invalid');
32245             this.el.addClass('is-valid');
32246         }
32247         
32248         
32249         this.fireEvent('valid', this);
32250     },
32251     
32252     /**
32253      * Mark this field as invalid
32254      * @param {String} msg The validation message
32255      */
32256     markInvalid : function(msg)
32257     {
32258         if(this.indicator){
32259             this.indicator.removeClass('invisible');
32260             this.indicator.addClass('visible');
32261         }
32262           if (Roo.bootstrap.version == 3) {
32263             this.el.removeClass(this.validClass);
32264             this.el.addClass(this.invalidClass);
32265         } else {
32266             this.el.removeClass('is-valid');
32267             this.el.addClass('is-invalid');
32268         }
32269         
32270         
32271         this.fireEvent('invalid', this, msg);
32272     }
32273     
32274    
32275 });
32276
32277 Roo.apply(Roo.bootstrap.FieldLabel, {
32278     
32279     groups: {},
32280     
32281      /**
32282     * register a FieldLabel Group
32283     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32284     */
32285     register : function(label)
32286     {
32287         if(this.groups.hasOwnProperty(label.target)){
32288             return;
32289         }
32290      
32291         this.groups[label.target] = label;
32292         
32293     },
32294     /**
32295     * fetch a FieldLabel Group based on the target
32296     * @param {string} target
32297     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32298     */
32299     get: function(target) {
32300         if (typeof(this.groups[target]) == 'undefined') {
32301             return false;
32302         }
32303         
32304         return this.groups[target] ;
32305     }
32306 });
32307
32308  
32309
32310  /*
32311  * - LGPL
32312  *
32313  * page DateSplitField.
32314  * 
32315  */
32316
32317
32318 /**
32319  * @class Roo.bootstrap.DateSplitField
32320  * @extends Roo.bootstrap.Component
32321  * Bootstrap DateSplitField class
32322  * @cfg {string} fieldLabel - the label associated
32323  * @cfg {Number} labelWidth set the width of label (0-12)
32324  * @cfg {String} labelAlign (top|left)
32325  * @cfg {Boolean} dayAllowBlank (true|false) default false
32326  * @cfg {Boolean} monthAllowBlank (true|false) default false
32327  * @cfg {Boolean} yearAllowBlank (true|false) default false
32328  * @cfg {string} dayPlaceholder 
32329  * @cfg {string} monthPlaceholder
32330  * @cfg {string} yearPlaceholder
32331  * @cfg {string} dayFormat default 'd'
32332  * @cfg {string} monthFormat default 'm'
32333  * @cfg {string} yearFormat default 'Y'
32334  * @cfg {Number} labellg set the width of label (1-12)
32335  * @cfg {Number} labelmd set the width of label (1-12)
32336  * @cfg {Number} labelsm set the width of label (1-12)
32337  * @cfg {Number} labelxs set the width of label (1-12)
32338
32339  *     
32340  * @constructor
32341  * Create a new DateSplitField
32342  * @param {Object} config The config object
32343  */
32344
32345 Roo.bootstrap.DateSplitField = function(config){
32346     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32347     
32348     this.addEvents({
32349         // raw events
32350          /**
32351          * @event years
32352          * getting the data of years
32353          * @param {Roo.bootstrap.DateSplitField} this
32354          * @param {Object} years
32355          */
32356         "years" : true,
32357         /**
32358          * @event days
32359          * getting the data of days
32360          * @param {Roo.bootstrap.DateSplitField} this
32361          * @param {Object} days
32362          */
32363         "days" : true,
32364         /**
32365          * @event invalid
32366          * Fires after the field has been marked as invalid.
32367          * @param {Roo.form.Field} this
32368          * @param {String} msg The validation message
32369          */
32370         invalid : true,
32371        /**
32372          * @event valid
32373          * Fires after the field has been validated with no errors.
32374          * @param {Roo.form.Field} this
32375          */
32376         valid : true
32377     });
32378 };
32379
32380 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
32381     
32382     fieldLabel : '',
32383     labelAlign : 'top',
32384     labelWidth : 3,
32385     dayAllowBlank : false,
32386     monthAllowBlank : false,
32387     yearAllowBlank : false,
32388     dayPlaceholder : '',
32389     monthPlaceholder : '',
32390     yearPlaceholder : '',
32391     dayFormat : 'd',
32392     monthFormat : 'm',
32393     yearFormat : 'Y',
32394     isFormField : true,
32395     labellg : 0,
32396     labelmd : 0,
32397     labelsm : 0,
32398     labelxs : 0,
32399     
32400     getAutoCreate : function()
32401     {
32402         var cfg = {
32403             tag : 'div',
32404             cls : 'row roo-date-split-field-group',
32405             cn : [
32406                 {
32407                     tag : 'input',
32408                     type : 'hidden',
32409                     cls : 'form-hidden-field roo-date-split-field-group-value',
32410                     name : this.name
32411                 }
32412             ]
32413         };
32414         
32415         var labelCls = 'col-md-12';
32416         var contentCls = 'col-md-4';
32417         
32418         if(this.fieldLabel){
32419             
32420             var label = {
32421                 tag : 'div',
32422                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
32423                 cn : [
32424                     {
32425                         tag : 'label',
32426                         html : this.fieldLabel
32427                     }
32428                 ]
32429             };
32430             
32431             if(this.labelAlign == 'left'){
32432             
32433                 if(this.labelWidth > 12){
32434                     label.style = "width: " + this.labelWidth + 'px';
32435                 }
32436
32437                 if(this.labelWidth < 13 && this.labelmd == 0){
32438                     this.labelmd = this.labelWidth;
32439                 }
32440
32441                 if(this.labellg > 0){
32442                     labelCls = ' col-lg-' + this.labellg;
32443                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
32444                 }
32445
32446                 if(this.labelmd > 0){
32447                     labelCls = ' col-md-' + this.labelmd;
32448                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
32449                 }
32450
32451                 if(this.labelsm > 0){
32452                     labelCls = ' col-sm-' + this.labelsm;
32453                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
32454                 }
32455
32456                 if(this.labelxs > 0){
32457                     labelCls = ' col-xs-' + this.labelxs;
32458                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
32459                 }
32460             }
32461             
32462             label.cls += ' ' + labelCls;
32463             
32464             cfg.cn.push(label);
32465         }
32466         
32467         Roo.each(['day', 'month', 'year'], function(t){
32468             cfg.cn.push({
32469                 tag : 'div',
32470                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
32471             });
32472         }, this);
32473         
32474         return cfg;
32475     },
32476     
32477     inputEl: function ()
32478     {
32479         return this.el.select('.roo-date-split-field-group-value', true).first();
32480     },
32481     
32482     onRender : function(ct, position) 
32483     {
32484         var _this = this;
32485         
32486         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32487         
32488         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
32489         
32490         this.dayField = new Roo.bootstrap.ComboBox({
32491             allowBlank : this.dayAllowBlank,
32492             alwaysQuery : true,
32493             displayField : 'value',
32494             editable : false,
32495             fieldLabel : '',
32496             forceSelection : true,
32497             mode : 'local',
32498             placeholder : this.dayPlaceholder,
32499             selectOnFocus : true,
32500             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32501             triggerAction : 'all',
32502             typeAhead : true,
32503             valueField : 'value',
32504             store : new Roo.data.SimpleStore({
32505                 data : (function() {    
32506                     var days = [];
32507                     _this.fireEvent('days', _this, days);
32508                     return days;
32509                 })(),
32510                 fields : [ 'value' ]
32511             }),
32512             listeners : {
32513                 select : function (_self, record, index)
32514                 {
32515                     _this.setValue(_this.getValue());
32516                 }
32517             }
32518         });
32519
32520         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
32521         
32522         this.monthField = new Roo.bootstrap.MonthField({
32523             after : '<i class=\"fa fa-calendar\"></i>',
32524             allowBlank : this.monthAllowBlank,
32525             placeholder : this.monthPlaceholder,
32526             readOnly : true,
32527             listeners : {
32528                 render : function (_self)
32529                 {
32530                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
32531                         e.preventDefault();
32532                         _self.focus();
32533                     });
32534                 },
32535                 select : function (_self, oldvalue, newvalue)
32536                 {
32537                     _this.setValue(_this.getValue());
32538                 }
32539             }
32540         });
32541         
32542         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
32543         
32544         this.yearField = new Roo.bootstrap.ComboBox({
32545             allowBlank : this.yearAllowBlank,
32546             alwaysQuery : true,
32547             displayField : 'value',
32548             editable : false,
32549             fieldLabel : '',
32550             forceSelection : true,
32551             mode : 'local',
32552             placeholder : this.yearPlaceholder,
32553             selectOnFocus : true,
32554             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32555             triggerAction : 'all',
32556             typeAhead : true,
32557             valueField : 'value',
32558             store : new Roo.data.SimpleStore({
32559                 data : (function() {
32560                     var years = [];
32561                     _this.fireEvent('years', _this, years);
32562                     return years;
32563                 })(),
32564                 fields : [ 'value' ]
32565             }),
32566             listeners : {
32567                 select : function (_self, record, index)
32568                 {
32569                     _this.setValue(_this.getValue());
32570                 }
32571             }
32572         });
32573
32574         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
32575     },
32576     
32577     setValue : function(v, format)
32578     {
32579         this.inputEl.dom.value = v;
32580         
32581         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
32582         
32583         var d = Date.parseDate(v, f);
32584         
32585         if(!d){
32586             this.validate();
32587             return;
32588         }
32589         
32590         this.setDay(d.format(this.dayFormat));
32591         this.setMonth(d.format(this.monthFormat));
32592         this.setYear(d.format(this.yearFormat));
32593         
32594         this.validate();
32595         
32596         return;
32597     },
32598     
32599     setDay : function(v)
32600     {
32601         this.dayField.setValue(v);
32602         this.inputEl.dom.value = this.getValue();
32603         this.validate();
32604         return;
32605     },
32606     
32607     setMonth : function(v)
32608     {
32609         this.monthField.setValue(v, true);
32610         this.inputEl.dom.value = this.getValue();
32611         this.validate();
32612         return;
32613     },
32614     
32615     setYear : function(v)
32616     {
32617         this.yearField.setValue(v);
32618         this.inputEl.dom.value = this.getValue();
32619         this.validate();
32620         return;
32621     },
32622     
32623     getDay : function()
32624     {
32625         return this.dayField.getValue();
32626     },
32627     
32628     getMonth : function()
32629     {
32630         return this.monthField.getValue();
32631     },
32632     
32633     getYear : function()
32634     {
32635         return this.yearField.getValue();
32636     },
32637     
32638     getValue : function()
32639     {
32640         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
32641         
32642         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
32643         
32644         return date;
32645     },
32646     
32647     reset : function()
32648     {
32649         this.setDay('');
32650         this.setMonth('');
32651         this.setYear('');
32652         this.inputEl.dom.value = '';
32653         this.validate();
32654         return;
32655     },
32656     
32657     validate : function()
32658     {
32659         var d = this.dayField.validate();
32660         var m = this.monthField.validate();
32661         var y = this.yearField.validate();
32662         
32663         var valid = true;
32664         
32665         if(
32666                 (!this.dayAllowBlank && !d) ||
32667                 (!this.monthAllowBlank && !m) ||
32668                 (!this.yearAllowBlank && !y)
32669         ){
32670             valid = false;
32671         }
32672         
32673         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
32674             return valid;
32675         }
32676         
32677         if(valid){
32678             this.markValid();
32679             return valid;
32680         }
32681         
32682         this.markInvalid();
32683         
32684         return valid;
32685     },
32686     
32687     markValid : function()
32688     {
32689         
32690         var label = this.el.select('label', true).first();
32691         var icon = this.el.select('i.fa-star', true).first();
32692
32693         if(label && icon){
32694             icon.remove();
32695         }
32696         
32697         this.fireEvent('valid', this);
32698     },
32699     
32700      /**
32701      * Mark this field as invalid
32702      * @param {String} msg The validation message
32703      */
32704     markInvalid : function(msg)
32705     {
32706         
32707         var label = this.el.select('label', true).first();
32708         var icon = this.el.select('i.fa-star', true).first();
32709
32710         if(label && !icon){
32711             this.el.select('.roo-date-split-field-label', true).createChild({
32712                 tag : 'i',
32713                 cls : 'text-danger fa fa-lg fa-star',
32714                 tooltip : 'This field is required',
32715                 style : 'margin-right:5px;'
32716             }, label, true);
32717         }
32718         
32719         this.fireEvent('invalid', this, msg);
32720     },
32721     
32722     clearInvalid : function()
32723     {
32724         var label = this.el.select('label', true).first();
32725         var icon = this.el.select('i.fa-star', true).first();
32726
32727         if(label && icon){
32728             icon.remove();
32729         }
32730         
32731         this.fireEvent('valid', this);
32732     },
32733     
32734     getName: function()
32735     {
32736         return this.name;
32737     }
32738     
32739 });
32740
32741  /**
32742  *
32743  * This is based on 
32744  * http://masonry.desandro.com
32745  *
32746  * The idea is to render all the bricks based on vertical width...
32747  *
32748  * The original code extends 'outlayer' - we might need to use that....
32749  * 
32750  */
32751
32752
32753 /**
32754  * @class Roo.bootstrap.LayoutMasonry
32755  * @extends Roo.bootstrap.Component
32756  * Bootstrap Layout Masonry class
32757  * 
32758  * @constructor
32759  * Create a new Element
32760  * @param {Object} config The config object
32761  */
32762
32763 Roo.bootstrap.LayoutMasonry = function(config){
32764     
32765     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
32766     
32767     this.bricks = [];
32768     
32769     Roo.bootstrap.LayoutMasonry.register(this);
32770     
32771     this.addEvents({
32772         // raw events
32773         /**
32774          * @event layout
32775          * Fire after layout the items
32776          * @param {Roo.bootstrap.LayoutMasonry} this
32777          * @param {Roo.EventObject} e
32778          */
32779         "layout" : true
32780     });
32781     
32782 };
32783
32784 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
32785     
32786     /**
32787      * @cfg {Boolean} isLayoutInstant = no animation?
32788      */   
32789     isLayoutInstant : false, // needed?
32790    
32791     /**
32792      * @cfg {Number} boxWidth  width of the columns
32793      */   
32794     boxWidth : 450,
32795     
32796       /**
32797      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
32798      */   
32799     boxHeight : 0,
32800     
32801     /**
32802      * @cfg {Number} padWidth padding below box..
32803      */   
32804     padWidth : 10, 
32805     
32806     /**
32807      * @cfg {Number} gutter gutter width..
32808      */   
32809     gutter : 10,
32810     
32811      /**
32812      * @cfg {Number} maxCols maximum number of columns
32813      */   
32814     
32815     maxCols: 0,
32816     
32817     /**
32818      * @cfg {Boolean} isAutoInitial defalut true
32819      */   
32820     isAutoInitial : true, 
32821     
32822     containerWidth: 0,
32823     
32824     /**
32825      * @cfg {Boolean} isHorizontal defalut false
32826      */   
32827     isHorizontal : false, 
32828
32829     currentSize : null,
32830     
32831     tag: 'div',
32832     
32833     cls: '',
32834     
32835     bricks: null, //CompositeElement
32836     
32837     cols : 1,
32838     
32839     _isLayoutInited : false,
32840     
32841 //    isAlternative : false, // only use for vertical layout...
32842     
32843     /**
32844      * @cfg {Number} alternativePadWidth padding below box..
32845      */   
32846     alternativePadWidth : 50,
32847     
32848     selectedBrick : [],
32849     
32850     getAutoCreate : function(){
32851         
32852         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
32853         
32854         var cfg = {
32855             tag: this.tag,
32856             cls: 'blog-masonary-wrapper ' + this.cls,
32857             cn : {
32858                 cls : 'mas-boxes masonary'
32859             }
32860         };
32861         
32862         return cfg;
32863     },
32864     
32865     getChildContainer: function( )
32866     {
32867         if (this.boxesEl) {
32868             return this.boxesEl;
32869         }
32870         
32871         this.boxesEl = this.el.select('.mas-boxes').first();
32872         
32873         return this.boxesEl;
32874     },
32875     
32876     
32877     initEvents : function()
32878     {
32879         var _this = this;
32880         
32881         if(this.isAutoInitial){
32882             Roo.log('hook children rendered');
32883             this.on('childrenrendered', function() {
32884                 Roo.log('children rendered');
32885                 _this.initial();
32886             } ,this);
32887         }
32888     },
32889     
32890     initial : function()
32891     {
32892         this.selectedBrick = [];
32893         
32894         this.currentSize = this.el.getBox(true);
32895         
32896         Roo.EventManager.onWindowResize(this.resize, this); 
32897
32898         if(!this.isAutoInitial){
32899             this.layout();
32900             return;
32901         }
32902         
32903         this.layout();
32904         
32905         return;
32906         //this.layout.defer(500,this);
32907         
32908     },
32909     
32910     resize : function()
32911     {
32912         var cs = this.el.getBox(true);
32913         
32914         if (
32915                 this.currentSize.width == cs.width && 
32916                 this.currentSize.x == cs.x && 
32917                 this.currentSize.height == cs.height && 
32918                 this.currentSize.y == cs.y 
32919         ) {
32920             Roo.log("no change in with or X or Y");
32921             return;
32922         }
32923         
32924         this.currentSize = cs;
32925         
32926         this.layout();
32927         
32928     },
32929     
32930     layout : function()
32931     {   
32932         this._resetLayout();
32933         
32934         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32935         
32936         this.layoutItems( isInstant );
32937       
32938         this._isLayoutInited = true;
32939         
32940         this.fireEvent('layout', this);
32941         
32942     },
32943     
32944     _resetLayout : function()
32945     {
32946         if(this.isHorizontal){
32947             this.horizontalMeasureColumns();
32948             return;
32949         }
32950         
32951         this.verticalMeasureColumns();
32952         
32953     },
32954     
32955     verticalMeasureColumns : function()
32956     {
32957         this.getContainerWidth();
32958         
32959 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
32960 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
32961 //            return;
32962 //        }
32963         
32964         var boxWidth = this.boxWidth + this.padWidth;
32965         
32966         if(this.containerWidth < this.boxWidth){
32967             boxWidth = this.containerWidth
32968         }
32969         
32970         var containerWidth = this.containerWidth;
32971         
32972         var cols = Math.floor(containerWidth / boxWidth);
32973         
32974         this.cols = Math.max( cols, 1 );
32975         
32976         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32977         
32978         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
32979         
32980         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
32981         
32982         this.colWidth = boxWidth + avail - this.padWidth;
32983         
32984         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
32985         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
32986     },
32987     
32988     horizontalMeasureColumns : function()
32989     {
32990         this.getContainerWidth();
32991         
32992         var boxWidth = this.boxWidth;
32993         
32994         if(this.containerWidth < boxWidth){
32995             boxWidth = this.containerWidth;
32996         }
32997         
32998         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
32999         
33000         this.el.setHeight(boxWidth);
33001         
33002     },
33003     
33004     getContainerWidth : function()
33005     {
33006         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33007     },
33008     
33009     layoutItems : function( isInstant )
33010     {
33011         Roo.log(this.bricks);
33012         
33013         var items = Roo.apply([], this.bricks);
33014         
33015         if(this.isHorizontal){
33016             this._horizontalLayoutItems( items , isInstant );
33017             return;
33018         }
33019         
33020 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33021 //            this._verticalAlternativeLayoutItems( items , isInstant );
33022 //            return;
33023 //        }
33024         
33025         this._verticalLayoutItems( items , isInstant );
33026         
33027     },
33028     
33029     _verticalLayoutItems : function ( items , isInstant)
33030     {
33031         if ( !items || !items.length ) {
33032             return;
33033         }
33034         
33035         var standard = [
33036             ['xs', 'xs', 'xs', 'tall'],
33037             ['xs', 'xs', 'tall'],
33038             ['xs', 'xs', 'sm'],
33039             ['xs', 'xs', 'xs'],
33040             ['xs', 'tall'],
33041             ['xs', 'sm'],
33042             ['xs', 'xs'],
33043             ['xs'],
33044             
33045             ['sm', 'xs', 'xs'],
33046             ['sm', 'xs'],
33047             ['sm'],
33048             
33049             ['tall', 'xs', 'xs', 'xs'],
33050             ['tall', 'xs', 'xs'],
33051             ['tall', 'xs'],
33052             ['tall']
33053             
33054         ];
33055         
33056         var queue = [];
33057         
33058         var boxes = [];
33059         
33060         var box = [];
33061         
33062         Roo.each(items, function(item, k){
33063             
33064             switch (item.size) {
33065                 // these layouts take up a full box,
33066                 case 'md' :
33067                 case 'md-left' :
33068                 case 'md-right' :
33069                 case 'wide' :
33070                     
33071                     if(box.length){
33072                         boxes.push(box);
33073                         box = [];
33074                     }
33075                     
33076                     boxes.push([item]);
33077                     
33078                     break;
33079                     
33080                 case 'xs' :
33081                 case 'sm' :
33082                 case 'tall' :
33083                     
33084                     box.push(item);
33085                     
33086                     break;
33087                 default :
33088                     break;
33089                     
33090             }
33091             
33092         }, this);
33093         
33094         if(box.length){
33095             boxes.push(box);
33096             box = [];
33097         }
33098         
33099         var filterPattern = function(box, length)
33100         {
33101             if(!box.length){
33102                 return;
33103             }
33104             
33105             var match = false;
33106             
33107             var pattern = box.slice(0, length);
33108             
33109             var format = [];
33110             
33111             Roo.each(pattern, function(i){
33112                 format.push(i.size);
33113             }, this);
33114             
33115             Roo.each(standard, function(s){
33116                 
33117                 if(String(s) != String(format)){
33118                     return;
33119                 }
33120                 
33121                 match = true;
33122                 return false;
33123                 
33124             }, this);
33125             
33126             if(!match && length == 1){
33127                 return;
33128             }
33129             
33130             if(!match){
33131                 filterPattern(box, length - 1);
33132                 return;
33133             }
33134                 
33135             queue.push(pattern);
33136
33137             box = box.slice(length, box.length);
33138
33139             filterPattern(box, 4);
33140
33141             return;
33142             
33143         }
33144         
33145         Roo.each(boxes, function(box, k){
33146             
33147             if(!box.length){
33148                 return;
33149             }
33150             
33151             if(box.length == 1){
33152                 queue.push(box);
33153                 return;
33154             }
33155             
33156             filterPattern(box, 4);
33157             
33158         }, this);
33159         
33160         this._processVerticalLayoutQueue( queue, isInstant );
33161         
33162     },
33163     
33164 //    _verticalAlternativeLayoutItems : function( items , isInstant )
33165 //    {
33166 //        if ( !items || !items.length ) {
33167 //            return;
33168 //        }
33169 //
33170 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
33171 //        
33172 //    },
33173     
33174     _horizontalLayoutItems : function ( items , isInstant)
33175     {
33176         if ( !items || !items.length || items.length < 3) {
33177             return;
33178         }
33179         
33180         items.reverse();
33181         
33182         var eItems = items.slice(0, 3);
33183         
33184         items = items.slice(3, items.length);
33185         
33186         var standard = [
33187             ['xs', 'xs', 'xs', 'wide'],
33188             ['xs', 'xs', 'wide'],
33189             ['xs', 'xs', 'sm'],
33190             ['xs', 'xs', 'xs'],
33191             ['xs', 'wide'],
33192             ['xs', 'sm'],
33193             ['xs', 'xs'],
33194             ['xs'],
33195             
33196             ['sm', 'xs', 'xs'],
33197             ['sm', 'xs'],
33198             ['sm'],
33199             
33200             ['wide', 'xs', 'xs', 'xs'],
33201             ['wide', 'xs', 'xs'],
33202             ['wide', 'xs'],
33203             ['wide'],
33204             
33205             ['wide-thin']
33206         ];
33207         
33208         var queue = [];
33209         
33210         var boxes = [];
33211         
33212         var box = [];
33213         
33214         Roo.each(items, function(item, k){
33215             
33216             switch (item.size) {
33217                 case 'md' :
33218                 case 'md-left' :
33219                 case 'md-right' :
33220                 case 'tall' :
33221                     
33222                     if(box.length){
33223                         boxes.push(box);
33224                         box = [];
33225                     }
33226                     
33227                     boxes.push([item]);
33228                     
33229                     break;
33230                     
33231                 case 'xs' :
33232                 case 'sm' :
33233                 case 'wide' :
33234                 case 'wide-thin' :
33235                     
33236                     box.push(item);
33237                     
33238                     break;
33239                 default :
33240                     break;
33241                     
33242             }
33243             
33244         }, this);
33245         
33246         if(box.length){
33247             boxes.push(box);
33248             box = [];
33249         }
33250         
33251         var filterPattern = function(box, length)
33252         {
33253             if(!box.length){
33254                 return;
33255             }
33256             
33257             var match = false;
33258             
33259             var pattern = box.slice(0, length);
33260             
33261             var format = [];
33262             
33263             Roo.each(pattern, function(i){
33264                 format.push(i.size);
33265             }, this);
33266             
33267             Roo.each(standard, function(s){
33268                 
33269                 if(String(s) != String(format)){
33270                     return;
33271                 }
33272                 
33273                 match = true;
33274                 return false;
33275                 
33276             }, this);
33277             
33278             if(!match && length == 1){
33279                 return;
33280             }
33281             
33282             if(!match){
33283                 filterPattern(box, length - 1);
33284                 return;
33285             }
33286                 
33287             queue.push(pattern);
33288
33289             box = box.slice(length, box.length);
33290
33291             filterPattern(box, 4);
33292
33293             return;
33294             
33295         }
33296         
33297         Roo.each(boxes, function(box, k){
33298             
33299             if(!box.length){
33300                 return;
33301             }
33302             
33303             if(box.length == 1){
33304                 queue.push(box);
33305                 return;
33306             }
33307             
33308             filterPattern(box, 4);
33309             
33310         }, this);
33311         
33312         
33313         var prune = [];
33314         
33315         var pos = this.el.getBox(true);
33316         
33317         var minX = pos.x;
33318         
33319         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33320         
33321         var hit_end = false;
33322         
33323         Roo.each(queue, function(box){
33324             
33325             if(hit_end){
33326                 
33327                 Roo.each(box, function(b){
33328                 
33329                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33330                     b.el.hide();
33331
33332                 }, this);
33333
33334                 return;
33335             }
33336             
33337             var mx = 0;
33338             
33339             Roo.each(box, function(b){
33340                 
33341                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33342                 b.el.show();
33343
33344                 mx = Math.max(mx, b.x);
33345                 
33346             }, this);
33347             
33348             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33349             
33350             if(maxX < minX){
33351                 
33352                 Roo.each(box, function(b){
33353                 
33354                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33355                     b.el.hide();
33356                     
33357                 }, this);
33358                 
33359                 hit_end = true;
33360                 
33361                 return;
33362             }
33363             
33364             prune.push(box);
33365             
33366         }, this);
33367         
33368         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
33369     },
33370     
33371     /** Sets position of item in DOM
33372     * @param {Element} item
33373     * @param {Number} x - horizontal position
33374     * @param {Number} y - vertical position
33375     * @param {Boolean} isInstant - disables transitions
33376     */
33377     _processVerticalLayoutQueue : function( queue, isInstant )
33378     {
33379         var pos = this.el.getBox(true);
33380         var x = pos.x;
33381         var y = pos.y;
33382         var maxY = [];
33383         
33384         for (var i = 0; i < this.cols; i++){
33385             maxY[i] = pos.y;
33386         }
33387         
33388         Roo.each(queue, function(box, k){
33389             
33390             var col = k % this.cols;
33391             
33392             Roo.each(box, function(b,kk){
33393                 
33394                 b.el.position('absolute');
33395                 
33396                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33397                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33398                 
33399                 if(b.size == 'md-left' || b.size == 'md-right'){
33400                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33401                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33402                 }
33403                 
33404                 b.el.setWidth(width);
33405                 b.el.setHeight(height);
33406                 // iframe?
33407                 b.el.select('iframe',true).setSize(width,height);
33408                 
33409             }, this);
33410             
33411             for (var i = 0; i < this.cols; i++){
33412                 
33413                 if(maxY[i] < maxY[col]){
33414                     col = i;
33415                     continue;
33416                 }
33417                 
33418                 col = Math.min(col, i);
33419                 
33420             }
33421             
33422             x = pos.x + col * (this.colWidth + this.padWidth);
33423             
33424             y = maxY[col];
33425             
33426             var positions = [];
33427             
33428             switch (box.length){
33429                 case 1 :
33430                     positions = this.getVerticalOneBoxColPositions(x, y, box);
33431                     break;
33432                 case 2 :
33433                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
33434                     break;
33435                 case 3 :
33436                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
33437                     break;
33438                 case 4 :
33439                     positions = this.getVerticalFourBoxColPositions(x, y, box);
33440                     break;
33441                 default :
33442                     break;
33443             }
33444             
33445             Roo.each(box, function(b,kk){
33446                 
33447                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33448                 
33449                 var sz = b.el.getSize();
33450                 
33451                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
33452                 
33453             }, this);
33454             
33455         }, this);
33456         
33457         var mY = 0;
33458         
33459         for (var i = 0; i < this.cols; i++){
33460             mY = Math.max(mY, maxY[i]);
33461         }
33462         
33463         this.el.setHeight(mY - pos.y);
33464         
33465     },
33466     
33467 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
33468 //    {
33469 //        var pos = this.el.getBox(true);
33470 //        var x = pos.x;
33471 //        var y = pos.y;
33472 //        var maxX = pos.right;
33473 //        
33474 //        var maxHeight = 0;
33475 //        
33476 //        Roo.each(items, function(item, k){
33477 //            
33478 //            var c = k % 2;
33479 //            
33480 //            item.el.position('absolute');
33481 //                
33482 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
33483 //
33484 //            item.el.setWidth(width);
33485 //
33486 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
33487 //
33488 //            item.el.setHeight(height);
33489 //            
33490 //            if(c == 0){
33491 //                item.el.setXY([x, y], isInstant ? false : true);
33492 //            } else {
33493 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
33494 //            }
33495 //            
33496 //            y = y + height + this.alternativePadWidth;
33497 //            
33498 //            maxHeight = maxHeight + height + this.alternativePadWidth;
33499 //            
33500 //        }, this);
33501 //        
33502 //        this.el.setHeight(maxHeight);
33503 //        
33504 //    },
33505     
33506     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
33507     {
33508         var pos = this.el.getBox(true);
33509         
33510         var minX = pos.x;
33511         var minY = pos.y;
33512         
33513         var maxX = pos.right;
33514         
33515         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
33516         
33517         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33518         
33519         Roo.each(queue, function(box, k){
33520             
33521             Roo.each(box, function(b, kk){
33522                 
33523                 b.el.position('absolute');
33524                 
33525                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33526                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33527                 
33528                 if(b.size == 'md-left' || b.size == 'md-right'){
33529                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33530                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33531                 }
33532                 
33533                 b.el.setWidth(width);
33534                 b.el.setHeight(height);
33535                 
33536             }, this);
33537             
33538             if(!box.length){
33539                 return;
33540             }
33541             
33542             var positions = [];
33543             
33544             switch (box.length){
33545                 case 1 :
33546                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
33547                     break;
33548                 case 2 :
33549                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
33550                     break;
33551                 case 3 :
33552                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
33553                     break;
33554                 case 4 :
33555                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
33556                     break;
33557                 default :
33558                     break;
33559             }
33560             
33561             Roo.each(box, function(b,kk){
33562                 
33563                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33564                 
33565                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
33566                 
33567             }, this);
33568             
33569         }, this);
33570         
33571     },
33572     
33573     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
33574     {
33575         Roo.each(eItems, function(b,k){
33576             
33577             b.size = (k == 0) ? 'sm' : 'xs';
33578             b.x = (k == 0) ? 2 : 1;
33579             b.y = (k == 0) ? 2 : 1;
33580             
33581             b.el.position('absolute');
33582             
33583             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33584                 
33585             b.el.setWidth(width);
33586             
33587             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33588             
33589             b.el.setHeight(height);
33590             
33591         }, this);
33592
33593         var positions = [];
33594         
33595         positions.push({
33596             x : maxX - this.unitWidth * 2 - this.gutter,
33597             y : minY
33598         });
33599         
33600         positions.push({
33601             x : maxX - this.unitWidth,
33602             y : minY + (this.unitWidth + this.gutter) * 2
33603         });
33604         
33605         positions.push({
33606             x : maxX - this.unitWidth * 3 - this.gutter * 2,
33607             y : minY
33608         });
33609         
33610         Roo.each(eItems, function(b,k){
33611             
33612             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
33613
33614         }, this);
33615         
33616     },
33617     
33618     getVerticalOneBoxColPositions : function(x, y, box)
33619     {
33620         var pos = [];
33621         
33622         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
33623         
33624         if(box[0].size == 'md-left'){
33625             rand = 0;
33626         }
33627         
33628         if(box[0].size == 'md-right'){
33629             rand = 1;
33630         }
33631         
33632         pos.push({
33633             x : x + (this.unitWidth + this.gutter) * rand,
33634             y : y
33635         });
33636         
33637         return pos;
33638     },
33639     
33640     getVerticalTwoBoxColPositions : function(x, y, box)
33641     {
33642         var pos = [];
33643         
33644         if(box[0].size == 'xs'){
33645             
33646             pos.push({
33647                 x : x,
33648                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
33649             });
33650
33651             pos.push({
33652                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
33653                 y : y
33654             });
33655             
33656             return pos;
33657             
33658         }
33659         
33660         pos.push({
33661             x : x,
33662             y : y
33663         });
33664
33665         pos.push({
33666             x : x + (this.unitWidth + this.gutter) * 2,
33667             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
33668         });
33669         
33670         return pos;
33671         
33672     },
33673     
33674     getVerticalThreeBoxColPositions : function(x, y, box)
33675     {
33676         var pos = [];
33677         
33678         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
33679             
33680             pos.push({
33681                 x : x,
33682                 y : y
33683             });
33684
33685             pos.push({
33686                 x : x + (this.unitWidth + this.gutter) * 1,
33687                 y : y
33688             });
33689             
33690             pos.push({
33691                 x : x + (this.unitWidth + this.gutter) * 2,
33692                 y : y
33693             });
33694             
33695             return pos;
33696             
33697         }
33698         
33699         if(box[0].size == 'xs' && box[1].size == 'xs'){
33700             
33701             pos.push({
33702                 x : x,
33703                 y : y
33704             });
33705
33706             pos.push({
33707                 x : x,
33708                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
33709             });
33710             
33711             pos.push({
33712                 x : x + (this.unitWidth + this.gutter) * 1,
33713                 y : y
33714             });
33715             
33716             return pos;
33717             
33718         }
33719         
33720         pos.push({
33721             x : x,
33722             y : y
33723         });
33724
33725         pos.push({
33726             x : x + (this.unitWidth + this.gutter) * 2,
33727             y : y
33728         });
33729
33730         pos.push({
33731             x : x + (this.unitWidth + this.gutter) * 2,
33732             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
33733         });
33734             
33735         return pos;
33736         
33737     },
33738     
33739     getVerticalFourBoxColPositions : function(x, y, box)
33740     {
33741         var pos = [];
33742         
33743         if(box[0].size == 'xs'){
33744             
33745             pos.push({
33746                 x : x,
33747                 y : y
33748             });
33749
33750             pos.push({
33751                 x : x,
33752                 y : y + (this.unitHeight + this.gutter) * 1
33753             });
33754             
33755             pos.push({
33756                 x : x,
33757                 y : y + (this.unitHeight + this.gutter) * 2
33758             });
33759             
33760             pos.push({
33761                 x : x + (this.unitWidth + this.gutter) * 1,
33762                 y : y
33763             });
33764             
33765             return pos;
33766             
33767         }
33768         
33769         pos.push({
33770             x : x,
33771             y : y
33772         });
33773
33774         pos.push({
33775             x : x + (this.unitWidth + this.gutter) * 2,
33776             y : y
33777         });
33778
33779         pos.push({
33780             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
33781             y : y + (this.unitHeight + this.gutter) * 1
33782         });
33783
33784         pos.push({
33785             x : x + (this.unitWidth + this.gutter) * 2,
33786             y : y + (this.unitWidth + this.gutter) * 2
33787         });
33788
33789         return pos;
33790         
33791     },
33792     
33793     getHorizontalOneBoxColPositions : function(maxX, minY, box)
33794     {
33795         var pos = [];
33796         
33797         if(box[0].size == 'md-left'){
33798             pos.push({
33799                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
33800                 y : minY
33801             });
33802             
33803             return pos;
33804         }
33805         
33806         if(box[0].size == 'md-right'){
33807             pos.push({
33808                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
33809                 y : minY + (this.unitWidth + this.gutter) * 1
33810             });
33811             
33812             return pos;
33813         }
33814         
33815         var rand = Math.floor(Math.random() * (4 - box[0].y));
33816         
33817         pos.push({
33818             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33819             y : minY + (this.unitWidth + this.gutter) * rand
33820         });
33821         
33822         return pos;
33823         
33824     },
33825     
33826     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
33827     {
33828         var pos = [];
33829         
33830         if(box[0].size == 'xs'){
33831             
33832             pos.push({
33833                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33834                 y : minY
33835             });
33836
33837             pos.push({
33838                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33839                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
33840             });
33841             
33842             return pos;
33843             
33844         }
33845         
33846         pos.push({
33847             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33848             y : minY
33849         });
33850
33851         pos.push({
33852             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33853             y : minY + (this.unitWidth + this.gutter) * 2
33854         });
33855         
33856         return pos;
33857         
33858     },
33859     
33860     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
33861     {
33862         var pos = [];
33863         
33864         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
33865             
33866             pos.push({
33867                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33868                 y : minY
33869             });
33870
33871             pos.push({
33872                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33873                 y : minY + (this.unitWidth + this.gutter) * 1
33874             });
33875             
33876             pos.push({
33877                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33878                 y : minY + (this.unitWidth + this.gutter) * 2
33879             });
33880             
33881             return pos;
33882             
33883         }
33884         
33885         if(box[0].size == 'xs' && box[1].size == 'xs'){
33886             
33887             pos.push({
33888                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33889                 y : minY
33890             });
33891
33892             pos.push({
33893                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33894                 y : minY
33895             });
33896             
33897             pos.push({
33898                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33899                 y : minY + (this.unitWidth + this.gutter) * 1
33900             });
33901             
33902             return pos;
33903             
33904         }
33905         
33906         pos.push({
33907             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33908             y : minY
33909         });
33910
33911         pos.push({
33912             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33913             y : minY + (this.unitWidth + this.gutter) * 2
33914         });
33915
33916         pos.push({
33917             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33918             y : minY + (this.unitWidth + this.gutter) * 2
33919         });
33920             
33921         return pos;
33922         
33923     },
33924     
33925     getHorizontalFourBoxColPositions : function(maxX, minY, box)
33926     {
33927         var pos = [];
33928         
33929         if(box[0].size == 'xs'){
33930             
33931             pos.push({
33932                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33933                 y : minY
33934             });
33935
33936             pos.push({
33937                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33938                 y : minY
33939             });
33940             
33941             pos.push({
33942                 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),
33943                 y : minY
33944             });
33945             
33946             pos.push({
33947                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
33948                 y : minY + (this.unitWidth + this.gutter) * 1
33949             });
33950             
33951             return pos;
33952             
33953         }
33954         
33955         pos.push({
33956             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33957             y : minY
33958         });
33959         
33960         pos.push({
33961             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33962             y : minY + (this.unitWidth + this.gutter) * 2
33963         });
33964         
33965         pos.push({
33966             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33967             y : minY + (this.unitWidth + this.gutter) * 2
33968         });
33969         
33970         pos.push({
33971             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),
33972             y : minY + (this.unitWidth + this.gutter) * 2
33973         });
33974
33975         return pos;
33976         
33977     },
33978     
33979     /**
33980     * remove a Masonry Brick
33981     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
33982     */
33983     removeBrick : function(brick_id)
33984     {
33985         if (!brick_id) {
33986             return;
33987         }
33988         
33989         for (var i = 0; i<this.bricks.length; i++) {
33990             if (this.bricks[i].id == brick_id) {
33991                 this.bricks.splice(i,1);
33992                 this.el.dom.removeChild(Roo.get(brick_id).dom);
33993                 this.initial();
33994             }
33995         }
33996     },
33997     
33998     /**
33999     * adds a Masonry Brick
34000     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34001     */
34002     addBrick : function(cfg)
34003     {
34004         var cn = new Roo.bootstrap.MasonryBrick(cfg);
34005         //this.register(cn);
34006         cn.parentId = this.id;
34007         cn.render(this.el);
34008         return cn;
34009     },
34010     
34011     /**
34012     * register a Masonry Brick
34013     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34014     */
34015     
34016     register : function(brick)
34017     {
34018         this.bricks.push(brick);
34019         brick.masonryId = this.id;
34020     },
34021     
34022     /**
34023     * clear all the Masonry Brick
34024     */
34025     clearAll : function()
34026     {
34027         this.bricks = [];
34028         //this.getChildContainer().dom.innerHTML = "";
34029         this.el.dom.innerHTML = '';
34030     },
34031     
34032     getSelected : function()
34033     {
34034         if (!this.selectedBrick) {
34035             return false;
34036         }
34037         
34038         return this.selectedBrick;
34039     }
34040 });
34041
34042 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34043     
34044     groups: {},
34045      /**
34046     * register a Masonry Layout
34047     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34048     */
34049     
34050     register : function(layout)
34051     {
34052         this.groups[layout.id] = layout;
34053     },
34054     /**
34055     * fetch a  Masonry Layout based on the masonry layout ID
34056     * @param {string} the masonry layout to add
34057     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34058     */
34059     
34060     get: function(layout_id) {
34061         if (typeof(this.groups[layout_id]) == 'undefined') {
34062             return false;
34063         }
34064         return this.groups[layout_id] ;
34065     }
34066     
34067     
34068     
34069 });
34070
34071  
34072
34073  /**
34074  *
34075  * This is based on 
34076  * http://masonry.desandro.com
34077  *
34078  * The idea is to render all the bricks based on vertical width...
34079  *
34080  * The original code extends 'outlayer' - we might need to use that....
34081  * 
34082  */
34083
34084
34085 /**
34086  * @class Roo.bootstrap.LayoutMasonryAuto
34087  * @extends Roo.bootstrap.Component
34088  * Bootstrap Layout Masonry class
34089  * 
34090  * @constructor
34091  * Create a new Element
34092  * @param {Object} config The config object
34093  */
34094
34095 Roo.bootstrap.LayoutMasonryAuto = function(config){
34096     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34097 };
34098
34099 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
34100     
34101       /**
34102      * @cfg {Boolean} isFitWidth  - resize the width..
34103      */   
34104     isFitWidth : false,  // options..
34105     /**
34106      * @cfg {Boolean} isOriginLeft = left align?
34107      */   
34108     isOriginLeft : true,
34109     /**
34110      * @cfg {Boolean} isOriginTop = top align?
34111      */   
34112     isOriginTop : false,
34113     /**
34114      * @cfg {Boolean} isLayoutInstant = no animation?
34115      */   
34116     isLayoutInstant : false, // needed?
34117     /**
34118      * @cfg {Boolean} isResizingContainer = not sure if this is used..
34119      */   
34120     isResizingContainer : true,
34121     /**
34122      * @cfg {Number} columnWidth  width of the columns 
34123      */   
34124     
34125     columnWidth : 0,
34126     
34127     /**
34128      * @cfg {Number} maxCols maximum number of columns
34129      */   
34130     
34131     maxCols: 0,
34132     /**
34133      * @cfg {Number} padHeight padding below box..
34134      */   
34135     
34136     padHeight : 10, 
34137     
34138     /**
34139      * @cfg {Boolean} isAutoInitial defalut true
34140      */   
34141     
34142     isAutoInitial : true, 
34143     
34144     // private?
34145     gutter : 0,
34146     
34147     containerWidth: 0,
34148     initialColumnWidth : 0,
34149     currentSize : null,
34150     
34151     colYs : null, // array.
34152     maxY : 0,
34153     padWidth: 10,
34154     
34155     
34156     tag: 'div',
34157     cls: '',
34158     bricks: null, //CompositeElement
34159     cols : 0, // array?
34160     // element : null, // wrapped now this.el
34161     _isLayoutInited : null, 
34162     
34163     
34164     getAutoCreate : function(){
34165         
34166         var cfg = {
34167             tag: this.tag,
34168             cls: 'blog-masonary-wrapper ' + this.cls,
34169             cn : {
34170                 cls : 'mas-boxes masonary'
34171             }
34172         };
34173         
34174         return cfg;
34175     },
34176     
34177     getChildContainer: function( )
34178     {
34179         if (this.boxesEl) {
34180             return this.boxesEl;
34181         }
34182         
34183         this.boxesEl = this.el.select('.mas-boxes').first();
34184         
34185         return this.boxesEl;
34186     },
34187     
34188     
34189     initEvents : function()
34190     {
34191         var _this = this;
34192         
34193         if(this.isAutoInitial){
34194             Roo.log('hook children rendered');
34195             this.on('childrenrendered', function() {
34196                 Roo.log('children rendered');
34197                 _this.initial();
34198             } ,this);
34199         }
34200         
34201     },
34202     
34203     initial : function()
34204     {
34205         this.reloadItems();
34206
34207         this.currentSize = this.el.getBox(true);
34208
34209         /// was window resize... - let's see if this works..
34210         Roo.EventManager.onWindowResize(this.resize, this); 
34211
34212         if(!this.isAutoInitial){
34213             this.layout();
34214             return;
34215         }
34216         
34217         this.layout.defer(500,this);
34218     },
34219     
34220     reloadItems: function()
34221     {
34222         this.bricks = this.el.select('.masonry-brick', true);
34223         
34224         this.bricks.each(function(b) {
34225             //Roo.log(b.getSize());
34226             if (!b.attr('originalwidth')) {
34227                 b.attr('originalwidth',  b.getSize().width);
34228             }
34229             
34230         });
34231         
34232         Roo.log(this.bricks.elements.length);
34233     },
34234     
34235     resize : function()
34236     {
34237         Roo.log('resize');
34238         var cs = this.el.getBox(true);
34239         
34240         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34241             Roo.log("no change in with or X");
34242             return;
34243         }
34244         this.currentSize = cs;
34245         this.layout();
34246     },
34247     
34248     layout : function()
34249     {
34250          Roo.log('layout');
34251         this._resetLayout();
34252         //this._manageStamps();
34253       
34254         // don't animate first layout
34255         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34256         this.layoutItems( isInstant );
34257       
34258         // flag for initalized
34259         this._isLayoutInited = true;
34260     },
34261     
34262     layoutItems : function( isInstant )
34263     {
34264         //var items = this._getItemsForLayout( this.items );
34265         // original code supports filtering layout items.. we just ignore it..
34266         
34267         this._layoutItems( this.bricks , isInstant );
34268       
34269         this._postLayout();
34270     },
34271     _layoutItems : function ( items , isInstant)
34272     {
34273        //this.fireEvent( 'layout', this, items );
34274     
34275
34276         if ( !items || !items.elements.length ) {
34277           // no items, emit event with empty array
34278             return;
34279         }
34280
34281         var queue = [];
34282         items.each(function(item) {
34283             Roo.log("layout item");
34284             Roo.log(item);
34285             // get x/y object from method
34286             var position = this._getItemLayoutPosition( item );
34287             // enqueue
34288             position.item = item;
34289             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34290             queue.push( position );
34291         }, this);
34292       
34293         this._processLayoutQueue( queue );
34294     },
34295     /** Sets position of item in DOM
34296     * @param {Element} item
34297     * @param {Number} x - horizontal position
34298     * @param {Number} y - vertical position
34299     * @param {Boolean} isInstant - disables transitions
34300     */
34301     _processLayoutQueue : function( queue )
34302     {
34303         for ( var i=0, len = queue.length; i < len; i++ ) {
34304             var obj = queue[i];
34305             obj.item.position('absolute');
34306             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34307         }
34308     },
34309       
34310     
34311     /**
34312     * Any logic you want to do after each layout,
34313     * i.e. size the container
34314     */
34315     _postLayout : function()
34316     {
34317         this.resizeContainer();
34318     },
34319     
34320     resizeContainer : function()
34321     {
34322         if ( !this.isResizingContainer ) {
34323             return;
34324         }
34325         var size = this._getContainerSize();
34326         if ( size ) {
34327             this.el.setSize(size.width,size.height);
34328             this.boxesEl.setSize(size.width,size.height);
34329         }
34330     },
34331     
34332     
34333     
34334     _resetLayout : function()
34335     {
34336         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34337         this.colWidth = this.el.getWidth();
34338         //this.gutter = this.el.getWidth(); 
34339         
34340         this.measureColumns();
34341
34342         // reset column Y
34343         var i = this.cols;
34344         this.colYs = [];
34345         while (i--) {
34346             this.colYs.push( 0 );
34347         }
34348     
34349         this.maxY = 0;
34350     },
34351
34352     measureColumns : function()
34353     {
34354         this.getContainerWidth();
34355       // if columnWidth is 0, default to outerWidth of first item
34356         if ( !this.columnWidth ) {
34357             var firstItem = this.bricks.first();
34358             Roo.log(firstItem);
34359             this.columnWidth  = this.containerWidth;
34360             if (firstItem && firstItem.attr('originalwidth') ) {
34361                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34362             }
34363             // columnWidth fall back to item of first element
34364             Roo.log("set column width?");
34365                         this.initialColumnWidth = this.columnWidth  ;
34366
34367             // if first elem has no width, default to size of container
34368             
34369         }
34370         
34371         
34372         if (this.initialColumnWidth) {
34373             this.columnWidth = this.initialColumnWidth;
34374         }
34375         
34376         
34377             
34378         // column width is fixed at the top - however if container width get's smaller we should
34379         // reduce it...
34380         
34381         // this bit calcs how man columns..
34382             
34383         var columnWidth = this.columnWidth += this.gutter;
34384       
34385         // calculate columns
34386         var containerWidth = this.containerWidth + this.gutter;
34387         
34388         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
34389         // fix rounding errors, typically with gutters
34390         var excess = columnWidth - containerWidth % columnWidth;
34391         
34392         
34393         // if overshoot is less than a pixel, round up, otherwise floor it
34394         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
34395         cols = Math[ mathMethod ]( cols );
34396         this.cols = Math.max( cols, 1 );
34397         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34398         
34399          // padding positioning..
34400         var totalColWidth = this.cols * this.columnWidth;
34401         var padavail = this.containerWidth - totalColWidth;
34402         // so for 2 columns - we need 3 'pads'
34403         
34404         var padNeeded = (1+this.cols) * this.padWidth;
34405         
34406         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
34407         
34408         this.columnWidth += padExtra
34409         //this.padWidth = Math.floor(padavail /  ( this.cols));
34410         
34411         // adjust colum width so that padding is fixed??
34412         
34413         // we have 3 columns ... total = width * 3
34414         // we have X left over... that should be used by 
34415         
34416         //if (this.expandC) {
34417             
34418         //}
34419         
34420         
34421         
34422     },
34423     
34424     getContainerWidth : function()
34425     {
34426        /* // container is parent if fit width
34427         var container = this.isFitWidth ? this.element.parentNode : this.element;
34428         // check that this.size and size are there
34429         // IE8 triggers resize on body size change, so they might not be
34430         
34431         var size = getSize( container );  //FIXME
34432         this.containerWidth = size && size.innerWidth; //FIXME
34433         */
34434          
34435         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34436         
34437     },
34438     
34439     _getItemLayoutPosition : function( item )  // what is item?
34440     {
34441         // we resize the item to our columnWidth..
34442       
34443         item.setWidth(this.columnWidth);
34444         item.autoBoxAdjust  = false;
34445         
34446         var sz = item.getSize();
34447  
34448         // how many columns does this brick span
34449         var remainder = this.containerWidth % this.columnWidth;
34450         
34451         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
34452         // round if off by 1 pixel, otherwise use ceil
34453         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
34454         colSpan = Math.min( colSpan, this.cols );
34455         
34456         // normally this should be '1' as we dont' currently allow multi width columns..
34457         
34458         var colGroup = this._getColGroup( colSpan );
34459         // get the minimum Y value from the columns
34460         var minimumY = Math.min.apply( Math, colGroup );
34461         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
34462         
34463         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
34464          
34465         // position the brick
34466         var position = {
34467             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
34468             y: this.currentSize.y + minimumY + this.padHeight
34469         };
34470         
34471         Roo.log(position);
34472         // apply setHeight to necessary columns
34473         var setHeight = minimumY + sz.height + this.padHeight;
34474         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
34475         
34476         var setSpan = this.cols + 1 - colGroup.length;
34477         for ( var i = 0; i < setSpan; i++ ) {
34478           this.colYs[ shortColIndex + i ] = setHeight ;
34479         }
34480       
34481         return position;
34482     },
34483     
34484     /**
34485      * @param {Number} colSpan - number of columns the element spans
34486      * @returns {Array} colGroup
34487      */
34488     _getColGroup : function( colSpan )
34489     {
34490         if ( colSpan < 2 ) {
34491           // if brick spans only one column, use all the column Ys
34492           return this.colYs;
34493         }
34494       
34495         var colGroup = [];
34496         // how many different places could this brick fit horizontally
34497         var groupCount = this.cols + 1 - colSpan;
34498         // for each group potential horizontal position
34499         for ( var i = 0; i < groupCount; i++ ) {
34500           // make an array of colY values for that one group
34501           var groupColYs = this.colYs.slice( i, i + colSpan );
34502           // and get the max value of the array
34503           colGroup[i] = Math.max.apply( Math, groupColYs );
34504         }
34505         return colGroup;
34506     },
34507     /*
34508     _manageStamp : function( stamp )
34509     {
34510         var stampSize =  stamp.getSize();
34511         var offset = stamp.getBox();
34512         // get the columns that this stamp affects
34513         var firstX = this.isOriginLeft ? offset.x : offset.right;
34514         var lastX = firstX + stampSize.width;
34515         var firstCol = Math.floor( firstX / this.columnWidth );
34516         firstCol = Math.max( 0, firstCol );
34517         
34518         var lastCol = Math.floor( lastX / this.columnWidth );
34519         // lastCol should not go over if multiple of columnWidth #425
34520         lastCol -= lastX % this.columnWidth ? 0 : 1;
34521         lastCol = Math.min( this.cols - 1, lastCol );
34522         
34523         // set colYs to bottom of the stamp
34524         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
34525             stampSize.height;
34526             
34527         for ( var i = firstCol; i <= lastCol; i++ ) {
34528           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
34529         }
34530     },
34531     */
34532     
34533     _getContainerSize : function()
34534     {
34535         this.maxY = Math.max.apply( Math, this.colYs );
34536         var size = {
34537             height: this.maxY
34538         };
34539       
34540         if ( this.isFitWidth ) {
34541             size.width = this._getContainerFitWidth();
34542         }
34543       
34544         return size;
34545     },
34546     
34547     _getContainerFitWidth : function()
34548     {
34549         var unusedCols = 0;
34550         // count unused columns
34551         var i = this.cols;
34552         while ( --i ) {
34553           if ( this.colYs[i] !== 0 ) {
34554             break;
34555           }
34556           unusedCols++;
34557         }
34558         // fit container to columns that have been used
34559         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
34560     },
34561     
34562     needsResizeLayout : function()
34563     {
34564         var previousWidth = this.containerWidth;
34565         this.getContainerWidth();
34566         return previousWidth !== this.containerWidth;
34567     }
34568  
34569 });
34570
34571  
34572
34573  /*
34574  * - LGPL
34575  *
34576  * element
34577  * 
34578  */
34579
34580 /**
34581  * @class Roo.bootstrap.MasonryBrick
34582  * @extends Roo.bootstrap.Component
34583  * Bootstrap MasonryBrick class
34584  * 
34585  * @constructor
34586  * Create a new MasonryBrick
34587  * @param {Object} config The config object
34588  */
34589
34590 Roo.bootstrap.MasonryBrick = function(config){
34591     
34592     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
34593     
34594     Roo.bootstrap.MasonryBrick.register(this);
34595     
34596     this.addEvents({
34597         // raw events
34598         /**
34599          * @event click
34600          * When a MasonryBrick is clcik
34601          * @param {Roo.bootstrap.MasonryBrick} this
34602          * @param {Roo.EventObject} e
34603          */
34604         "click" : true
34605     });
34606 };
34607
34608 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
34609     
34610     /**
34611      * @cfg {String} title
34612      */   
34613     title : '',
34614     /**
34615      * @cfg {String} html
34616      */   
34617     html : '',
34618     /**
34619      * @cfg {String} bgimage
34620      */   
34621     bgimage : '',
34622     /**
34623      * @cfg {String} videourl
34624      */   
34625     videourl : '',
34626     /**
34627      * @cfg {String} cls
34628      */   
34629     cls : '',
34630     /**
34631      * @cfg {String} href
34632      */   
34633     href : '',
34634     /**
34635      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
34636      */   
34637     size : 'xs',
34638     
34639     /**
34640      * @cfg {String} placetitle (center|bottom)
34641      */   
34642     placetitle : '',
34643     
34644     /**
34645      * @cfg {Boolean} isFitContainer defalut true
34646      */   
34647     isFitContainer : true, 
34648     
34649     /**
34650      * @cfg {Boolean} preventDefault defalut false
34651      */   
34652     preventDefault : false, 
34653     
34654     /**
34655      * @cfg {Boolean} inverse defalut false
34656      */   
34657     maskInverse : false, 
34658     
34659     getAutoCreate : function()
34660     {
34661         if(!this.isFitContainer){
34662             return this.getSplitAutoCreate();
34663         }
34664         
34665         var cls = 'masonry-brick masonry-brick-full';
34666         
34667         if(this.href.length){
34668             cls += ' masonry-brick-link';
34669         }
34670         
34671         if(this.bgimage.length){
34672             cls += ' masonry-brick-image';
34673         }
34674         
34675         if(this.maskInverse){
34676             cls += ' mask-inverse';
34677         }
34678         
34679         if(!this.html.length && !this.maskInverse && !this.videourl.length){
34680             cls += ' enable-mask';
34681         }
34682         
34683         if(this.size){
34684             cls += ' masonry-' + this.size + '-brick';
34685         }
34686         
34687         if(this.placetitle.length){
34688             
34689             switch (this.placetitle) {
34690                 case 'center' :
34691                     cls += ' masonry-center-title';
34692                     break;
34693                 case 'bottom' :
34694                     cls += ' masonry-bottom-title';
34695                     break;
34696                 default:
34697                     break;
34698             }
34699             
34700         } else {
34701             if(!this.html.length && !this.bgimage.length){
34702                 cls += ' masonry-center-title';
34703             }
34704
34705             if(!this.html.length && this.bgimage.length){
34706                 cls += ' masonry-bottom-title';
34707             }
34708         }
34709         
34710         if(this.cls){
34711             cls += ' ' + this.cls;
34712         }
34713         
34714         var cfg = {
34715             tag: (this.href.length) ? 'a' : 'div',
34716             cls: cls,
34717             cn: [
34718                 {
34719                     tag: 'div',
34720                     cls: 'masonry-brick-mask'
34721                 },
34722                 {
34723                     tag: 'div',
34724                     cls: 'masonry-brick-paragraph',
34725                     cn: []
34726                 }
34727             ]
34728         };
34729         
34730         if(this.href.length){
34731             cfg.href = this.href;
34732         }
34733         
34734         var cn = cfg.cn[1].cn;
34735         
34736         if(this.title.length){
34737             cn.push({
34738                 tag: 'h4',
34739                 cls: 'masonry-brick-title',
34740                 html: this.title
34741             });
34742         }
34743         
34744         if(this.html.length){
34745             cn.push({
34746                 tag: 'p',
34747                 cls: 'masonry-brick-text',
34748                 html: this.html
34749             });
34750         }
34751         
34752         if (!this.title.length && !this.html.length) {
34753             cfg.cn[1].cls += ' hide';
34754         }
34755         
34756         if(this.bgimage.length){
34757             cfg.cn.push({
34758                 tag: 'img',
34759                 cls: 'masonry-brick-image-view',
34760                 src: this.bgimage
34761             });
34762         }
34763         
34764         if(this.videourl.length){
34765             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
34766             // youtube support only?
34767             cfg.cn.push({
34768                 tag: 'iframe',
34769                 cls: 'masonry-brick-image-view',
34770                 src: vurl,
34771                 frameborder : 0,
34772                 allowfullscreen : true
34773             });
34774         }
34775         
34776         return cfg;
34777         
34778     },
34779     
34780     getSplitAutoCreate : function()
34781     {
34782         var cls = 'masonry-brick masonry-brick-split';
34783         
34784         if(this.href.length){
34785             cls += ' masonry-brick-link';
34786         }
34787         
34788         if(this.bgimage.length){
34789             cls += ' masonry-brick-image';
34790         }
34791         
34792         if(this.size){
34793             cls += ' masonry-' + this.size + '-brick';
34794         }
34795         
34796         switch (this.placetitle) {
34797             case 'center' :
34798                 cls += ' masonry-center-title';
34799                 break;
34800             case 'bottom' :
34801                 cls += ' masonry-bottom-title';
34802                 break;
34803             default:
34804                 if(!this.bgimage.length){
34805                     cls += ' masonry-center-title';
34806                 }
34807
34808                 if(this.bgimage.length){
34809                     cls += ' masonry-bottom-title';
34810                 }
34811                 break;
34812         }
34813         
34814         if(this.cls){
34815             cls += ' ' + this.cls;
34816         }
34817         
34818         var cfg = {
34819             tag: (this.href.length) ? 'a' : 'div',
34820             cls: cls,
34821             cn: [
34822                 {
34823                     tag: 'div',
34824                     cls: 'masonry-brick-split-head',
34825                     cn: [
34826                         {
34827                             tag: 'div',
34828                             cls: 'masonry-brick-paragraph',
34829                             cn: []
34830                         }
34831                     ]
34832                 },
34833                 {
34834                     tag: 'div',
34835                     cls: 'masonry-brick-split-body',
34836                     cn: []
34837                 }
34838             ]
34839         };
34840         
34841         if(this.href.length){
34842             cfg.href = this.href;
34843         }
34844         
34845         if(this.title.length){
34846             cfg.cn[0].cn[0].cn.push({
34847                 tag: 'h4',
34848                 cls: 'masonry-brick-title',
34849                 html: this.title
34850             });
34851         }
34852         
34853         if(this.html.length){
34854             cfg.cn[1].cn.push({
34855                 tag: 'p',
34856                 cls: 'masonry-brick-text',
34857                 html: this.html
34858             });
34859         }
34860
34861         if(this.bgimage.length){
34862             cfg.cn[0].cn.push({
34863                 tag: 'img',
34864                 cls: 'masonry-brick-image-view',
34865                 src: this.bgimage
34866             });
34867         }
34868         
34869         if(this.videourl.length){
34870             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
34871             // youtube support only?
34872             cfg.cn[0].cn.cn.push({
34873                 tag: 'iframe',
34874                 cls: 'masonry-brick-image-view',
34875                 src: vurl,
34876                 frameborder : 0,
34877                 allowfullscreen : true
34878             });
34879         }
34880         
34881         return cfg;
34882     },
34883     
34884     initEvents: function() 
34885     {
34886         switch (this.size) {
34887             case 'xs' :
34888                 this.x = 1;
34889                 this.y = 1;
34890                 break;
34891             case 'sm' :
34892                 this.x = 2;
34893                 this.y = 2;
34894                 break;
34895             case 'md' :
34896             case 'md-left' :
34897             case 'md-right' :
34898                 this.x = 3;
34899                 this.y = 3;
34900                 break;
34901             case 'tall' :
34902                 this.x = 2;
34903                 this.y = 3;
34904                 break;
34905             case 'wide' :
34906                 this.x = 3;
34907                 this.y = 2;
34908                 break;
34909             case 'wide-thin' :
34910                 this.x = 3;
34911                 this.y = 1;
34912                 break;
34913                         
34914             default :
34915                 break;
34916         }
34917         
34918         if(Roo.isTouch){
34919             this.el.on('touchstart', this.onTouchStart, this);
34920             this.el.on('touchmove', this.onTouchMove, this);
34921             this.el.on('touchend', this.onTouchEnd, this);
34922             this.el.on('contextmenu', this.onContextMenu, this);
34923         } else {
34924             this.el.on('mouseenter'  ,this.enter, this);
34925             this.el.on('mouseleave', this.leave, this);
34926             this.el.on('click', this.onClick, this);
34927         }
34928         
34929         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
34930             this.parent().bricks.push(this);   
34931         }
34932         
34933     },
34934     
34935     onClick: function(e, el)
34936     {
34937         var time = this.endTimer - this.startTimer;
34938         // Roo.log(e.preventDefault());
34939         if(Roo.isTouch){
34940             if(time > 1000){
34941                 e.preventDefault();
34942                 return;
34943             }
34944         }
34945         
34946         if(!this.preventDefault){
34947             return;
34948         }
34949         
34950         e.preventDefault();
34951         
34952         if (this.activeClass != '') {
34953             this.selectBrick();
34954         }
34955         
34956         this.fireEvent('click', this, e);
34957     },
34958     
34959     enter: function(e, el)
34960     {
34961         e.preventDefault();
34962         
34963         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
34964             return;
34965         }
34966         
34967         if(this.bgimage.length && this.html.length){
34968             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
34969         }
34970     },
34971     
34972     leave: function(e, el)
34973     {
34974         e.preventDefault();
34975         
34976         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
34977             return;
34978         }
34979         
34980         if(this.bgimage.length && this.html.length){
34981             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
34982         }
34983     },
34984     
34985     onTouchStart: function(e, el)
34986     {
34987 //        e.preventDefault();
34988         
34989         this.touchmoved = false;
34990         
34991         if(!this.isFitContainer){
34992             return;
34993         }
34994         
34995         if(!this.bgimage.length || !this.html.length){
34996             return;
34997         }
34998         
34999         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35000         
35001         this.timer = new Date().getTime();
35002         
35003     },
35004     
35005     onTouchMove: function(e, el)
35006     {
35007         this.touchmoved = true;
35008     },
35009     
35010     onContextMenu : function(e,el)
35011     {
35012         e.preventDefault();
35013         e.stopPropagation();
35014         return false;
35015     },
35016     
35017     onTouchEnd: function(e, el)
35018     {
35019 //        e.preventDefault();
35020         
35021         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35022         
35023             this.leave(e,el);
35024             
35025             return;
35026         }
35027         
35028         if(!this.bgimage.length || !this.html.length){
35029             
35030             if(this.href.length){
35031                 window.location.href = this.href;
35032             }
35033             
35034             return;
35035         }
35036         
35037         if(!this.isFitContainer){
35038             return;
35039         }
35040         
35041         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35042         
35043         window.location.href = this.href;
35044     },
35045     
35046     //selection on single brick only
35047     selectBrick : function() {
35048         
35049         if (!this.parentId) {
35050             return;
35051         }
35052         
35053         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35054         var index = m.selectedBrick.indexOf(this.id);
35055         
35056         if ( index > -1) {
35057             m.selectedBrick.splice(index,1);
35058             this.el.removeClass(this.activeClass);
35059             return;
35060         }
35061         
35062         for(var i = 0; i < m.selectedBrick.length; i++) {
35063             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35064             b.el.removeClass(b.activeClass);
35065         }
35066         
35067         m.selectedBrick = [];
35068         
35069         m.selectedBrick.push(this.id);
35070         this.el.addClass(this.activeClass);
35071         return;
35072     },
35073     
35074     isSelected : function(){
35075         return this.el.hasClass(this.activeClass);
35076         
35077     }
35078 });
35079
35080 Roo.apply(Roo.bootstrap.MasonryBrick, {
35081     
35082     //groups: {},
35083     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35084      /**
35085     * register a Masonry Brick
35086     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35087     */
35088     
35089     register : function(brick)
35090     {
35091         //this.groups[brick.id] = brick;
35092         this.groups.add(brick.id, brick);
35093     },
35094     /**
35095     * fetch a  masonry brick based on the masonry brick ID
35096     * @param {string} the masonry brick to add
35097     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35098     */
35099     
35100     get: function(brick_id) 
35101     {
35102         // if (typeof(this.groups[brick_id]) == 'undefined') {
35103         //     return false;
35104         // }
35105         // return this.groups[brick_id] ;
35106         
35107         if(this.groups.key(brick_id)) {
35108             return this.groups.key(brick_id);
35109         }
35110         
35111         return false;
35112     }
35113     
35114     
35115     
35116 });
35117
35118  /*
35119  * - LGPL
35120  *
35121  * element
35122  * 
35123  */
35124
35125 /**
35126  * @class Roo.bootstrap.Brick
35127  * @extends Roo.bootstrap.Component
35128  * Bootstrap Brick class
35129  * 
35130  * @constructor
35131  * Create a new Brick
35132  * @param {Object} config The config object
35133  */
35134
35135 Roo.bootstrap.Brick = function(config){
35136     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35137     
35138     this.addEvents({
35139         // raw events
35140         /**
35141          * @event click
35142          * When a Brick is click
35143          * @param {Roo.bootstrap.Brick} this
35144          * @param {Roo.EventObject} e
35145          */
35146         "click" : true
35147     });
35148 };
35149
35150 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
35151     
35152     /**
35153      * @cfg {String} title
35154      */   
35155     title : '',
35156     /**
35157      * @cfg {String} html
35158      */   
35159     html : '',
35160     /**
35161      * @cfg {String} bgimage
35162      */   
35163     bgimage : '',
35164     /**
35165      * @cfg {String} cls
35166      */   
35167     cls : '',
35168     /**
35169      * @cfg {String} href
35170      */   
35171     href : '',
35172     /**
35173      * @cfg {String} video
35174      */   
35175     video : '',
35176     /**
35177      * @cfg {Boolean} square
35178      */   
35179     square : true,
35180     
35181     getAutoCreate : function()
35182     {
35183         var cls = 'roo-brick';
35184         
35185         if(this.href.length){
35186             cls += ' roo-brick-link';
35187         }
35188         
35189         if(this.bgimage.length){
35190             cls += ' roo-brick-image';
35191         }
35192         
35193         if(!this.html.length && !this.bgimage.length){
35194             cls += ' roo-brick-center-title';
35195         }
35196         
35197         if(!this.html.length && this.bgimage.length){
35198             cls += ' roo-brick-bottom-title';
35199         }
35200         
35201         if(this.cls){
35202             cls += ' ' + this.cls;
35203         }
35204         
35205         var cfg = {
35206             tag: (this.href.length) ? 'a' : 'div',
35207             cls: cls,
35208             cn: [
35209                 {
35210                     tag: 'div',
35211                     cls: 'roo-brick-paragraph',
35212                     cn: []
35213                 }
35214             ]
35215         };
35216         
35217         if(this.href.length){
35218             cfg.href = this.href;
35219         }
35220         
35221         var cn = cfg.cn[0].cn;
35222         
35223         if(this.title.length){
35224             cn.push({
35225                 tag: 'h4',
35226                 cls: 'roo-brick-title',
35227                 html: this.title
35228             });
35229         }
35230         
35231         if(this.html.length){
35232             cn.push({
35233                 tag: 'p',
35234                 cls: 'roo-brick-text',
35235                 html: this.html
35236             });
35237         } else {
35238             cn.cls += ' hide';
35239         }
35240         
35241         if(this.bgimage.length){
35242             cfg.cn.push({
35243                 tag: 'img',
35244                 cls: 'roo-brick-image-view',
35245                 src: this.bgimage
35246             });
35247         }
35248         
35249         return cfg;
35250     },
35251     
35252     initEvents: function() 
35253     {
35254         if(this.title.length || this.html.length){
35255             this.el.on('mouseenter'  ,this.enter, this);
35256             this.el.on('mouseleave', this.leave, this);
35257         }
35258         
35259         Roo.EventManager.onWindowResize(this.resize, this); 
35260         
35261         if(this.bgimage.length){
35262             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35263             this.imageEl.on('load', this.onImageLoad, this);
35264             return;
35265         }
35266         
35267         this.resize();
35268     },
35269     
35270     onImageLoad : function()
35271     {
35272         this.resize();
35273     },
35274     
35275     resize : function()
35276     {
35277         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35278         
35279         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35280         
35281         if(this.bgimage.length){
35282             var image = this.el.select('.roo-brick-image-view', true).first();
35283             
35284             image.setWidth(paragraph.getWidth());
35285             
35286             if(this.square){
35287                 image.setHeight(paragraph.getWidth());
35288             }
35289             
35290             this.el.setHeight(image.getHeight());
35291             paragraph.setHeight(image.getHeight());
35292             
35293         }
35294         
35295     },
35296     
35297     enter: function(e, el)
35298     {
35299         e.preventDefault();
35300         
35301         if(this.bgimage.length){
35302             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35303             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35304         }
35305     },
35306     
35307     leave: function(e, el)
35308     {
35309         e.preventDefault();
35310         
35311         if(this.bgimage.length){
35312             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35313             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35314         }
35315     }
35316     
35317 });
35318
35319  
35320
35321  /*
35322  * - LGPL
35323  *
35324  * Number field 
35325  */
35326
35327 /**
35328  * @class Roo.bootstrap.NumberField
35329  * @extends Roo.bootstrap.Input
35330  * Bootstrap NumberField class
35331  * 
35332  * 
35333  * 
35334  * 
35335  * @constructor
35336  * Create a new NumberField
35337  * @param {Object} config The config object
35338  */
35339
35340 Roo.bootstrap.NumberField = function(config){
35341     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35342 };
35343
35344 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35345     
35346     /**
35347      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35348      */
35349     allowDecimals : true,
35350     /**
35351      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35352      */
35353     decimalSeparator : ".",
35354     /**
35355      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35356      */
35357     decimalPrecision : 2,
35358     /**
35359      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35360      */
35361     allowNegative : true,
35362     
35363     /**
35364      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
35365      */
35366     allowZero: true,
35367     /**
35368      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
35369      */
35370     minValue : Number.NEGATIVE_INFINITY,
35371     /**
35372      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
35373      */
35374     maxValue : Number.MAX_VALUE,
35375     /**
35376      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
35377      */
35378     minText : "The minimum value for this field is {0}",
35379     /**
35380      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
35381      */
35382     maxText : "The maximum value for this field is {0}",
35383     /**
35384      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
35385      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
35386      */
35387     nanText : "{0} is not a valid number",
35388     /**
35389      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
35390      */
35391     thousandsDelimiter : false,
35392     /**
35393      * @cfg {String} valueAlign alignment of value
35394      */
35395     valueAlign : "left",
35396
35397     getAutoCreate : function()
35398     {
35399         var hiddenInput = {
35400             tag: 'input',
35401             type: 'hidden',
35402             id: Roo.id(),
35403             cls: 'hidden-number-input'
35404         };
35405         
35406         if (this.name) {
35407             hiddenInput.name = this.name;
35408         }
35409         
35410         this.name = '';
35411         
35412         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
35413         
35414         this.name = hiddenInput.name;
35415         
35416         if(cfg.cn.length > 0) {
35417             cfg.cn.push(hiddenInput);
35418         }
35419         
35420         return cfg;
35421     },
35422
35423     // private
35424     initEvents : function()
35425     {   
35426         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
35427         
35428         var allowed = "0123456789";
35429         
35430         if(this.allowDecimals){
35431             allowed += this.decimalSeparator;
35432         }
35433         
35434         if(this.allowNegative){
35435             allowed += "-";
35436         }
35437         
35438         if(this.thousandsDelimiter) {
35439             allowed += ",";
35440         }
35441         
35442         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
35443         
35444         var keyPress = function(e){
35445             
35446             var k = e.getKey();
35447             
35448             var c = e.getCharCode();
35449             
35450             if(
35451                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
35452                     allowed.indexOf(String.fromCharCode(c)) === -1
35453             ){
35454                 e.stopEvent();
35455                 return;
35456             }
35457             
35458             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
35459                 return;
35460             }
35461             
35462             if(allowed.indexOf(String.fromCharCode(c)) === -1){
35463                 e.stopEvent();
35464             }
35465         };
35466         
35467         this.el.on("keypress", keyPress, this);
35468     },
35469     
35470     validateValue : function(value)
35471     {
35472         
35473         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
35474             return false;
35475         }
35476         
35477         var num = this.parseValue(value);
35478         
35479         if(isNaN(num)){
35480             this.markInvalid(String.format(this.nanText, value));
35481             return false;
35482         }
35483         
35484         if(num < this.minValue){
35485             this.markInvalid(String.format(this.minText, this.minValue));
35486             return false;
35487         }
35488         
35489         if(num > this.maxValue){
35490             this.markInvalid(String.format(this.maxText, this.maxValue));
35491             return false;
35492         }
35493         
35494         return true;
35495     },
35496
35497     getValue : function()
35498     {
35499         var v = this.hiddenEl().getValue();
35500         
35501         return this.fixPrecision(this.parseValue(v));
35502     },
35503
35504     parseValue : function(value)
35505     {
35506         if(this.thousandsDelimiter) {
35507             value += "";
35508             r = new RegExp(",", "g");
35509             value = value.replace(r, "");
35510         }
35511         
35512         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
35513         return isNaN(value) ? '' : value;
35514     },
35515
35516     fixPrecision : function(value)
35517     {
35518         if(this.thousandsDelimiter) {
35519             value += "";
35520             r = new RegExp(",", "g");
35521             value = value.replace(r, "");
35522         }
35523         
35524         var nan = isNaN(value);
35525         
35526         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
35527             return nan ? '' : value;
35528         }
35529         return parseFloat(value).toFixed(this.decimalPrecision);
35530     },
35531
35532     setValue : function(v)
35533     {
35534         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
35535         
35536         this.value = v;
35537         
35538         if(this.rendered){
35539             
35540             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
35541             
35542             this.inputEl().dom.value = (v == '') ? '' :
35543                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
35544             
35545             if(!this.allowZero && v === '0') {
35546                 this.hiddenEl().dom.value = '';
35547                 this.inputEl().dom.value = '';
35548             }
35549             
35550             this.validate();
35551         }
35552     },
35553
35554     decimalPrecisionFcn : function(v)
35555     {
35556         return Math.floor(v);
35557     },
35558
35559     beforeBlur : function()
35560     {
35561         var v = this.parseValue(this.getRawValue());
35562         
35563         if(v || v === 0 || v === ''){
35564             this.setValue(v);
35565         }
35566     },
35567     
35568     hiddenEl : function()
35569     {
35570         return this.el.select('input.hidden-number-input',true).first();
35571     }
35572     
35573 });
35574
35575  
35576
35577 /*
35578 * Licence: LGPL
35579 */
35580
35581 /**
35582  * @class Roo.bootstrap.DocumentSlider
35583  * @extends Roo.bootstrap.Component
35584  * Bootstrap DocumentSlider class
35585  * 
35586  * @constructor
35587  * Create a new DocumentViewer
35588  * @param {Object} config The config object
35589  */
35590
35591 Roo.bootstrap.DocumentSlider = function(config){
35592     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
35593     
35594     this.files = [];
35595     
35596     this.addEvents({
35597         /**
35598          * @event initial
35599          * Fire after initEvent
35600          * @param {Roo.bootstrap.DocumentSlider} this
35601          */
35602         "initial" : true,
35603         /**
35604          * @event update
35605          * Fire after update
35606          * @param {Roo.bootstrap.DocumentSlider} this
35607          */
35608         "update" : true,
35609         /**
35610          * @event click
35611          * Fire after click
35612          * @param {Roo.bootstrap.DocumentSlider} this
35613          */
35614         "click" : true
35615     });
35616 };
35617
35618 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
35619     
35620     files : false,
35621     
35622     indicator : 0,
35623     
35624     getAutoCreate : function()
35625     {
35626         var cfg = {
35627             tag : 'div',
35628             cls : 'roo-document-slider',
35629             cn : [
35630                 {
35631                     tag : 'div',
35632                     cls : 'roo-document-slider-header',
35633                     cn : [
35634                         {
35635                             tag : 'div',
35636                             cls : 'roo-document-slider-header-title'
35637                         }
35638                     ]
35639                 },
35640                 {
35641                     tag : 'div',
35642                     cls : 'roo-document-slider-body',
35643                     cn : [
35644                         {
35645                             tag : 'div',
35646                             cls : 'roo-document-slider-prev',
35647                             cn : [
35648                                 {
35649                                     tag : 'i',
35650                                     cls : 'fa fa-chevron-left'
35651                                 }
35652                             ]
35653                         },
35654                         {
35655                             tag : 'div',
35656                             cls : 'roo-document-slider-thumb',
35657                             cn : [
35658                                 {
35659                                     tag : 'img',
35660                                     cls : 'roo-document-slider-image'
35661                                 }
35662                             ]
35663                         },
35664                         {
35665                             tag : 'div',
35666                             cls : 'roo-document-slider-next',
35667                             cn : [
35668                                 {
35669                                     tag : 'i',
35670                                     cls : 'fa fa-chevron-right'
35671                                 }
35672                             ]
35673                         }
35674                     ]
35675                 }
35676             ]
35677         };
35678         
35679         return cfg;
35680     },
35681     
35682     initEvents : function()
35683     {
35684         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
35685         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
35686         
35687         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
35688         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
35689         
35690         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
35691         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
35692         
35693         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
35694         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
35695         
35696         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
35697         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
35698         
35699         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
35700         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
35701         
35702         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
35703         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
35704         
35705         this.thumbEl.on('click', this.onClick, this);
35706         
35707         this.prevIndicator.on('click', this.prev, this);
35708         
35709         this.nextIndicator.on('click', this.next, this);
35710         
35711     },
35712     
35713     initial : function()
35714     {
35715         if(this.files.length){
35716             this.indicator = 1;
35717             this.update()
35718         }
35719         
35720         this.fireEvent('initial', this);
35721     },
35722     
35723     update : function()
35724     {
35725         this.imageEl.attr('src', this.files[this.indicator - 1]);
35726         
35727         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
35728         
35729         this.prevIndicator.show();
35730         
35731         if(this.indicator == 1){
35732             this.prevIndicator.hide();
35733         }
35734         
35735         this.nextIndicator.show();
35736         
35737         if(this.indicator == this.files.length){
35738             this.nextIndicator.hide();
35739         }
35740         
35741         this.thumbEl.scrollTo('top');
35742         
35743         this.fireEvent('update', this);
35744     },
35745     
35746     onClick : function(e)
35747     {
35748         e.preventDefault();
35749         
35750         this.fireEvent('click', this);
35751     },
35752     
35753     prev : function(e)
35754     {
35755         e.preventDefault();
35756         
35757         this.indicator = Math.max(1, this.indicator - 1);
35758         
35759         this.update();
35760     },
35761     
35762     next : function(e)
35763     {
35764         e.preventDefault();
35765         
35766         this.indicator = Math.min(this.files.length, this.indicator + 1);
35767         
35768         this.update();
35769     }
35770 });
35771 /*
35772  * - LGPL
35773  *
35774  * RadioSet
35775  *
35776  *
35777  */
35778
35779 /**
35780  * @class Roo.bootstrap.RadioSet
35781  * @extends Roo.bootstrap.Input
35782  * Bootstrap RadioSet class
35783  * @cfg {String} indicatorpos (left|right) default left
35784  * @cfg {Boolean} inline (true|false) inline the element (default true)
35785  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
35786  * @constructor
35787  * Create a new RadioSet
35788  * @param {Object} config The config object
35789  */
35790
35791 Roo.bootstrap.RadioSet = function(config){
35792     
35793     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
35794     
35795     this.radioes = [];
35796     
35797     Roo.bootstrap.RadioSet.register(this);
35798     
35799     this.addEvents({
35800         /**
35801         * @event check
35802         * Fires when the element is checked or unchecked.
35803         * @param {Roo.bootstrap.RadioSet} this This radio
35804         * @param {Roo.bootstrap.Radio} item The checked item
35805         */
35806        check : true,
35807        /**
35808         * @event click
35809         * Fires when the element is click.
35810         * @param {Roo.bootstrap.RadioSet} this This radio set
35811         * @param {Roo.bootstrap.Radio} item The checked item
35812         * @param {Roo.EventObject} e The event object
35813         */
35814        click : true
35815     });
35816     
35817 };
35818
35819 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
35820
35821     radioes : false,
35822     
35823     inline : true,
35824     
35825     weight : '',
35826     
35827     indicatorpos : 'left',
35828     
35829     getAutoCreate : function()
35830     {
35831         var label = {
35832             tag : 'label',
35833             cls : 'roo-radio-set-label',
35834             cn : [
35835                 {
35836                     tag : 'span',
35837                     html : this.fieldLabel
35838                 }
35839             ]
35840         };
35841         if (Roo.bootstrap.version == 3) {
35842             
35843             
35844             if(this.indicatorpos == 'left'){
35845                 label.cn.unshift({
35846                     tag : 'i',
35847                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
35848                     tooltip : 'This field is required'
35849                 });
35850             } else {
35851                 label.cn.push({
35852                     tag : 'i',
35853                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
35854                     tooltip : 'This field is required'
35855                 });
35856             }
35857         }
35858         var items = {
35859             tag : 'div',
35860             cls : 'roo-radio-set-items'
35861         };
35862         
35863         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
35864         
35865         if (align === 'left' && this.fieldLabel.length) {
35866             
35867             items = {
35868                 cls : "roo-radio-set-right", 
35869                 cn: [
35870                     items
35871                 ]
35872             };
35873             
35874             if(this.labelWidth > 12){
35875                 label.style = "width: " + this.labelWidth + 'px';
35876             }
35877             
35878             if(this.labelWidth < 13 && this.labelmd == 0){
35879                 this.labelmd = this.labelWidth;
35880             }
35881             
35882             if(this.labellg > 0){
35883                 label.cls += ' col-lg-' + this.labellg;
35884                 items.cls += ' col-lg-' + (12 - this.labellg);
35885             }
35886             
35887             if(this.labelmd > 0){
35888                 label.cls += ' col-md-' + this.labelmd;
35889                 items.cls += ' col-md-' + (12 - this.labelmd);
35890             }
35891             
35892             if(this.labelsm > 0){
35893                 label.cls += ' col-sm-' + this.labelsm;
35894                 items.cls += ' col-sm-' + (12 - this.labelsm);
35895             }
35896             
35897             if(this.labelxs > 0){
35898                 label.cls += ' col-xs-' + this.labelxs;
35899                 items.cls += ' col-xs-' + (12 - this.labelxs);
35900             }
35901         }
35902         
35903         var cfg = {
35904             tag : 'div',
35905             cls : 'roo-radio-set',
35906             cn : [
35907                 {
35908                     tag : 'input',
35909                     cls : 'roo-radio-set-input',
35910                     type : 'hidden',
35911                     name : this.name,
35912                     value : this.value ? this.value :  ''
35913                 },
35914                 label,
35915                 items
35916             ]
35917         };
35918         
35919         if(this.weight.length){
35920             cfg.cls += ' roo-radio-' + this.weight;
35921         }
35922         
35923         if(this.inline) {
35924             cfg.cls += ' roo-radio-set-inline';
35925         }
35926         
35927         var settings=this;
35928         ['xs','sm','md','lg'].map(function(size){
35929             if (settings[size]) {
35930                 cfg.cls += ' col-' + size + '-' + settings[size];
35931             }
35932         });
35933         
35934         return cfg;
35935         
35936     },
35937
35938     initEvents : function()
35939     {
35940         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
35941         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
35942         
35943         if(!this.fieldLabel.length){
35944             this.labelEl.hide();
35945         }
35946         
35947         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
35948         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
35949         
35950         this.indicator = this.indicatorEl();
35951         
35952         if(this.indicator){
35953             this.indicator.addClass('invisible');
35954         }
35955         
35956         this.originalValue = this.getValue();
35957         
35958     },
35959     
35960     inputEl: function ()
35961     {
35962         return this.el.select('.roo-radio-set-input', true).first();
35963     },
35964     
35965     getChildContainer : function()
35966     {
35967         return this.itemsEl;
35968     },
35969     
35970     register : function(item)
35971     {
35972         this.radioes.push(item);
35973         
35974     },
35975     
35976     validate : function()
35977     {   
35978         if(this.getVisibilityEl().hasClass('hidden')){
35979             return true;
35980         }
35981         
35982         var valid = false;
35983         
35984         Roo.each(this.radioes, function(i){
35985             if(!i.checked){
35986                 return;
35987             }
35988             
35989             valid = true;
35990             return false;
35991         });
35992         
35993         if(this.allowBlank) {
35994             return true;
35995         }
35996         
35997         if(this.disabled || valid){
35998             this.markValid();
35999             return true;
36000         }
36001         
36002         this.markInvalid();
36003         return false;
36004         
36005     },
36006     
36007     markValid : function()
36008     {
36009         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36010             this.indicatorEl().removeClass('visible');
36011             this.indicatorEl().addClass('invisible');
36012         }
36013         
36014         
36015         if (Roo.bootstrap.version == 3) {
36016             this.el.removeClass([this.invalidClass, this.validClass]);
36017             this.el.addClass(this.validClass);
36018         } else {
36019             this.el.removeClass(['is-invalid','is-valid']);
36020             this.el.addClass(['is-valid']);
36021         }
36022         this.fireEvent('valid', this);
36023     },
36024     
36025     markInvalid : function(msg)
36026     {
36027         if(this.allowBlank || this.disabled){
36028             return;
36029         }
36030         
36031         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36032             this.indicatorEl().removeClass('invisible');
36033             this.indicatorEl().addClass('visible');
36034         }
36035         if (Roo.bootstrap.version == 3) {
36036             this.el.removeClass([this.invalidClass, this.validClass]);
36037             this.el.addClass(this.invalidClass);
36038         } else {
36039             this.el.removeClass(['is-invalid','is-valid']);
36040             this.el.addClass(['is-invalid']);
36041         }
36042         
36043         this.fireEvent('invalid', this, msg);
36044         
36045     },
36046     
36047     setValue : function(v, suppressEvent)
36048     {   
36049         if(this.value === v){
36050             return;
36051         }
36052         
36053         this.value = v;
36054         
36055         if(this.rendered){
36056             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36057         }
36058         
36059         Roo.each(this.radioes, function(i){
36060             i.checked = false;
36061             i.el.removeClass('checked');
36062         });
36063         
36064         Roo.each(this.radioes, function(i){
36065             
36066             if(i.value === v || i.value.toString() === v.toString()){
36067                 i.checked = true;
36068                 i.el.addClass('checked');
36069                 
36070                 if(suppressEvent !== true){
36071                     this.fireEvent('check', this, i);
36072                 }
36073                 
36074                 return false;
36075             }
36076             
36077         }, this);
36078         
36079         this.validate();
36080     },
36081     
36082     clearInvalid : function(){
36083         
36084         if(!this.el || this.preventMark){
36085             return;
36086         }
36087         
36088         this.el.removeClass([this.invalidClass]);
36089         
36090         this.fireEvent('valid', this);
36091     }
36092     
36093 });
36094
36095 Roo.apply(Roo.bootstrap.RadioSet, {
36096     
36097     groups: {},
36098     
36099     register : function(set)
36100     {
36101         this.groups[set.name] = set;
36102     },
36103     
36104     get: function(name) 
36105     {
36106         if (typeof(this.groups[name]) == 'undefined') {
36107             return false;
36108         }
36109         
36110         return this.groups[name] ;
36111     }
36112     
36113 });
36114 /*
36115  * Based on:
36116  * Ext JS Library 1.1.1
36117  * Copyright(c) 2006-2007, Ext JS, LLC.
36118  *
36119  * Originally Released Under LGPL - original licence link has changed is not relivant.
36120  *
36121  * Fork - LGPL
36122  * <script type="text/javascript">
36123  */
36124
36125
36126 /**
36127  * @class Roo.bootstrap.SplitBar
36128  * @extends Roo.util.Observable
36129  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36130  * <br><br>
36131  * Usage:
36132  * <pre><code>
36133 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36134                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36135 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36136 split.minSize = 100;
36137 split.maxSize = 600;
36138 split.animate = true;
36139 split.on('moved', splitterMoved);
36140 </code></pre>
36141  * @constructor
36142  * Create a new SplitBar
36143  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
36144  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
36145  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36146  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
36147                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36148                         position of the SplitBar).
36149  */
36150 Roo.bootstrap.SplitBar = function(cfg){
36151     
36152     /** @private */
36153     
36154     //{
36155     //  dragElement : elm
36156     //  resizingElement: el,
36157         // optional..
36158     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36159     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
36160         // existingProxy ???
36161     //}
36162     
36163     this.el = Roo.get(cfg.dragElement, true);
36164     this.el.dom.unselectable = "on";
36165     /** @private */
36166     this.resizingEl = Roo.get(cfg.resizingElement, true);
36167
36168     /**
36169      * @private
36170      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36171      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36172      * @type Number
36173      */
36174     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36175     
36176     /**
36177      * The minimum size of the resizing element. (Defaults to 0)
36178      * @type Number
36179      */
36180     this.minSize = 0;
36181     
36182     /**
36183      * The maximum size of the resizing element. (Defaults to 2000)
36184      * @type Number
36185      */
36186     this.maxSize = 2000;
36187     
36188     /**
36189      * Whether to animate the transition to the new size
36190      * @type Boolean
36191      */
36192     this.animate = false;
36193     
36194     /**
36195      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36196      * @type Boolean
36197      */
36198     this.useShim = false;
36199     
36200     /** @private */
36201     this.shim = null;
36202     
36203     if(!cfg.existingProxy){
36204         /** @private */
36205         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36206     }else{
36207         this.proxy = Roo.get(cfg.existingProxy).dom;
36208     }
36209     /** @private */
36210     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36211     
36212     /** @private */
36213     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36214     
36215     /** @private */
36216     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36217     
36218     /** @private */
36219     this.dragSpecs = {};
36220     
36221     /**
36222      * @private The adapter to use to positon and resize elements
36223      */
36224     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36225     this.adapter.init(this);
36226     
36227     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36228         /** @private */
36229         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36230         this.el.addClass("roo-splitbar-h");
36231     }else{
36232         /** @private */
36233         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36234         this.el.addClass("roo-splitbar-v");
36235     }
36236     
36237     this.addEvents({
36238         /**
36239          * @event resize
36240          * Fires when the splitter is moved (alias for {@link #event-moved})
36241          * @param {Roo.bootstrap.SplitBar} this
36242          * @param {Number} newSize the new width or height
36243          */
36244         "resize" : true,
36245         /**
36246          * @event moved
36247          * Fires when the splitter is moved
36248          * @param {Roo.bootstrap.SplitBar} this
36249          * @param {Number} newSize the new width or height
36250          */
36251         "moved" : true,
36252         /**
36253          * @event beforeresize
36254          * Fires before the splitter is dragged
36255          * @param {Roo.bootstrap.SplitBar} this
36256          */
36257         "beforeresize" : true,
36258
36259         "beforeapply" : true
36260     });
36261
36262     Roo.util.Observable.call(this);
36263 };
36264
36265 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36266     onStartProxyDrag : function(x, y){
36267         this.fireEvent("beforeresize", this);
36268         if(!this.overlay){
36269             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
36270             o.unselectable();
36271             o.enableDisplayMode("block");
36272             // all splitbars share the same overlay
36273             Roo.bootstrap.SplitBar.prototype.overlay = o;
36274         }
36275         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36276         this.overlay.show();
36277         Roo.get(this.proxy).setDisplayed("block");
36278         var size = this.adapter.getElementSize(this);
36279         this.activeMinSize = this.getMinimumSize();;
36280         this.activeMaxSize = this.getMaximumSize();;
36281         var c1 = size - this.activeMinSize;
36282         var c2 = Math.max(this.activeMaxSize - size, 0);
36283         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36284             this.dd.resetConstraints();
36285             this.dd.setXConstraint(
36286                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
36287                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36288             );
36289             this.dd.setYConstraint(0, 0);
36290         }else{
36291             this.dd.resetConstraints();
36292             this.dd.setXConstraint(0, 0);
36293             this.dd.setYConstraint(
36294                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
36295                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36296             );
36297          }
36298         this.dragSpecs.startSize = size;
36299         this.dragSpecs.startPoint = [x, y];
36300         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36301     },
36302     
36303     /** 
36304      * @private Called after the drag operation by the DDProxy
36305      */
36306     onEndProxyDrag : function(e){
36307         Roo.get(this.proxy).setDisplayed(false);
36308         var endPoint = Roo.lib.Event.getXY(e);
36309         if(this.overlay){
36310             this.overlay.hide();
36311         }
36312         var newSize;
36313         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36314             newSize = this.dragSpecs.startSize + 
36315                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36316                     endPoint[0] - this.dragSpecs.startPoint[0] :
36317                     this.dragSpecs.startPoint[0] - endPoint[0]
36318                 );
36319         }else{
36320             newSize = this.dragSpecs.startSize + 
36321                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36322                     endPoint[1] - this.dragSpecs.startPoint[1] :
36323                     this.dragSpecs.startPoint[1] - endPoint[1]
36324                 );
36325         }
36326         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36327         if(newSize != this.dragSpecs.startSize){
36328             if(this.fireEvent('beforeapply', this, newSize) !== false){
36329                 this.adapter.setElementSize(this, newSize);
36330                 this.fireEvent("moved", this, newSize);
36331                 this.fireEvent("resize", this, newSize);
36332             }
36333         }
36334     },
36335     
36336     /**
36337      * Get the adapter this SplitBar uses
36338      * @return The adapter object
36339      */
36340     getAdapter : function(){
36341         return this.adapter;
36342     },
36343     
36344     /**
36345      * Set the adapter this SplitBar uses
36346      * @param {Object} adapter A SplitBar adapter object
36347      */
36348     setAdapter : function(adapter){
36349         this.adapter = adapter;
36350         this.adapter.init(this);
36351     },
36352     
36353     /**
36354      * Gets the minimum size for the resizing element
36355      * @return {Number} The minimum size
36356      */
36357     getMinimumSize : function(){
36358         return this.minSize;
36359     },
36360     
36361     /**
36362      * Sets the minimum size for the resizing element
36363      * @param {Number} minSize The minimum size
36364      */
36365     setMinimumSize : function(minSize){
36366         this.minSize = minSize;
36367     },
36368     
36369     /**
36370      * Gets the maximum size for the resizing element
36371      * @return {Number} The maximum size
36372      */
36373     getMaximumSize : function(){
36374         return this.maxSize;
36375     },
36376     
36377     /**
36378      * Sets the maximum size for the resizing element
36379      * @param {Number} maxSize The maximum size
36380      */
36381     setMaximumSize : function(maxSize){
36382         this.maxSize = maxSize;
36383     },
36384     
36385     /**
36386      * Sets the initialize size for the resizing element
36387      * @param {Number} size The initial size
36388      */
36389     setCurrentSize : function(size){
36390         var oldAnimate = this.animate;
36391         this.animate = false;
36392         this.adapter.setElementSize(this, size);
36393         this.animate = oldAnimate;
36394     },
36395     
36396     /**
36397      * Destroy this splitbar. 
36398      * @param {Boolean} removeEl True to remove the element
36399      */
36400     destroy : function(removeEl){
36401         if(this.shim){
36402             this.shim.remove();
36403         }
36404         this.dd.unreg();
36405         this.proxy.parentNode.removeChild(this.proxy);
36406         if(removeEl){
36407             this.el.remove();
36408         }
36409     }
36410 });
36411
36412 /**
36413  * @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.
36414  */
36415 Roo.bootstrap.SplitBar.createProxy = function(dir){
36416     var proxy = new Roo.Element(document.createElement("div"));
36417     proxy.unselectable();
36418     var cls = 'roo-splitbar-proxy';
36419     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
36420     document.body.appendChild(proxy.dom);
36421     return proxy.dom;
36422 };
36423
36424 /** 
36425  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
36426  * Default Adapter. It assumes the splitter and resizing element are not positioned
36427  * elements and only gets/sets the width of the element. Generally used for table based layouts.
36428  */
36429 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
36430 };
36431
36432 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
36433     // do nothing for now
36434     init : function(s){
36435     
36436     },
36437     /**
36438      * Called before drag operations to get the current size of the resizing element. 
36439      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36440      */
36441      getElementSize : function(s){
36442         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36443             return s.resizingEl.getWidth();
36444         }else{
36445             return s.resizingEl.getHeight();
36446         }
36447     },
36448     
36449     /**
36450      * Called after drag operations to set the size of the resizing element.
36451      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36452      * @param {Number} newSize The new size to set
36453      * @param {Function} onComplete A function to be invoked when resizing is complete
36454      */
36455     setElementSize : function(s, newSize, onComplete){
36456         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36457             if(!s.animate){
36458                 s.resizingEl.setWidth(newSize);
36459                 if(onComplete){
36460                     onComplete(s, newSize);
36461                 }
36462             }else{
36463                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
36464             }
36465         }else{
36466             
36467             if(!s.animate){
36468                 s.resizingEl.setHeight(newSize);
36469                 if(onComplete){
36470                     onComplete(s, newSize);
36471                 }
36472             }else{
36473                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
36474             }
36475         }
36476     }
36477 };
36478
36479 /** 
36480  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
36481  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
36482  * Adapter that  moves the splitter element to align with the resized sizing element. 
36483  * Used with an absolute positioned SplitBar.
36484  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
36485  * document.body, make sure you assign an id to the body element.
36486  */
36487 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
36488     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36489     this.container = Roo.get(container);
36490 };
36491
36492 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
36493     init : function(s){
36494         this.basic.init(s);
36495     },
36496     
36497     getElementSize : function(s){
36498         return this.basic.getElementSize(s);
36499     },
36500     
36501     setElementSize : function(s, newSize, onComplete){
36502         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
36503     },
36504     
36505     moveSplitter : function(s){
36506         var yes = Roo.bootstrap.SplitBar;
36507         switch(s.placement){
36508             case yes.LEFT:
36509                 s.el.setX(s.resizingEl.getRight());
36510                 break;
36511             case yes.RIGHT:
36512                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
36513                 break;
36514             case yes.TOP:
36515                 s.el.setY(s.resizingEl.getBottom());
36516                 break;
36517             case yes.BOTTOM:
36518                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
36519                 break;
36520         }
36521     }
36522 };
36523
36524 /**
36525  * Orientation constant - Create a vertical SplitBar
36526  * @static
36527  * @type Number
36528  */
36529 Roo.bootstrap.SplitBar.VERTICAL = 1;
36530
36531 /**
36532  * Orientation constant - Create a horizontal SplitBar
36533  * @static
36534  * @type Number
36535  */
36536 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
36537
36538 /**
36539  * Placement constant - The resizing element is to the left of the splitter element
36540  * @static
36541  * @type Number
36542  */
36543 Roo.bootstrap.SplitBar.LEFT = 1;
36544
36545 /**
36546  * Placement constant - The resizing element is to the right of the splitter element
36547  * @static
36548  * @type Number
36549  */
36550 Roo.bootstrap.SplitBar.RIGHT = 2;
36551
36552 /**
36553  * Placement constant - The resizing element is positioned above the splitter element
36554  * @static
36555  * @type Number
36556  */
36557 Roo.bootstrap.SplitBar.TOP = 3;
36558
36559 /**
36560  * Placement constant - The resizing element is positioned under splitter element
36561  * @static
36562  * @type Number
36563  */
36564 Roo.bootstrap.SplitBar.BOTTOM = 4;
36565 Roo.namespace("Roo.bootstrap.layout");/*
36566  * Based on:
36567  * Ext JS Library 1.1.1
36568  * Copyright(c) 2006-2007, Ext JS, LLC.
36569  *
36570  * Originally Released Under LGPL - original licence link has changed is not relivant.
36571  *
36572  * Fork - LGPL
36573  * <script type="text/javascript">
36574  */
36575
36576 /**
36577  * @class Roo.bootstrap.layout.Manager
36578  * @extends Roo.bootstrap.Component
36579  * Base class for layout managers.
36580  */
36581 Roo.bootstrap.layout.Manager = function(config)
36582 {
36583     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
36584
36585
36586
36587
36588
36589     /** false to disable window resize monitoring @type Boolean */
36590     this.monitorWindowResize = true;
36591     this.regions = {};
36592     this.addEvents({
36593         /**
36594          * @event layout
36595          * Fires when a layout is performed.
36596          * @param {Roo.LayoutManager} this
36597          */
36598         "layout" : true,
36599         /**
36600          * @event regionresized
36601          * Fires when the user resizes a region.
36602          * @param {Roo.LayoutRegion} region The resized region
36603          * @param {Number} newSize The new size (width for east/west, height for north/south)
36604          */
36605         "regionresized" : true,
36606         /**
36607          * @event regioncollapsed
36608          * Fires when a region is collapsed.
36609          * @param {Roo.LayoutRegion} region The collapsed region
36610          */
36611         "regioncollapsed" : true,
36612         /**
36613          * @event regionexpanded
36614          * Fires when a region is expanded.
36615          * @param {Roo.LayoutRegion} region The expanded region
36616          */
36617         "regionexpanded" : true
36618     });
36619     this.updating = false;
36620
36621     if (config.el) {
36622         this.el = Roo.get(config.el);
36623         this.initEvents();
36624     }
36625
36626 };
36627
36628 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
36629
36630
36631     regions : null,
36632
36633     monitorWindowResize : true,
36634
36635
36636     updating : false,
36637
36638
36639     onRender : function(ct, position)
36640     {
36641         if(!this.el){
36642             this.el = Roo.get(ct);
36643             this.initEvents();
36644         }
36645         //this.fireEvent('render',this);
36646     },
36647
36648
36649     initEvents: function()
36650     {
36651
36652
36653         // ie scrollbar fix
36654         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
36655             document.body.scroll = "no";
36656         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
36657             this.el.position('relative');
36658         }
36659         this.id = this.el.id;
36660         this.el.addClass("roo-layout-container");
36661         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
36662         if(this.el.dom != document.body ) {
36663             this.el.on('resize', this.layout,this);
36664             this.el.on('show', this.layout,this);
36665         }
36666
36667     },
36668
36669     /**
36670      * Returns true if this layout is currently being updated
36671      * @return {Boolean}
36672      */
36673     isUpdating : function(){
36674         return this.updating;
36675     },
36676
36677     /**
36678      * Suspend the LayoutManager from doing auto-layouts while
36679      * making multiple add or remove calls
36680      */
36681     beginUpdate : function(){
36682         this.updating = true;
36683     },
36684
36685     /**
36686      * Restore auto-layouts and optionally disable the manager from performing a layout
36687      * @param {Boolean} noLayout true to disable a layout update
36688      */
36689     endUpdate : function(noLayout){
36690         this.updating = false;
36691         if(!noLayout){
36692             this.layout();
36693         }
36694     },
36695
36696     layout: function(){
36697         // abstract...
36698     },
36699
36700     onRegionResized : function(region, newSize){
36701         this.fireEvent("regionresized", region, newSize);
36702         this.layout();
36703     },
36704
36705     onRegionCollapsed : function(region){
36706         this.fireEvent("regioncollapsed", region);
36707     },
36708
36709     onRegionExpanded : function(region){
36710         this.fireEvent("regionexpanded", region);
36711     },
36712
36713     /**
36714      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
36715      * performs box-model adjustments.
36716      * @return {Object} The size as an object {width: (the width), height: (the height)}
36717      */
36718     getViewSize : function()
36719     {
36720         var size;
36721         if(this.el.dom != document.body){
36722             size = this.el.getSize();
36723         }else{
36724             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
36725         }
36726         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
36727         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
36728         return size;
36729     },
36730
36731     /**
36732      * Returns the Element this layout is bound to.
36733      * @return {Roo.Element}
36734      */
36735     getEl : function(){
36736         return this.el;
36737     },
36738
36739     /**
36740      * Returns the specified region.
36741      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
36742      * @return {Roo.LayoutRegion}
36743      */
36744     getRegion : function(target){
36745         return this.regions[target.toLowerCase()];
36746     },
36747
36748     onWindowResize : function(){
36749         if(this.monitorWindowResize){
36750             this.layout();
36751         }
36752     }
36753 });
36754 /*
36755  * Based on:
36756  * Ext JS Library 1.1.1
36757  * Copyright(c) 2006-2007, Ext JS, LLC.
36758  *
36759  * Originally Released Under LGPL - original licence link has changed is not relivant.
36760  *
36761  * Fork - LGPL
36762  * <script type="text/javascript">
36763  */
36764 /**
36765  * @class Roo.bootstrap.layout.Border
36766  * @extends Roo.bootstrap.layout.Manager
36767  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
36768  * please see: examples/bootstrap/nested.html<br><br>
36769  
36770 <b>The container the layout is rendered into can be either the body element or any other element.
36771 If it is not the body element, the container needs to either be an absolute positioned element,
36772 or you will need to add "position:relative" to the css of the container.  You will also need to specify
36773 the container size if it is not the body element.</b>
36774
36775 * @constructor
36776 * Create a new Border
36777 * @param {Object} config Configuration options
36778  */
36779 Roo.bootstrap.layout.Border = function(config){
36780     config = config || {};
36781     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
36782     
36783     
36784     
36785     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
36786         if(config[region]){
36787             config[region].region = region;
36788             this.addRegion(config[region]);
36789         }
36790     },this);
36791     
36792 };
36793
36794 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
36795
36796 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
36797     
36798     parent : false, // this might point to a 'nest' or a ???
36799     
36800     /**
36801      * Creates and adds a new region if it doesn't already exist.
36802      * @param {String} target The target region key (north, south, east, west or center).
36803      * @param {Object} config The regions config object
36804      * @return {BorderLayoutRegion} The new region
36805      */
36806     addRegion : function(config)
36807     {
36808         if(!this.regions[config.region]){
36809             var r = this.factory(config);
36810             this.bindRegion(r);
36811         }
36812         return this.regions[config.region];
36813     },
36814
36815     // private (kinda)
36816     bindRegion : function(r){
36817         this.regions[r.config.region] = r;
36818         
36819         r.on("visibilitychange",    this.layout, this);
36820         r.on("paneladded",          this.layout, this);
36821         r.on("panelremoved",        this.layout, this);
36822         r.on("invalidated",         this.layout, this);
36823         r.on("resized",             this.onRegionResized, this);
36824         r.on("collapsed",           this.onRegionCollapsed, this);
36825         r.on("expanded",            this.onRegionExpanded, this);
36826     },
36827
36828     /**
36829      * Performs a layout update.
36830      */
36831     layout : function()
36832     {
36833         if(this.updating) {
36834             return;
36835         }
36836         
36837         // render all the rebions if they have not been done alreayd?
36838         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
36839             if(this.regions[region] && !this.regions[region].bodyEl){
36840                 this.regions[region].onRender(this.el)
36841             }
36842         },this);
36843         
36844         var size = this.getViewSize();
36845         var w = size.width;
36846         var h = size.height;
36847         var centerW = w;
36848         var centerH = h;
36849         var centerY = 0;
36850         var centerX = 0;
36851         //var x = 0, y = 0;
36852
36853         var rs = this.regions;
36854         var north = rs["north"];
36855         var south = rs["south"]; 
36856         var west = rs["west"];
36857         var east = rs["east"];
36858         var center = rs["center"];
36859         //if(this.hideOnLayout){ // not supported anymore
36860             //c.el.setStyle("display", "none");
36861         //}
36862         if(north && north.isVisible()){
36863             var b = north.getBox();
36864             var m = north.getMargins();
36865             b.width = w - (m.left+m.right);
36866             b.x = m.left;
36867             b.y = m.top;
36868             centerY = b.height + b.y + m.bottom;
36869             centerH -= centerY;
36870             north.updateBox(this.safeBox(b));
36871         }
36872         if(south && south.isVisible()){
36873             var b = south.getBox();
36874             var m = south.getMargins();
36875             b.width = w - (m.left+m.right);
36876             b.x = m.left;
36877             var totalHeight = (b.height + m.top + m.bottom);
36878             b.y = h - totalHeight + m.top;
36879             centerH -= totalHeight;
36880             south.updateBox(this.safeBox(b));
36881         }
36882         if(west && west.isVisible()){
36883             var b = west.getBox();
36884             var m = west.getMargins();
36885             b.height = centerH - (m.top+m.bottom);
36886             b.x = m.left;
36887             b.y = centerY + m.top;
36888             var totalWidth = (b.width + m.left + m.right);
36889             centerX += totalWidth;
36890             centerW -= totalWidth;
36891             west.updateBox(this.safeBox(b));
36892         }
36893         if(east && east.isVisible()){
36894             var b = east.getBox();
36895             var m = east.getMargins();
36896             b.height = centerH - (m.top+m.bottom);
36897             var totalWidth = (b.width + m.left + m.right);
36898             b.x = w - totalWidth + m.left;
36899             b.y = centerY + m.top;
36900             centerW -= totalWidth;
36901             east.updateBox(this.safeBox(b));
36902         }
36903         if(center){
36904             var m = center.getMargins();
36905             var centerBox = {
36906                 x: centerX + m.left,
36907                 y: centerY + m.top,
36908                 width: centerW - (m.left+m.right),
36909                 height: centerH - (m.top+m.bottom)
36910             };
36911             //if(this.hideOnLayout){
36912                 //center.el.setStyle("display", "block");
36913             //}
36914             center.updateBox(this.safeBox(centerBox));
36915         }
36916         this.el.repaint();
36917         this.fireEvent("layout", this);
36918     },
36919
36920     // private
36921     safeBox : function(box){
36922         box.width = Math.max(0, box.width);
36923         box.height = Math.max(0, box.height);
36924         return box;
36925     },
36926
36927     /**
36928      * Adds a ContentPanel (or subclass) to this layout.
36929      * @param {String} target The target region key (north, south, east, west or center).
36930      * @param {Roo.ContentPanel} panel The panel to add
36931      * @return {Roo.ContentPanel} The added panel
36932      */
36933     add : function(target, panel){
36934          
36935         target = target.toLowerCase();
36936         return this.regions[target].add(panel);
36937     },
36938
36939     /**
36940      * Remove a ContentPanel (or subclass) to this layout.
36941      * @param {String} target The target region key (north, south, east, west or center).
36942      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
36943      * @return {Roo.ContentPanel} The removed panel
36944      */
36945     remove : function(target, panel){
36946         target = target.toLowerCase();
36947         return this.regions[target].remove(panel);
36948     },
36949
36950     /**
36951      * Searches all regions for a panel with the specified id
36952      * @param {String} panelId
36953      * @return {Roo.ContentPanel} The panel or null if it wasn't found
36954      */
36955     findPanel : function(panelId){
36956         var rs = this.regions;
36957         for(var target in rs){
36958             if(typeof rs[target] != "function"){
36959                 var p = rs[target].getPanel(panelId);
36960                 if(p){
36961                     return p;
36962                 }
36963             }
36964         }
36965         return null;
36966     },
36967
36968     /**
36969      * Searches all regions for a panel with the specified id and activates (shows) it.
36970      * @param {String/ContentPanel} panelId The panels id or the panel itself
36971      * @return {Roo.ContentPanel} The shown panel or null
36972      */
36973     showPanel : function(panelId) {
36974       var rs = this.regions;
36975       for(var target in rs){
36976          var r = rs[target];
36977          if(typeof r != "function"){
36978             if(r.hasPanel(panelId)){
36979                return r.showPanel(panelId);
36980             }
36981          }
36982       }
36983       return null;
36984    },
36985
36986    /**
36987      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
36988      * @param {Roo.state.Provider} provider (optional) An alternate state provider
36989      */
36990    /*
36991     restoreState : function(provider){
36992         if(!provider){
36993             provider = Roo.state.Manager;
36994         }
36995         var sm = new Roo.LayoutStateManager();
36996         sm.init(this, provider);
36997     },
36998 */
36999  
37000  
37001     /**
37002      * Adds a xtype elements to the layout.
37003      * <pre><code>
37004
37005 layout.addxtype({
37006        xtype : 'ContentPanel',
37007        region: 'west',
37008        items: [ .... ]
37009    }
37010 );
37011
37012 layout.addxtype({
37013         xtype : 'NestedLayoutPanel',
37014         region: 'west',
37015         layout: {
37016            center: { },
37017            west: { }   
37018         },
37019         items : [ ... list of content panels or nested layout panels.. ]
37020    }
37021 );
37022 </code></pre>
37023      * @param {Object} cfg Xtype definition of item to add.
37024      */
37025     addxtype : function(cfg)
37026     {
37027         // basically accepts a pannel...
37028         // can accept a layout region..!?!?
37029         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37030         
37031         
37032         // theory?  children can only be panels??
37033         
37034         //if (!cfg.xtype.match(/Panel$/)) {
37035         //    return false;
37036         //}
37037         var ret = false;
37038         
37039         if (typeof(cfg.region) == 'undefined') {
37040             Roo.log("Failed to add Panel, region was not set");
37041             Roo.log(cfg);
37042             return false;
37043         }
37044         var region = cfg.region;
37045         delete cfg.region;
37046         
37047           
37048         var xitems = [];
37049         if (cfg.items) {
37050             xitems = cfg.items;
37051             delete cfg.items;
37052         }
37053         var nb = false;
37054         
37055         if ( region == 'center') {
37056             Roo.log("Center: " + cfg.title);
37057         }
37058         
37059         
37060         switch(cfg.xtype) 
37061         {
37062             case 'Content':  // ContentPanel (el, cfg)
37063             case 'Scroll':  // ContentPanel (el, cfg)
37064             case 'View': 
37065                 cfg.autoCreate = cfg.autoCreate || true;
37066                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37067                 //} else {
37068                 //    var el = this.el.createChild();
37069                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37070                 //}
37071                 
37072                 this.add(region, ret);
37073                 break;
37074             
37075             /*
37076             case 'TreePanel': // our new panel!
37077                 cfg.el = this.el.createChild();
37078                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37079                 this.add(region, ret);
37080                 break;
37081             */
37082             
37083             case 'Nest': 
37084                 // create a new Layout (which is  a Border Layout...
37085                 
37086                 var clayout = cfg.layout;
37087                 clayout.el  = this.el.createChild();
37088                 clayout.items   = clayout.items  || [];
37089                 
37090                 delete cfg.layout;
37091                 
37092                 // replace this exitems with the clayout ones..
37093                 xitems = clayout.items;
37094                  
37095                 // force background off if it's in center...
37096                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37097                     cfg.background = false;
37098                 }
37099                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
37100                 
37101                 
37102                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37103                 //console.log('adding nested layout panel '  + cfg.toSource());
37104                 this.add(region, ret);
37105                 nb = {}; /// find first...
37106                 break;
37107             
37108             case 'Grid':
37109                 
37110                 // needs grid and region
37111                 
37112                 //var el = this.getRegion(region).el.createChild();
37113                 /*
37114                  *var el = this.el.createChild();
37115                 // create the grid first...
37116                 cfg.grid.container = el;
37117                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37118                 */
37119                 
37120                 if (region == 'center' && this.active ) {
37121                     cfg.background = false;
37122                 }
37123                 
37124                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37125                 
37126                 this.add(region, ret);
37127                 /*
37128                 if (cfg.background) {
37129                     // render grid on panel activation (if panel background)
37130                     ret.on('activate', function(gp) {
37131                         if (!gp.grid.rendered) {
37132                     //        gp.grid.render(el);
37133                         }
37134                     });
37135                 } else {
37136                   //  cfg.grid.render(el);
37137                 }
37138                 */
37139                 break;
37140            
37141            
37142             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37143                 // it was the old xcomponent building that caused this before.
37144                 // espeically if border is the top element in the tree.
37145                 ret = this;
37146                 break; 
37147                 
37148                     
37149                 
37150                 
37151                 
37152             default:
37153                 /*
37154                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37155                     
37156                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37157                     this.add(region, ret);
37158                 } else {
37159                 */
37160                     Roo.log(cfg);
37161                     throw "Can not add '" + cfg.xtype + "' to Border";
37162                     return null;
37163              
37164                                 
37165              
37166         }
37167         this.beginUpdate();
37168         // add children..
37169         var region = '';
37170         var abn = {};
37171         Roo.each(xitems, function(i)  {
37172             region = nb && i.region ? i.region : false;
37173             
37174             var add = ret.addxtype(i);
37175            
37176             if (region) {
37177                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37178                 if (!i.background) {
37179                     abn[region] = nb[region] ;
37180                 }
37181             }
37182             
37183         });
37184         this.endUpdate();
37185
37186         // make the last non-background panel active..
37187         //if (nb) { Roo.log(abn); }
37188         if (nb) {
37189             
37190             for(var r in abn) {
37191                 region = this.getRegion(r);
37192                 if (region) {
37193                     // tried using nb[r], but it does not work..
37194                      
37195                     region.showPanel(abn[r]);
37196                    
37197                 }
37198             }
37199         }
37200         return ret;
37201         
37202     },
37203     
37204     
37205 // private
37206     factory : function(cfg)
37207     {
37208         
37209         var validRegions = Roo.bootstrap.layout.Border.regions;
37210
37211         var target = cfg.region;
37212         cfg.mgr = this;
37213         
37214         var r = Roo.bootstrap.layout;
37215         Roo.log(target);
37216         switch(target){
37217             case "north":
37218                 return new r.North(cfg);
37219             case "south":
37220                 return new r.South(cfg);
37221             case "east":
37222                 return new r.East(cfg);
37223             case "west":
37224                 return new r.West(cfg);
37225             case "center":
37226                 return new r.Center(cfg);
37227         }
37228         throw 'Layout region "'+target+'" not supported.';
37229     }
37230     
37231     
37232 });
37233  /*
37234  * Based on:
37235  * Ext JS Library 1.1.1
37236  * Copyright(c) 2006-2007, Ext JS, LLC.
37237  *
37238  * Originally Released Under LGPL - original licence link has changed is not relivant.
37239  *
37240  * Fork - LGPL
37241  * <script type="text/javascript">
37242  */
37243  
37244 /**
37245  * @class Roo.bootstrap.layout.Basic
37246  * @extends Roo.util.Observable
37247  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37248  * and does not have a titlebar, tabs or any other features. All it does is size and position 
37249  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37250  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37251  * @cfg {string}   region  the region that it inhabits..
37252  * @cfg {bool}   skipConfig skip config?
37253  * 
37254
37255  */
37256 Roo.bootstrap.layout.Basic = function(config){
37257     
37258     this.mgr = config.mgr;
37259     
37260     this.position = config.region;
37261     
37262     var skipConfig = config.skipConfig;
37263     
37264     this.events = {
37265         /**
37266          * @scope Roo.BasicLayoutRegion
37267          */
37268         
37269         /**
37270          * @event beforeremove
37271          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37272          * @param {Roo.LayoutRegion} this
37273          * @param {Roo.ContentPanel} panel The panel
37274          * @param {Object} e The cancel event object
37275          */
37276         "beforeremove" : true,
37277         /**
37278          * @event invalidated
37279          * Fires when the layout for this region is changed.
37280          * @param {Roo.LayoutRegion} this
37281          */
37282         "invalidated" : true,
37283         /**
37284          * @event visibilitychange
37285          * Fires when this region is shown or hidden 
37286          * @param {Roo.LayoutRegion} this
37287          * @param {Boolean} visibility true or false
37288          */
37289         "visibilitychange" : true,
37290         /**
37291          * @event paneladded
37292          * Fires when a panel is added. 
37293          * @param {Roo.LayoutRegion} this
37294          * @param {Roo.ContentPanel} panel The panel
37295          */
37296         "paneladded" : true,
37297         /**
37298          * @event panelremoved
37299          * Fires when a panel is removed. 
37300          * @param {Roo.LayoutRegion} this
37301          * @param {Roo.ContentPanel} panel The panel
37302          */
37303         "panelremoved" : true,
37304         /**
37305          * @event beforecollapse
37306          * Fires when this region before collapse.
37307          * @param {Roo.LayoutRegion} this
37308          */
37309         "beforecollapse" : true,
37310         /**
37311          * @event collapsed
37312          * Fires when this region is collapsed.
37313          * @param {Roo.LayoutRegion} this
37314          */
37315         "collapsed" : true,
37316         /**
37317          * @event expanded
37318          * Fires when this region is expanded.
37319          * @param {Roo.LayoutRegion} this
37320          */
37321         "expanded" : true,
37322         /**
37323          * @event slideshow
37324          * Fires when this region is slid into view.
37325          * @param {Roo.LayoutRegion} this
37326          */
37327         "slideshow" : true,
37328         /**
37329          * @event slidehide
37330          * Fires when this region slides out of view. 
37331          * @param {Roo.LayoutRegion} this
37332          */
37333         "slidehide" : true,
37334         /**
37335          * @event panelactivated
37336          * Fires when a panel is activated. 
37337          * @param {Roo.LayoutRegion} this
37338          * @param {Roo.ContentPanel} panel The activated panel
37339          */
37340         "panelactivated" : true,
37341         /**
37342          * @event resized
37343          * Fires when the user resizes this region. 
37344          * @param {Roo.LayoutRegion} this
37345          * @param {Number} newSize The new size (width for east/west, height for north/south)
37346          */
37347         "resized" : true
37348     };
37349     /** A collection of panels in this region. @type Roo.util.MixedCollection */
37350     this.panels = new Roo.util.MixedCollection();
37351     this.panels.getKey = this.getPanelId.createDelegate(this);
37352     this.box = null;
37353     this.activePanel = null;
37354     // ensure listeners are added...
37355     
37356     if (config.listeners || config.events) {
37357         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37358             listeners : config.listeners || {},
37359             events : config.events || {}
37360         });
37361     }
37362     
37363     if(skipConfig !== true){
37364         this.applyConfig(config);
37365     }
37366 };
37367
37368 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
37369 {
37370     getPanelId : function(p){
37371         return p.getId();
37372     },
37373     
37374     applyConfig : function(config){
37375         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37376         this.config = config;
37377         
37378     },
37379     
37380     /**
37381      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
37382      * the width, for horizontal (north, south) the height.
37383      * @param {Number} newSize The new width or height
37384      */
37385     resizeTo : function(newSize){
37386         var el = this.el ? this.el :
37387                  (this.activePanel ? this.activePanel.getEl() : null);
37388         if(el){
37389             switch(this.position){
37390                 case "east":
37391                 case "west":
37392                     el.setWidth(newSize);
37393                     this.fireEvent("resized", this, newSize);
37394                 break;
37395                 case "north":
37396                 case "south":
37397                     el.setHeight(newSize);
37398                     this.fireEvent("resized", this, newSize);
37399                 break;                
37400             }
37401         }
37402     },
37403     
37404     getBox : function(){
37405         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
37406     },
37407     
37408     getMargins : function(){
37409         return this.margins;
37410     },
37411     
37412     updateBox : function(box){
37413         this.box = box;
37414         var el = this.activePanel.getEl();
37415         el.dom.style.left = box.x + "px";
37416         el.dom.style.top = box.y + "px";
37417         this.activePanel.setSize(box.width, box.height);
37418     },
37419     
37420     /**
37421      * Returns the container element for this region.
37422      * @return {Roo.Element}
37423      */
37424     getEl : function(){
37425         return this.activePanel;
37426     },
37427     
37428     /**
37429      * Returns true if this region is currently visible.
37430      * @return {Boolean}
37431      */
37432     isVisible : function(){
37433         return this.activePanel ? true : false;
37434     },
37435     
37436     setActivePanel : function(panel){
37437         panel = this.getPanel(panel);
37438         if(this.activePanel && this.activePanel != panel){
37439             this.activePanel.setActiveState(false);
37440             this.activePanel.getEl().setLeftTop(-10000,-10000);
37441         }
37442         this.activePanel = panel;
37443         panel.setActiveState(true);
37444         if(this.box){
37445             panel.setSize(this.box.width, this.box.height);
37446         }
37447         this.fireEvent("panelactivated", this, panel);
37448         this.fireEvent("invalidated");
37449     },
37450     
37451     /**
37452      * Show the specified panel.
37453      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
37454      * @return {Roo.ContentPanel} The shown panel or null
37455      */
37456     showPanel : function(panel){
37457         panel = this.getPanel(panel);
37458         if(panel){
37459             this.setActivePanel(panel);
37460         }
37461         return panel;
37462     },
37463     
37464     /**
37465      * Get the active panel for this region.
37466      * @return {Roo.ContentPanel} The active panel or null
37467      */
37468     getActivePanel : function(){
37469         return this.activePanel;
37470     },
37471     
37472     /**
37473      * Add the passed ContentPanel(s)
37474      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
37475      * @return {Roo.ContentPanel} The panel added (if only one was added)
37476      */
37477     add : function(panel){
37478         if(arguments.length > 1){
37479             for(var i = 0, len = arguments.length; i < len; i++) {
37480                 this.add(arguments[i]);
37481             }
37482             return null;
37483         }
37484         if(this.hasPanel(panel)){
37485             this.showPanel(panel);
37486             return panel;
37487         }
37488         var el = panel.getEl();
37489         if(el.dom.parentNode != this.mgr.el.dom){
37490             this.mgr.el.dom.appendChild(el.dom);
37491         }
37492         if(panel.setRegion){
37493             panel.setRegion(this);
37494         }
37495         this.panels.add(panel);
37496         el.setStyle("position", "absolute");
37497         if(!panel.background){
37498             this.setActivePanel(panel);
37499             if(this.config.initialSize && this.panels.getCount()==1){
37500                 this.resizeTo(this.config.initialSize);
37501             }
37502         }
37503         this.fireEvent("paneladded", this, panel);
37504         return panel;
37505     },
37506     
37507     /**
37508      * Returns true if the panel is in this region.
37509      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37510      * @return {Boolean}
37511      */
37512     hasPanel : function(panel){
37513         if(typeof panel == "object"){ // must be panel obj
37514             panel = panel.getId();
37515         }
37516         return this.getPanel(panel) ? true : false;
37517     },
37518     
37519     /**
37520      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
37521      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37522      * @param {Boolean} preservePanel Overrides the config preservePanel option
37523      * @return {Roo.ContentPanel} The panel that was removed
37524      */
37525     remove : function(panel, preservePanel){
37526         panel = this.getPanel(panel);
37527         if(!panel){
37528             return null;
37529         }
37530         var e = {};
37531         this.fireEvent("beforeremove", this, panel, e);
37532         if(e.cancel === true){
37533             return null;
37534         }
37535         var panelId = panel.getId();
37536         this.panels.removeKey(panelId);
37537         return panel;
37538     },
37539     
37540     /**
37541      * Returns the panel specified or null if it's not in this region.
37542      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37543      * @return {Roo.ContentPanel}
37544      */
37545     getPanel : function(id){
37546         if(typeof id == "object"){ // must be panel obj
37547             return id;
37548         }
37549         return this.panels.get(id);
37550     },
37551     
37552     /**
37553      * Returns this regions position (north/south/east/west/center).
37554      * @return {String} 
37555      */
37556     getPosition: function(){
37557         return this.position;    
37558     }
37559 });/*
37560  * Based on:
37561  * Ext JS Library 1.1.1
37562  * Copyright(c) 2006-2007, Ext JS, LLC.
37563  *
37564  * Originally Released Under LGPL - original licence link has changed is not relivant.
37565  *
37566  * Fork - LGPL
37567  * <script type="text/javascript">
37568  */
37569  
37570 /**
37571  * @class Roo.bootstrap.layout.Region
37572  * @extends Roo.bootstrap.layout.Basic
37573  * This class represents a region in a layout manager.
37574  
37575  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
37576  * @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})
37577  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
37578  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
37579  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
37580  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
37581  * @cfg {String}    title           The title for the region (overrides panel titles)
37582  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
37583  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
37584  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
37585  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
37586  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
37587  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
37588  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
37589  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
37590  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
37591  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
37592
37593  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
37594  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
37595  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
37596  * @cfg {Number}    width           For East/West panels
37597  * @cfg {Number}    height          For North/South panels
37598  * @cfg {Boolean}   split           To show the splitter
37599  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
37600  * 
37601  * @cfg {string}   cls             Extra CSS classes to add to region
37602  * 
37603  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37604  * @cfg {string}   region  the region that it inhabits..
37605  *
37606
37607  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
37608  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
37609
37610  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
37611  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
37612  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
37613  */
37614 Roo.bootstrap.layout.Region = function(config)
37615 {
37616     this.applyConfig(config);
37617
37618     var mgr = config.mgr;
37619     var pos = config.region;
37620     config.skipConfig = true;
37621     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
37622     
37623     if (mgr.el) {
37624         this.onRender(mgr.el);   
37625     }
37626      
37627     this.visible = true;
37628     this.collapsed = false;
37629     this.unrendered_panels = [];
37630 };
37631
37632 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
37633
37634     position: '', // set by wrapper (eg. north/south etc..)
37635     unrendered_panels : null,  // unrendered panels.
37636     
37637     tabPosition : false,
37638     
37639     mgr: false, // points to 'Border'
37640     
37641     
37642     createBody : function(){
37643         /** This region's body element 
37644         * @type Roo.Element */
37645         this.bodyEl = this.el.createChild({
37646                 tag: "div",
37647                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
37648         });
37649     },
37650
37651     onRender: function(ctr, pos)
37652     {
37653         var dh = Roo.DomHelper;
37654         /** This region's container element 
37655         * @type Roo.Element */
37656         this.el = dh.append(ctr.dom, {
37657                 tag: "div",
37658                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
37659             }, true);
37660         /** This region's title element 
37661         * @type Roo.Element */
37662     
37663         this.titleEl = dh.append(this.el.dom,  {
37664                 tag: "div",
37665                 unselectable: "on",
37666                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
37667                 children:[
37668                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
37669                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
37670                 ]
37671             }, true);
37672         
37673         this.titleEl.enableDisplayMode();
37674         /** This region's title text element 
37675         * @type HTMLElement */
37676         this.titleTextEl = this.titleEl.dom.firstChild;
37677         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
37678         /*
37679         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
37680         this.closeBtn.enableDisplayMode();
37681         this.closeBtn.on("click", this.closeClicked, this);
37682         this.closeBtn.hide();
37683     */
37684         this.createBody(this.config);
37685         if(this.config.hideWhenEmpty){
37686             this.hide();
37687             this.on("paneladded", this.validateVisibility, this);
37688             this.on("panelremoved", this.validateVisibility, this);
37689         }
37690         if(this.autoScroll){
37691             this.bodyEl.setStyle("overflow", "auto");
37692         }else{
37693             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
37694         }
37695         //if(c.titlebar !== false){
37696             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
37697                 this.titleEl.hide();
37698             }else{
37699                 this.titleEl.show();
37700                 if(this.config.title){
37701                     this.titleTextEl.innerHTML = this.config.title;
37702                 }
37703             }
37704         //}
37705         if(this.config.collapsed){
37706             this.collapse(true);
37707         }
37708         if(this.config.hidden){
37709             this.hide();
37710         }
37711         
37712         if (this.unrendered_panels && this.unrendered_panels.length) {
37713             for (var i =0;i< this.unrendered_panels.length; i++) {
37714                 this.add(this.unrendered_panels[i]);
37715             }
37716             this.unrendered_panels = null;
37717             
37718         }
37719         
37720     },
37721     
37722     applyConfig : function(c)
37723     {
37724         /*
37725          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
37726             var dh = Roo.DomHelper;
37727             if(c.titlebar !== false){
37728                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
37729                 this.collapseBtn.on("click", this.collapse, this);
37730                 this.collapseBtn.enableDisplayMode();
37731                 /*
37732                 if(c.showPin === true || this.showPin){
37733                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
37734                     this.stickBtn.enableDisplayMode();
37735                     this.stickBtn.on("click", this.expand, this);
37736                     this.stickBtn.hide();
37737                 }
37738                 
37739             }
37740             */
37741             /** This region's collapsed element
37742             * @type Roo.Element */
37743             /*
37744              *
37745             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
37746                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
37747             ]}, true);
37748             
37749             if(c.floatable !== false){
37750                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
37751                this.collapsedEl.on("click", this.collapseClick, this);
37752             }
37753
37754             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
37755                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
37756                    id: "message", unselectable: "on", style:{"float":"left"}});
37757                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
37758              }
37759             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
37760             this.expandBtn.on("click", this.expand, this);
37761             
37762         }
37763         
37764         if(this.collapseBtn){
37765             this.collapseBtn.setVisible(c.collapsible == true);
37766         }
37767         
37768         this.cmargins = c.cmargins || this.cmargins ||
37769                          (this.position == "west" || this.position == "east" ?
37770                              {top: 0, left: 2, right:2, bottom: 0} :
37771                              {top: 2, left: 0, right:0, bottom: 2});
37772         */
37773         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37774         
37775         
37776         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
37777         
37778         this.autoScroll = c.autoScroll || false;
37779         
37780         
37781        
37782         
37783         this.duration = c.duration || .30;
37784         this.slideDuration = c.slideDuration || .45;
37785         this.config = c;
37786        
37787     },
37788     /**
37789      * Returns true if this region is currently visible.
37790      * @return {Boolean}
37791      */
37792     isVisible : function(){
37793         return this.visible;
37794     },
37795
37796     /**
37797      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
37798      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
37799      */
37800     //setCollapsedTitle : function(title){
37801     //    title = title || "&#160;";
37802      //   if(this.collapsedTitleTextEl){
37803       //      this.collapsedTitleTextEl.innerHTML = title;
37804        // }
37805     //},
37806
37807     getBox : function(){
37808         var b;
37809       //  if(!this.collapsed){
37810             b = this.el.getBox(false, true);
37811        // }else{
37812           //  b = this.collapsedEl.getBox(false, true);
37813         //}
37814         return b;
37815     },
37816
37817     getMargins : function(){
37818         return this.margins;
37819         //return this.collapsed ? this.cmargins : this.margins;
37820     },
37821 /*
37822     highlight : function(){
37823         this.el.addClass("x-layout-panel-dragover");
37824     },
37825
37826     unhighlight : function(){
37827         this.el.removeClass("x-layout-panel-dragover");
37828     },
37829 */
37830     updateBox : function(box)
37831     {
37832         if (!this.bodyEl) {
37833             return; // not rendered yet..
37834         }
37835         
37836         this.box = box;
37837         if(!this.collapsed){
37838             this.el.dom.style.left = box.x + "px";
37839             this.el.dom.style.top = box.y + "px";
37840             this.updateBody(box.width, box.height);
37841         }else{
37842             this.collapsedEl.dom.style.left = box.x + "px";
37843             this.collapsedEl.dom.style.top = box.y + "px";
37844             this.collapsedEl.setSize(box.width, box.height);
37845         }
37846         if(this.tabs){
37847             this.tabs.autoSizeTabs();
37848         }
37849     },
37850
37851     updateBody : function(w, h)
37852     {
37853         if(w !== null){
37854             this.el.setWidth(w);
37855             w -= this.el.getBorderWidth("rl");
37856             if(this.config.adjustments){
37857                 w += this.config.adjustments[0];
37858             }
37859         }
37860         if(h !== null && h > 0){
37861             this.el.setHeight(h);
37862             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
37863             h -= this.el.getBorderWidth("tb");
37864             if(this.config.adjustments){
37865                 h += this.config.adjustments[1];
37866             }
37867             this.bodyEl.setHeight(h);
37868             if(this.tabs){
37869                 h = this.tabs.syncHeight(h);
37870             }
37871         }
37872         if(this.panelSize){
37873             w = w !== null ? w : this.panelSize.width;
37874             h = h !== null ? h : this.panelSize.height;
37875         }
37876         if(this.activePanel){
37877             var el = this.activePanel.getEl();
37878             w = w !== null ? w : el.getWidth();
37879             h = h !== null ? h : el.getHeight();
37880             this.panelSize = {width: w, height: h};
37881             this.activePanel.setSize(w, h);
37882         }
37883         if(Roo.isIE && this.tabs){
37884             this.tabs.el.repaint();
37885         }
37886     },
37887
37888     /**
37889      * Returns the container element for this region.
37890      * @return {Roo.Element}
37891      */
37892     getEl : function(){
37893         return this.el;
37894     },
37895
37896     /**
37897      * Hides this region.
37898      */
37899     hide : function(){
37900         //if(!this.collapsed){
37901             this.el.dom.style.left = "-2000px";
37902             this.el.hide();
37903         //}else{
37904          //   this.collapsedEl.dom.style.left = "-2000px";
37905          //   this.collapsedEl.hide();
37906        // }
37907         this.visible = false;
37908         this.fireEvent("visibilitychange", this, false);
37909     },
37910
37911     /**
37912      * Shows this region if it was previously hidden.
37913      */
37914     show : function(){
37915         //if(!this.collapsed){
37916             this.el.show();
37917         //}else{
37918         //    this.collapsedEl.show();
37919        // }
37920         this.visible = true;
37921         this.fireEvent("visibilitychange", this, true);
37922     },
37923 /*
37924     closeClicked : function(){
37925         if(this.activePanel){
37926             this.remove(this.activePanel);
37927         }
37928     },
37929
37930     collapseClick : function(e){
37931         if(this.isSlid){
37932            e.stopPropagation();
37933            this.slideIn();
37934         }else{
37935            e.stopPropagation();
37936            this.slideOut();
37937         }
37938     },
37939 */
37940     /**
37941      * Collapses this region.
37942      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
37943      */
37944     /*
37945     collapse : function(skipAnim, skipCheck = false){
37946         if(this.collapsed) {
37947             return;
37948         }
37949         
37950         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
37951             
37952             this.collapsed = true;
37953             if(this.split){
37954                 this.split.el.hide();
37955             }
37956             if(this.config.animate && skipAnim !== true){
37957                 this.fireEvent("invalidated", this);
37958                 this.animateCollapse();
37959             }else{
37960                 this.el.setLocation(-20000,-20000);
37961                 this.el.hide();
37962                 this.collapsedEl.show();
37963                 this.fireEvent("collapsed", this);
37964                 this.fireEvent("invalidated", this);
37965             }
37966         }
37967         
37968     },
37969 */
37970     animateCollapse : function(){
37971         // overridden
37972     },
37973
37974     /**
37975      * Expands this region if it was previously collapsed.
37976      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
37977      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
37978      */
37979     /*
37980     expand : function(e, skipAnim){
37981         if(e) {
37982             e.stopPropagation();
37983         }
37984         if(!this.collapsed || this.el.hasActiveFx()) {
37985             return;
37986         }
37987         if(this.isSlid){
37988             this.afterSlideIn();
37989             skipAnim = true;
37990         }
37991         this.collapsed = false;
37992         if(this.config.animate && skipAnim !== true){
37993             this.animateExpand();
37994         }else{
37995             this.el.show();
37996             if(this.split){
37997                 this.split.el.show();
37998             }
37999             this.collapsedEl.setLocation(-2000,-2000);
38000             this.collapsedEl.hide();
38001             this.fireEvent("invalidated", this);
38002             this.fireEvent("expanded", this);
38003         }
38004     },
38005 */
38006     animateExpand : function(){
38007         // overridden
38008     },
38009
38010     initTabs : function()
38011     {
38012         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38013         
38014         var ts = new Roo.bootstrap.panel.Tabs({
38015             el: this.bodyEl.dom,
38016             region : this,
38017             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
38018             disableTooltips: this.config.disableTabTips,
38019             toolbar : this.config.toolbar
38020         });
38021         
38022         if(this.config.hideTabs){
38023             ts.stripWrap.setDisplayed(false);
38024         }
38025         this.tabs = ts;
38026         ts.resizeTabs = this.config.resizeTabs === true;
38027         ts.minTabWidth = this.config.minTabWidth || 40;
38028         ts.maxTabWidth = this.config.maxTabWidth || 250;
38029         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38030         ts.monitorResize = false;
38031         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38032         ts.bodyEl.addClass('roo-layout-tabs-body');
38033         this.panels.each(this.initPanelAsTab, this);
38034     },
38035
38036     initPanelAsTab : function(panel){
38037         var ti = this.tabs.addTab(
38038             panel.getEl().id,
38039             panel.getTitle(),
38040             null,
38041             this.config.closeOnTab && panel.isClosable(),
38042             panel.tpl
38043         );
38044         if(panel.tabTip !== undefined){
38045             ti.setTooltip(panel.tabTip);
38046         }
38047         ti.on("activate", function(){
38048               this.setActivePanel(panel);
38049         }, this);
38050         
38051         if(this.config.closeOnTab){
38052             ti.on("beforeclose", function(t, e){
38053                 e.cancel = true;
38054                 this.remove(panel);
38055             }, this);
38056         }
38057         
38058         panel.tabItem = ti;
38059         
38060         return ti;
38061     },
38062
38063     updatePanelTitle : function(panel, title)
38064     {
38065         if(this.activePanel == panel){
38066             this.updateTitle(title);
38067         }
38068         if(this.tabs){
38069             var ti = this.tabs.getTab(panel.getEl().id);
38070             ti.setText(title);
38071             if(panel.tabTip !== undefined){
38072                 ti.setTooltip(panel.tabTip);
38073             }
38074         }
38075     },
38076
38077     updateTitle : function(title){
38078         if(this.titleTextEl && !this.config.title){
38079             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
38080         }
38081     },
38082
38083     setActivePanel : function(panel)
38084     {
38085         panel = this.getPanel(panel);
38086         if(this.activePanel && this.activePanel != panel){
38087             if(this.activePanel.setActiveState(false) === false){
38088                 return;
38089             }
38090         }
38091         this.activePanel = panel;
38092         panel.setActiveState(true);
38093         if(this.panelSize){
38094             panel.setSize(this.panelSize.width, this.panelSize.height);
38095         }
38096         if(this.closeBtn){
38097             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38098         }
38099         this.updateTitle(panel.getTitle());
38100         if(this.tabs){
38101             this.fireEvent("invalidated", this);
38102         }
38103         this.fireEvent("panelactivated", this, panel);
38104     },
38105
38106     /**
38107      * Shows the specified panel.
38108      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38109      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38110      */
38111     showPanel : function(panel)
38112     {
38113         panel = this.getPanel(panel);
38114         if(panel){
38115             if(this.tabs){
38116                 var tab = this.tabs.getTab(panel.getEl().id);
38117                 if(tab.isHidden()){
38118                     this.tabs.unhideTab(tab.id);
38119                 }
38120                 tab.activate();
38121             }else{
38122                 this.setActivePanel(panel);
38123             }
38124         }
38125         return panel;
38126     },
38127
38128     /**
38129      * Get the active panel for this region.
38130      * @return {Roo.ContentPanel} The active panel or null
38131      */
38132     getActivePanel : function(){
38133         return this.activePanel;
38134     },
38135
38136     validateVisibility : function(){
38137         if(this.panels.getCount() < 1){
38138             this.updateTitle("&#160;");
38139             this.closeBtn.hide();
38140             this.hide();
38141         }else{
38142             if(!this.isVisible()){
38143                 this.show();
38144             }
38145         }
38146     },
38147
38148     /**
38149      * Adds the passed ContentPanel(s) to this region.
38150      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38151      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38152      */
38153     add : function(panel)
38154     {
38155         if(arguments.length > 1){
38156             for(var i = 0, len = arguments.length; i < len; i++) {
38157                 this.add(arguments[i]);
38158             }
38159             return null;
38160         }
38161         
38162         // if we have not been rendered yet, then we can not really do much of this..
38163         if (!this.bodyEl) {
38164             this.unrendered_panels.push(panel);
38165             return panel;
38166         }
38167         
38168         
38169         
38170         
38171         if(this.hasPanel(panel)){
38172             this.showPanel(panel);
38173             return panel;
38174         }
38175         panel.setRegion(this);
38176         this.panels.add(panel);
38177        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38178             // sinle panel - no tab...?? would it not be better to render it with the tabs,
38179             // and hide them... ???
38180             this.bodyEl.dom.appendChild(panel.getEl().dom);
38181             if(panel.background !== true){
38182                 this.setActivePanel(panel);
38183             }
38184             this.fireEvent("paneladded", this, panel);
38185             return panel;
38186         }
38187         */
38188         if(!this.tabs){
38189             this.initTabs();
38190         }else{
38191             this.initPanelAsTab(panel);
38192         }
38193         
38194         
38195         if(panel.background !== true){
38196             this.tabs.activate(panel.getEl().id);
38197         }
38198         this.fireEvent("paneladded", this, panel);
38199         return panel;
38200     },
38201
38202     /**
38203      * Hides the tab for the specified panel.
38204      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38205      */
38206     hidePanel : function(panel){
38207         if(this.tabs && (panel = this.getPanel(panel))){
38208             this.tabs.hideTab(panel.getEl().id);
38209         }
38210     },
38211
38212     /**
38213      * Unhides the tab for a previously hidden panel.
38214      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38215      */
38216     unhidePanel : function(panel){
38217         if(this.tabs && (panel = this.getPanel(panel))){
38218             this.tabs.unhideTab(panel.getEl().id);
38219         }
38220     },
38221
38222     clearPanels : function(){
38223         while(this.panels.getCount() > 0){
38224              this.remove(this.panels.first());
38225         }
38226     },
38227
38228     /**
38229      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38230      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38231      * @param {Boolean} preservePanel Overrides the config preservePanel option
38232      * @return {Roo.ContentPanel} The panel that was removed
38233      */
38234     remove : function(panel, preservePanel)
38235     {
38236         panel = this.getPanel(panel);
38237         if(!panel){
38238             return null;
38239         }
38240         var e = {};
38241         this.fireEvent("beforeremove", this, panel, e);
38242         if(e.cancel === true){
38243             return null;
38244         }
38245         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38246         var panelId = panel.getId();
38247         this.panels.removeKey(panelId);
38248         if(preservePanel){
38249             document.body.appendChild(panel.getEl().dom);
38250         }
38251         if(this.tabs){
38252             this.tabs.removeTab(panel.getEl().id);
38253         }else if (!preservePanel){
38254             this.bodyEl.dom.removeChild(panel.getEl().dom);
38255         }
38256         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38257             var p = this.panels.first();
38258             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38259             tempEl.appendChild(p.getEl().dom);
38260             this.bodyEl.update("");
38261             this.bodyEl.dom.appendChild(p.getEl().dom);
38262             tempEl = null;
38263             this.updateTitle(p.getTitle());
38264             this.tabs = null;
38265             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38266             this.setActivePanel(p);
38267         }
38268         panel.setRegion(null);
38269         if(this.activePanel == panel){
38270             this.activePanel = null;
38271         }
38272         if(this.config.autoDestroy !== false && preservePanel !== true){
38273             try{panel.destroy();}catch(e){}
38274         }
38275         this.fireEvent("panelremoved", this, panel);
38276         return panel;
38277     },
38278
38279     /**
38280      * Returns the TabPanel component used by this region
38281      * @return {Roo.TabPanel}
38282      */
38283     getTabs : function(){
38284         return this.tabs;
38285     },
38286
38287     createTool : function(parentEl, className){
38288         var btn = Roo.DomHelper.append(parentEl, {
38289             tag: "div",
38290             cls: "x-layout-tools-button",
38291             children: [ {
38292                 tag: "div",
38293                 cls: "roo-layout-tools-button-inner " + className,
38294                 html: "&#160;"
38295             }]
38296         }, true);
38297         btn.addClassOnOver("roo-layout-tools-button-over");
38298         return btn;
38299     }
38300 });/*
38301  * Based on:
38302  * Ext JS Library 1.1.1
38303  * Copyright(c) 2006-2007, Ext JS, LLC.
38304  *
38305  * Originally Released Under LGPL - original licence link has changed is not relivant.
38306  *
38307  * Fork - LGPL
38308  * <script type="text/javascript">
38309  */
38310  
38311
38312
38313 /**
38314  * @class Roo.SplitLayoutRegion
38315  * @extends Roo.LayoutRegion
38316  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38317  */
38318 Roo.bootstrap.layout.Split = function(config){
38319     this.cursor = config.cursor;
38320     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38321 };
38322
38323 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38324 {
38325     splitTip : "Drag to resize.",
38326     collapsibleSplitTip : "Drag to resize. Double click to hide.",
38327     useSplitTips : false,
38328
38329     applyConfig : function(config){
38330         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38331     },
38332     
38333     onRender : function(ctr,pos) {
38334         
38335         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38336         if(!this.config.split){
38337             return;
38338         }
38339         if(!this.split){
38340             
38341             var splitEl = Roo.DomHelper.append(ctr.dom,  {
38342                             tag: "div",
38343                             id: this.el.id + "-split",
38344                             cls: "roo-layout-split roo-layout-split-"+this.position,
38345                             html: "&#160;"
38346             });
38347             /** The SplitBar for this region 
38348             * @type Roo.SplitBar */
38349             // does not exist yet...
38350             Roo.log([this.position, this.orientation]);
38351             
38352             this.split = new Roo.bootstrap.SplitBar({
38353                 dragElement : splitEl,
38354                 resizingElement: this.el,
38355                 orientation : this.orientation
38356             });
38357             
38358             this.split.on("moved", this.onSplitMove, this);
38359             this.split.useShim = this.config.useShim === true;
38360             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38361             if(this.useSplitTips){
38362                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38363             }
38364             //if(config.collapsible){
38365             //    this.split.el.on("dblclick", this.collapse,  this);
38366             //}
38367         }
38368         if(typeof this.config.minSize != "undefined"){
38369             this.split.minSize = this.config.minSize;
38370         }
38371         if(typeof this.config.maxSize != "undefined"){
38372             this.split.maxSize = this.config.maxSize;
38373         }
38374         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
38375             this.hideSplitter();
38376         }
38377         
38378     },
38379
38380     getHMaxSize : function(){
38381          var cmax = this.config.maxSize || 10000;
38382          var center = this.mgr.getRegion("center");
38383          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
38384     },
38385
38386     getVMaxSize : function(){
38387          var cmax = this.config.maxSize || 10000;
38388          var center = this.mgr.getRegion("center");
38389          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
38390     },
38391
38392     onSplitMove : function(split, newSize){
38393         this.fireEvent("resized", this, newSize);
38394     },
38395     
38396     /** 
38397      * Returns the {@link Roo.SplitBar} for this region.
38398      * @return {Roo.SplitBar}
38399      */
38400     getSplitBar : function(){
38401         return this.split;
38402     },
38403     
38404     hide : function(){
38405         this.hideSplitter();
38406         Roo.bootstrap.layout.Split.superclass.hide.call(this);
38407     },
38408
38409     hideSplitter : function(){
38410         if(this.split){
38411             this.split.el.setLocation(-2000,-2000);
38412             this.split.el.hide();
38413         }
38414     },
38415
38416     show : function(){
38417         if(this.split){
38418             this.split.el.show();
38419         }
38420         Roo.bootstrap.layout.Split.superclass.show.call(this);
38421     },
38422     
38423     beforeSlide: function(){
38424         if(Roo.isGecko){// firefox overflow auto bug workaround
38425             this.bodyEl.clip();
38426             if(this.tabs) {
38427                 this.tabs.bodyEl.clip();
38428             }
38429             if(this.activePanel){
38430                 this.activePanel.getEl().clip();
38431                 
38432                 if(this.activePanel.beforeSlide){
38433                     this.activePanel.beforeSlide();
38434                 }
38435             }
38436         }
38437     },
38438     
38439     afterSlide : function(){
38440         if(Roo.isGecko){// firefox overflow auto bug workaround
38441             this.bodyEl.unclip();
38442             if(this.tabs) {
38443                 this.tabs.bodyEl.unclip();
38444             }
38445             if(this.activePanel){
38446                 this.activePanel.getEl().unclip();
38447                 if(this.activePanel.afterSlide){
38448                     this.activePanel.afterSlide();
38449                 }
38450             }
38451         }
38452     },
38453
38454     initAutoHide : function(){
38455         if(this.autoHide !== false){
38456             if(!this.autoHideHd){
38457                 var st = new Roo.util.DelayedTask(this.slideIn, this);
38458                 this.autoHideHd = {
38459                     "mouseout": function(e){
38460                         if(!e.within(this.el, true)){
38461                             st.delay(500);
38462                         }
38463                     },
38464                     "mouseover" : function(e){
38465                         st.cancel();
38466                     },
38467                     scope : this
38468                 };
38469             }
38470             this.el.on(this.autoHideHd);
38471         }
38472     },
38473
38474     clearAutoHide : function(){
38475         if(this.autoHide !== false){
38476             this.el.un("mouseout", this.autoHideHd.mouseout);
38477             this.el.un("mouseover", this.autoHideHd.mouseover);
38478         }
38479     },
38480
38481     clearMonitor : function(){
38482         Roo.get(document).un("click", this.slideInIf, this);
38483     },
38484
38485     // these names are backwards but not changed for compat
38486     slideOut : function(){
38487         if(this.isSlid || this.el.hasActiveFx()){
38488             return;
38489         }
38490         this.isSlid = true;
38491         if(this.collapseBtn){
38492             this.collapseBtn.hide();
38493         }
38494         this.closeBtnState = this.closeBtn.getStyle('display');
38495         this.closeBtn.hide();
38496         if(this.stickBtn){
38497             this.stickBtn.show();
38498         }
38499         this.el.show();
38500         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
38501         this.beforeSlide();
38502         this.el.setStyle("z-index", 10001);
38503         this.el.slideIn(this.getSlideAnchor(), {
38504             callback: function(){
38505                 this.afterSlide();
38506                 this.initAutoHide();
38507                 Roo.get(document).on("click", this.slideInIf, this);
38508                 this.fireEvent("slideshow", this);
38509             },
38510             scope: this,
38511             block: true
38512         });
38513     },
38514
38515     afterSlideIn : function(){
38516         this.clearAutoHide();
38517         this.isSlid = false;
38518         this.clearMonitor();
38519         this.el.setStyle("z-index", "");
38520         if(this.collapseBtn){
38521             this.collapseBtn.show();
38522         }
38523         this.closeBtn.setStyle('display', this.closeBtnState);
38524         if(this.stickBtn){
38525             this.stickBtn.hide();
38526         }
38527         this.fireEvent("slidehide", this);
38528     },
38529
38530     slideIn : function(cb){
38531         if(!this.isSlid || this.el.hasActiveFx()){
38532             Roo.callback(cb);
38533             return;
38534         }
38535         this.isSlid = false;
38536         this.beforeSlide();
38537         this.el.slideOut(this.getSlideAnchor(), {
38538             callback: function(){
38539                 this.el.setLeftTop(-10000, -10000);
38540                 this.afterSlide();
38541                 this.afterSlideIn();
38542                 Roo.callback(cb);
38543             },
38544             scope: this,
38545             block: true
38546         });
38547     },
38548     
38549     slideInIf : function(e){
38550         if(!e.within(this.el)){
38551             this.slideIn();
38552         }
38553     },
38554
38555     animateCollapse : function(){
38556         this.beforeSlide();
38557         this.el.setStyle("z-index", 20000);
38558         var anchor = this.getSlideAnchor();
38559         this.el.slideOut(anchor, {
38560             callback : function(){
38561                 this.el.setStyle("z-index", "");
38562                 this.collapsedEl.slideIn(anchor, {duration:.3});
38563                 this.afterSlide();
38564                 this.el.setLocation(-10000,-10000);
38565                 this.el.hide();
38566                 this.fireEvent("collapsed", this);
38567             },
38568             scope: this,
38569             block: true
38570         });
38571     },
38572
38573     animateExpand : function(){
38574         this.beforeSlide();
38575         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
38576         this.el.setStyle("z-index", 20000);
38577         this.collapsedEl.hide({
38578             duration:.1
38579         });
38580         this.el.slideIn(this.getSlideAnchor(), {
38581             callback : function(){
38582                 this.el.setStyle("z-index", "");
38583                 this.afterSlide();
38584                 if(this.split){
38585                     this.split.el.show();
38586                 }
38587                 this.fireEvent("invalidated", this);
38588                 this.fireEvent("expanded", this);
38589             },
38590             scope: this,
38591             block: true
38592         });
38593     },
38594
38595     anchors : {
38596         "west" : "left",
38597         "east" : "right",
38598         "north" : "top",
38599         "south" : "bottom"
38600     },
38601
38602     sanchors : {
38603         "west" : "l",
38604         "east" : "r",
38605         "north" : "t",
38606         "south" : "b"
38607     },
38608
38609     canchors : {
38610         "west" : "tl-tr",
38611         "east" : "tr-tl",
38612         "north" : "tl-bl",
38613         "south" : "bl-tl"
38614     },
38615
38616     getAnchor : function(){
38617         return this.anchors[this.position];
38618     },
38619
38620     getCollapseAnchor : function(){
38621         return this.canchors[this.position];
38622     },
38623
38624     getSlideAnchor : function(){
38625         return this.sanchors[this.position];
38626     },
38627
38628     getAlignAdj : function(){
38629         var cm = this.cmargins;
38630         switch(this.position){
38631             case "west":
38632                 return [0, 0];
38633             break;
38634             case "east":
38635                 return [0, 0];
38636             break;
38637             case "north":
38638                 return [0, 0];
38639             break;
38640             case "south":
38641                 return [0, 0];
38642             break;
38643         }
38644     },
38645
38646     getExpandAdj : function(){
38647         var c = this.collapsedEl, cm = this.cmargins;
38648         switch(this.position){
38649             case "west":
38650                 return [-(cm.right+c.getWidth()+cm.left), 0];
38651             break;
38652             case "east":
38653                 return [cm.right+c.getWidth()+cm.left, 0];
38654             break;
38655             case "north":
38656                 return [0, -(cm.top+cm.bottom+c.getHeight())];
38657             break;
38658             case "south":
38659                 return [0, cm.top+cm.bottom+c.getHeight()];
38660             break;
38661         }
38662     }
38663 });/*
38664  * Based on:
38665  * Ext JS Library 1.1.1
38666  * Copyright(c) 2006-2007, Ext JS, LLC.
38667  *
38668  * Originally Released Under LGPL - original licence link has changed is not relivant.
38669  *
38670  * Fork - LGPL
38671  * <script type="text/javascript">
38672  */
38673 /*
38674  * These classes are private internal classes
38675  */
38676 Roo.bootstrap.layout.Center = function(config){
38677     config.region = "center";
38678     Roo.bootstrap.layout.Region.call(this, config);
38679     this.visible = true;
38680     this.minWidth = config.minWidth || 20;
38681     this.minHeight = config.minHeight || 20;
38682 };
38683
38684 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
38685     hide : function(){
38686         // center panel can't be hidden
38687     },
38688     
38689     show : function(){
38690         // center panel can't be hidden
38691     },
38692     
38693     getMinWidth: function(){
38694         return this.minWidth;
38695     },
38696     
38697     getMinHeight: function(){
38698         return this.minHeight;
38699     }
38700 });
38701
38702
38703
38704
38705  
38706
38707
38708
38709
38710
38711
38712 Roo.bootstrap.layout.North = function(config)
38713 {
38714     config.region = 'north';
38715     config.cursor = 'n-resize';
38716     
38717     Roo.bootstrap.layout.Split.call(this, config);
38718     
38719     
38720     if(this.split){
38721         this.split.placement = Roo.bootstrap.SplitBar.TOP;
38722         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
38723         this.split.el.addClass("roo-layout-split-v");
38724     }
38725     var size = config.initialSize || config.height;
38726     if(typeof size != "undefined"){
38727         this.el.setHeight(size);
38728     }
38729 };
38730 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
38731 {
38732     orientation: Roo.bootstrap.SplitBar.VERTICAL,
38733     
38734     
38735     
38736     getBox : function(){
38737         if(this.collapsed){
38738             return this.collapsedEl.getBox();
38739         }
38740         var box = this.el.getBox();
38741         if(this.split){
38742             box.height += this.split.el.getHeight();
38743         }
38744         return box;
38745     },
38746     
38747     updateBox : function(box){
38748         if(this.split && !this.collapsed){
38749             box.height -= this.split.el.getHeight();
38750             this.split.el.setLeft(box.x);
38751             this.split.el.setTop(box.y+box.height);
38752             this.split.el.setWidth(box.width);
38753         }
38754         if(this.collapsed){
38755             this.updateBody(box.width, null);
38756         }
38757         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38758     }
38759 });
38760
38761
38762
38763
38764
38765 Roo.bootstrap.layout.South = function(config){
38766     config.region = 'south';
38767     config.cursor = 's-resize';
38768     Roo.bootstrap.layout.Split.call(this, config);
38769     if(this.split){
38770         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
38771         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
38772         this.split.el.addClass("roo-layout-split-v");
38773     }
38774     var size = config.initialSize || config.height;
38775     if(typeof size != "undefined"){
38776         this.el.setHeight(size);
38777     }
38778 };
38779
38780 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
38781     orientation: Roo.bootstrap.SplitBar.VERTICAL,
38782     getBox : function(){
38783         if(this.collapsed){
38784             return this.collapsedEl.getBox();
38785         }
38786         var box = this.el.getBox();
38787         if(this.split){
38788             var sh = this.split.el.getHeight();
38789             box.height += sh;
38790             box.y -= sh;
38791         }
38792         return box;
38793     },
38794     
38795     updateBox : function(box){
38796         if(this.split && !this.collapsed){
38797             var sh = this.split.el.getHeight();
38798             box.height -= sh;
38799             box.y += sh;
38800             this.split.el.setLeft(box.x);
38801             this.split.el.setTop(box.y-sh);
38802             this.split.el.setWidth(box.width);
38803         }
38804         if(this.collapsed){
38805             this.updateBody(box.width, null);
38806         }
38807         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38808     }
38809 });
38810
38811 Roo.bootstrap.layout.East = function(config){
38812     config.region = "east";
38813     config.cursor = "e-resize";
38814     Roo.bootstrap.layout.Split.call(this, config);
38815     if(this.split){
38816         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
38817         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
38818         this.split.el.addClass("roo-layout-split-h");
38819     }
38820     var size = config.initialSize || config.width;
38821     if(typeof size != "undefined"){
38822         this.el.setWidth(size);
38823     }
38824 };
38825 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
38826     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
38827     getBox : function(){
38828         if(this.collapsed){
38829             return this.collapsedEl.getBox();
38830         }
38831         var box = this.el.getBox();
38832         if(this.split){
38833             var sw = this.split.el.getWidth();
38834             box.width += sw;
38835             box.x -= sw;
38836         }
38837         return box;
38838     },
38839
38840     updateBox : function(box){
38841         if(this.split && !this.collapsed){
38842             var sw = this.split.el.getWidth();
38843             box.width -= sw;
38844             this.split.el.setLeft(box.x);
38845             this.split.el.setTop(box.y);
38846             this.split.el.setHeight(box.height);
38847             box.x += sw;
38848         }
38849         if(this.collapsed){
38850             this.updateBody(null, box.height);
38851         }
38852         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38853     }
38854 });
38855
38856 Roo.bootstrap.layout.West = function(config){
38857     config.region = "west";
38858     config.cursor = "w-resize";
38859     
38860     Roo.bootstrap.layout.Split.call(this, config);
38861     if(this.split){
38862         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
38863         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
38864         this.split.el.addClass("roo-layout-split-h");
38865     }
38866     
38867 };
38868 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
38869     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
38870     
38871     onRender: function(ctr, pos)
38872     {
38873         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
38874         var size = this.config.initialSize || this.config.width;
38875         if(typeof size != "undefined"){
38876             this.el.setWidth(size);
38877         }
38878     },
38879     
38880     getBox : function(){
38881         if(this.collapsed){
38882             return this.collapsedEl.getBox();
38883         }
38884         var box = this.el.getBox();
38885         if(this.split){
38886             box.width += this.split.el.getWidth();
38887         }
38888         return box;
38889     },
38890     
38891     updateBox : function(box){
38892         if(this.split && !this.collapsed){
38893             var sw = this.split.el.getWidth();
38894             box.width -= sw;
38895             this.split.el.setLeft(box.x+box.width);
38896             this.split.el.setTop(box.y);
38897             this.split.el.setHeight(box.height);
38898         }
38899         if(this.collapsed){
38900             this.updateBody(null, box.height);
38901         }
38902         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38903     }
38904 });Roo.namespace("Roo.bootstrap.panel");/*
38905  * Based on:
38906  * Ext JS Library 1.1.1
38907  * Copyright(c) 2006-2007, Ext JS, LLC.
38908  *
38909  * Originally Released Under LGPL - original licence link has changed is not relivant.
38910  *
38911  * Fork - LGPL
38912  * <script type="text/javascript">
38913  */
38914 /**
38915  * @class Roo.ContentPanel
38916  * @extends Roo.util.Observable
38917  * A basic ContentPanel element.
38918  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
38919  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
38920  * @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
38921  * @cfg {Boolean}   closable      True if the panel can be closed/removed
38922  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
38923  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
38924  * @cfg {Toolbar}   toolbar       A toolbar for this panel
38925  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
38926  * @cfg {String} title          The title for this panel
38927  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
38928  * @cfg {String} url            Calls {@link #setUrl} with this value
38929  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
38930  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
38931  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
38932  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
38933  * @cfg {Boolean} badges render the badges
38934
38935  * @constructor
38936  * Create a new ContentPanel.
38937  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
38938  * @param {String/Object} config A string to set only the title or a config object
38939  * @param {String} content (optional) Set the HTML content for this panel
38940  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
38941  */
38942 Roo.bootstrap.panel.Content = function( config){
38943     
38944     this.tpl = config.tpl || false;
38945     
38946     var el = config.el;
38947     var content = config.content;
38948
38949     if(config.autoCreate){ // xtype is available if this is called from factory
38950         el = Roo.id();
38951     }
38952     this.el = Roo.get(el);
38953     if(!this.el && config && config.autoCreate){
38954         if(typeof config.autoCreate == "object"){
38955             if(!config.autoCreate.id){
38956                 config.autoCreate.id = config.id||el;
38957             }
38958             this.el = Roo.DomHelper.append(document.body,
38959                         config.autoCreate, true);
38960         }else{
38961             var elcfg =  {   tag: "div",
38962                             cls: "roo-layout-inactive-content",
38963                             id: config.id||el
38964                             };
38965             if (config.html) {
38966                 elcfg.html = config.html;
38967                 
38968             }
38969                         
38970             this.el = Roo.DomHelper.append(document.body, elcfg , true);
38971         }
38972     } 
38973     this.closable = false;
38974     this.loaded = false;
38975     this.active = false;
38976    
38977       
38978     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
38979         
38980         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
38981         
38982         this.wrapEl = this.el; //this.el.wrap();
38983         var ti = [];
38984         if (config.toolbar.items) {
38985             ti = config.toolbar.items ;
38986             delete config.toolbar.items ;
38987         }
38988         
38989         var nitems = [];
38990         this.toolbar.render(this.wrapEl, 'before');
38991         for(var i =0;i < ti.length;i++) {
38992           //  Roo.log(['add child', items[i]]);
38993             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
38994         }
38995         this.toolbar.items = nitems;
38996         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
38997         delete config.toolbar;
38998         
38999     }
39000     /*
39001     // xtype created footer. - not sure if will work as we normally have to render first..
39002     if (this.footer && !this.footer.el && this.footer.xtype) {
39003         if (!this.wrapEl) {
39004             this.wrapEl = this.el.wrap();
39005         }
39006     
39007         this.footer.container = this.wrapEl.createChild();
39008          
39009         this.footer = Roo.factory(this.footer, Roo);
39010         
39011     }
39012     */
39013     
39014      if(typeof config == "string"){
39015         this.title = config;
39016     }else{
39017         Roo.apply(this, config);
39018     }
39019     
39020     if(this.resizeEl){
39021         this.resizeEl = Roo.get(this.resizeEl, true);
39022     }else{
39023         this.resizeEl = this.el;
39024     }
39025     // handle view.xtype
39026     
39027  
39028     
39029     
39030     this.addEvents({
39031         /**
39032          * @event activate
39033          * Fires when this panel is activated. 
39034          * @param {Roo.ContentPanel} this
39035          */
39036         "activate" : true,
39037         /**
39038          * @event deactivate
39039          * Fires when this panel is activated. 
39040          * @param {Roo.ContentPanel} this
39041          */
39042         "deactivate" : true,
39043
39044         /**
39045          * @event resize
39046          * Fires when this panel is resized if fitToFrame is true.
39047          * @param {Roo.ContentPanel} this
39048          * @param {Number} width The width after any component adjustments
39049          * @param {Number} height The height after any component adjustments
39050          */
39051         "resize" : true,
39052         
39053          /**
39054          * @event render
39055          * Fires when this tab is created
39056          * @param {Roo.ContentPanel} this
39057          */
39058         "render" : true
39059         
39060         
39061         
39062     });
39063     
39064
39065     
39066     
39067     if(this.autoScroll){
39068         this.resizeEl.setStyle("overflow", "auto");
39069     } else {
39070         // fix randome scrolling
39071         //this.el.on('scroll', function() {
39072         //    Roo.log('fix random scolling');
39073         //    this.scrollTo('top',0); 
39074         //});
39075     }
39076     content = content || this.content;
39077     if(content){
39078         this.setContent(content);
39079     }
39080     if(config && config.url){
39081         this.setUrl(this.url, this.params, this.loadOnce);
39082     }
39083     
39084     
39085     
39086     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39087     
39088     if (this.view && typeof(this.view.xtype) != 'undefined') {
39089         this.view.el = this.el.appendChild(document.createElement("div"));
39090         this.view = Roo.factory(this.view); 
39091         this.view.render  &&  this.view.render(false, '');  
39092     }
39093     
39094     
39095     this.fireEvent('render', this);
39096 };
39097
39098 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39099     
39100     tabTip : '',
39101     
39102     setRegion : function(region){
39103         this.region = region;
39104         this.setActiveClass(region && !this.background);
39105     },
39106     
39107     
39108     setActiveClass: function(state)
39109     {
39110         if(state){
39111            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39112            this.el.setStyle('position','relative');
39113         }else{
39114            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39115            this.el.setStyle('position', 'absolute');
39116         } 
39117     },
39118     
39119     /**
39120      * Returns the toolbar for this Panel if one was configured. 
39121      * @return {Roo.Toolbar} 
39122      */
39123     getToolbar : function(){
39124         return this.toolbar;
39125     },
39126     
39127     setActiveState : function(active)
39128     {
39129         this.active = active;
39130         this.setActiveClass(active);
39131         if(!active){
39132             if(this.fireEvent("deactivate", this) === false){
39133                 return false;
39134             }
39135             return true;
39136         }
39137         this.fireEvent("activate", this);
39138         return true;
39139     },
39140     /**
39141      * Updates this panel's element
39142      * @param {String} content The new content
39143      * @param {Boolean} loadScripts (optional) true to look for and process scripts
39144     */
39145     setContent : function(content, loadScripts){
39146         this.el.update(content, loadScripts);
39147     },
39148
39149     ignoreResize : function(w, h){
39150         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39151             return true;
39152         }else{
39153             this.lastSize = {width: w, height: h};
39154             return false;
39155         }
39156     },
39157     /**
39158      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39159      * @return {Roo.UpdateManager} The UpdateManager
39160      */
39161     getUpdateManager : function(){
39162         return this.el.getUpdateManager();
39163     },
39164      /**
39165      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39166      * @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:
39167 <pre><code>
39168 panel.load({
39169     url: "your-url.php",
39170     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39171     callback: yourFunction,
39172     scope: yourObject, //(optional scope)
39173     discardUrl: false,
39174     nocache: false,
39175     text: "Loading...",
39176     timeout: 30,
39177     scripts: false
39178 });
39179 </code></pre>
39180      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39181      * 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.
39182      * @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}
39183      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39184      * @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.
39185      * @return {Roo.ContentPanel} this
39186      */
39187     load : function(){
39188         var um = this.el.getUpdateManager();
39189         um.update.apply(um, arguments);
39190         return this;
39191     },
39192
39193
39194     /**
39195      * 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.
39196      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39197      * @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)
39198      * @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)
39199      * @return {Roo.UpdateManager} The UpdateManager
39200      */
39201     setUrl : function(url, params, loadOnce){
39202         if(this.refreshDelegate){
39203             this.removeListener("activate", this.refreshDelegate);
39204         }
39205         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39206         this.on("activate", this.refreshDelegate);
39207         return this.el.getUpdateManager();
39208     },
39209     
39210     _handleRefresh : function(url, params, loadOnce){
39211         if(!loadOnce || !this.loaded){
39212             var updater = this.el.getUpdateManager();
39213             updater.update(url, params, this._setLoaded.createDelegate(this));
39214         }
39215     },
39216     
39217     _setLoaded : function(){
39218         this.loaded = true;
39219     }, 
39220     
39221     /**
39222      * Returns this panel's id
39223      * @return {String} 
39224      */
39225     getId : function(){
39226         return this.el.id;
39227     },
39228     
39229     /** 
39230      * Returns this panel's element - used by regiosn to add.
39231      * @return {Roo.Element} 
39232      */
39233     getEl : function(){
39234         return this.wrapEl || this.el;
39235     },
39236     
39237    
39238     
39239     adjustForComponents : function(width, height)
39240     {
39241         //Roo.log('adjustForComponents ');
39242         if(this.resizeEl != this.el){
39243             width -= this.el.getFrameWidth('lr');
39244             height -= this.el.getFrameWidth('tb');
39245         }
39246         if(this.toolbar){
39247             var te = this.toolbar.getEl();
39248             te.setWidth(width);
39249             height -= te.getHeight();
39250         }
39251         if(this.footer){
39252             var te = this.footer.getEl();
39253             te.setWidth(width);
39254             height -= te.getHeight();
39255         }
39256         
39257         
39258         if(this.adjustments){
39259             width += this.adjustments[0];
39260             height += this.adjustments[1];
39261         }
39262         return {"width": width, "height": height};
39263     },
39264     
39265     setSize : function(width, height){
39266         if(this.fitToFrame && !this.ignoreResize(width, height)){
39267             if(this.fitContainer && this.resizeEl != this.el){
39268                 this.el.setSize(width, height);
39269             }
39270             var size = this.adjustForComponents(width, height);
39271             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39272             this.fireEvent('resize', this, size.width, size.height);
39273         }
39274     },
39275     
39276     /**
39277      * Returns this panel's title
39278      * @return {String} 
39279      */
39280     getTitle : function(){
39281         
39282         if (typeof(this.title) != 'object') {
39283             return this.title;
39284         }
39285         
39286         var t = '';
39287         for (var k in this.title) {
39288             if (!this.title.hasOwnProperty(k)) {
39289                 continue;
39290             }
39291             
39292             if (k.indexOf('-') >= 0) {
39293                 var s = k.split('-');
39294                 for (var i = 0; i<s.length; i++) {
39295                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
39296                 }
39297             } else {
39298                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
39299             }
39300         }
39301         return t;
39302     },
39303     
39304     /**
39305      * Set this panel's title
39306      * @param {String} title
39307      */
39308     setTitle : function(title){
39309         this.title = title;
39310         if(this.region){
39311             this.region.updatePanelTitle(this, title);
39312         }
39313     },
39314     
39315     /**
39316      * Returns true is this panel was configured to be closable
39317      * @return {Boolean} 
39318      */
39319     isClosable : function(){
39320         return this.closable;
39321     },
39322     
39323     beforeSlide : function(){
39324         this.el.clip();
39325         this.resizeEl.clip();
39326     },
39327     
39328     afterSlide : function(){
39329         this.el.unclip();
39330         this.resizeEl.unclip();
39331     },
39332     
39333     /**
39334      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
39335      *   Will fail silently if the {@link #setUrl} method has not been called.
39336      *   This does not activate the panel, just updates its content.
39337      */
39338     refresh : function(){
39339         if(this.refreshDelegate){
39340            this.loaded = false;
39341            this.refreshDelegate();
39342         }
39343     },
39344     
39345     /**
39346      * Destroys this panel
39347      */
39348     destroy : function(){
39349         this.el.removeAllListeners();
39350         var tempEl = document.createElement("span");
39351         tempEl.appendChild(this.el.dom);
39352         tempEl.innerHTML = "";
39353         this.el.remove();
39354         this.el = null;
39355     },
39356     
39357     /**
39358      * form - if the content panel contains a form - this is a reference to it.
39359      * @type {Roo.form.Form}
39360      */
39361     form : false,
39362     /**
39363      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
39364      *    This contains a reference to it.
39365      * @type {Roo.View}
39366      */
39367     view : false,
39368     
39369       /**
39370      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
39371      * <pre><code>
39372
39373 layout.addxtype({
39374        xtype : 'Form',
39375        items: [ .... ]
39376    }
39377 );
39378
39379 </code></pre>
39380      * @param {Object} cfg Xtype definition of item to add.
39381      */
39382     
39383     
39384     getChildContainer: function () {
39385         return this.getEl();
39386     }
39387     
39388     
39389     /*
39390         var  ret = new Roo.factory(cfg);
39391         return ret;
39392         
39393         
39394         // add form..
39395         if (cfg.xtype.match(/^Form$/)) {
39396             
39397             var el;
39398             //if (this.footer) {
39399             //    el = this.footer.container.insertSibling(false, 'before');
39400             //} else {
39401                 el = this.el.createChild();
39402             //}
39403
39404             this.form = new  Roo.form.Form(cfg);
39405             
39406             
39407             if ( this.form.allItems.length) {
39408                 this.form.render(el.dom);
39409             }
39410             return this.form;
39411         }
39412         // should only have one of theses..
39413         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
39414             // views.. should not be just added - used named prop 'view''
39415             
39416             cfg.el = this.el.appendChild(document.createElement("div"));
39417             // factory?
39418             
39419             var ret = new Roo.factory(cfg);
39420              
39421              ret.render && ret.render(false, ''); // render blank..
39422             this.view = ret;
39423             return ret;
39424         }
39425         return false;
39426     }
39427     \*/
39428 });
39429  
39430 /**
39431  * @class Roo.bootstrap.panel.Grid
39432  * @extends Roo.bootstrap.panel.Content
39433  * @constructor
39434  * Create a new GridPanel.
39435  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
39436  * @param {Object} config A the config object
39437   
39438  */
39439
39440
39441
39442 Roo.bootstrap.panel.Grid = function(config)
39443 {
39444     
39445       
39446     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
39447         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
39448
39449     config.el = this.wrapper;
39450     //this.el = this.wrapper;
39451     
39452       if (config.container) {
39453         // ctor'ed from a Border/panel.grid
39454         
39455         
39456         this.wrapper.setStyle("overflow", "hidden");
39457         this.wrapper.addClass('roo-grid-container');
39458
39459     }
39460     
39461     
39462     if(config.toolbar){
39463         var tool_el = this.wrapper.createChild();    
39464         this.toolbar = Roo.factory(config.toolbar);
39465         var ti = [];
39466         if (config.toolbar.items) {
39467             ti = config.toolbar.items ;
39468             delete config.toolbar.items ;
39469         }
39470         
39471         var nitems = [];
39472         this.toolbar.render(tool_el);
39473         for(var i =0;i < ti.length;i++) {
39474           //  Roo.log(['add child', items[i]]);
39475             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39476         }
39477         this.toolbar.items = nitems;
39478         
39479         delete config.toolbar;
39480     }
39481     
39482     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
39483     config.grid.scrollBody = true;;
39484     config.grid.monitorWindowResize = false; // turn off autosizing
39485     config.grid.autoHeight = false;
39486     config.grid.autoWidth = false;
39487     
39488     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
39489     
39490     if (config.background) {
39491         // render grid on panel activation (if panel background)
39492         this.on('activate', function(gp) {
39493             if (!gp.grid.rendered) {
39494                 gp.grid.render(this.wrapper);
39495                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
39496             }
39497         });
39498             
39499     } else {
39500         this.grid.render(this.wrapper);
39501         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
39502
39503     }
39504     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
39505     // ??? needed ??? config.el = this.wrapper;
39506     
39507     
39508     
39509   
39510     // xtype created footer. - not sure if will work as we normally have to render first..
39511     if (this.footer && !this.footer.el && this.footer.xtype) {
39512         
39513         var ctr = this.grid.getView().getFooterPanel(true);
39514         this.footer.dataSource = this.grid.dataSource;
39515         this.footer = Roo.factory(this.footer, Roo);
39516         this.footer.render(ctr);
39517         
39518     }
39519     
39520     
39521     
39522     
39523      
39524 };
39525
39526 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
39527     getId : function(){
39528         return this.grid.id;
39529     },
39530     
39531     /**
39532      * Returns the grid for this panel
39533      * @return {Roo.bootstrap.Table} 
39534      */
39535     getGrid : function(){
39536         return this.grid;    
39537     },
39538     
39539     setSize : function(width, height){
39540         if(!this.ignoreResize(width, height)){
39541             var grid = this.grid;
39542             var size = this.adjustForComponents(width, height);
39543             var gridel = grid.getGridEl();
39544             gridel.setSize(size.width, size.height);
39545             /*
39546             var thd = grid.getGridEl().select('thead',true).first();
39547             var tbd = grid.getGridEl().select('tbody', true).first();
39548             if (tbd) {
39549                 tbd.setSize(width, height - thd.getHeight());
39550             }
39551             */
39552             grid.autoSize();
39553         }
39554     },
39555      
39556     
39557     
39558     beforeSlide : function(){
39559         this.grid.getView().scroller.clip();
39560     },
39561     
39562     afterSlide : function(){
39563         this.grid.getView().scroller.unclip();
39564     },
39565     
39566     destroy : function(){
39567         this.grid.destroy();
39568         delete this.grid;
39569         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
39570     }
39571 });
39572
39573 /**
39574  * @class Roo.bootstrap.panel.Nest
39575  * @extends Roo.bootstrap.panel.Content
39576  * @constructor
39577  * Create a new Panel, that can contain a layout.Border.
39578  * 
39579  * 
39580  * @param {Roo.BorderLayout} layout The layout for this panel
39581  * @param {String/Object} config A string to set only the title or a config object
39582  */
39583 Roo.bootstrap.panel.Nest = function(config)
39584 {
39585     // construct with only one argument..
39586     /* FIXME - implement nicer consturctors
39587     if (layout.layout) {
39588         config = layout;
39589         layout = config.layout;
39590         delete config.layout;
39591     }
39592     if (layout.xtype && !layout.getEl) {
39593         // then layout needs constructing..
39594         layout = Roo.factory(layout, Roo);
39595     }
39596     */
39597     
39598     config.el =  config.layout.getEl();
39599     
39600     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
39601     
39602     config.layout.monitorWindowResize = false; // turn off autosizing
39603     this.layout = config.layout;
39604     this.layout.getEl().addClass("roo-layout-nested-layout");
39605     this.layout.parent = this;
39606     
39607     
39608     
39609     
39610 };
39611
39612 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
39613
39614     setSize : function(width, height){
39615         if(!this.ignoreResize(width, height)){
39616             var size = this.adjustForComponents(width, height);
39617             var el = this.layout.getEl();
39618             if (size.height < 1) {
39619                 el.setWidth(size.width);   
39620             } else {
39621                 el.setSize(size.width, size.height);
39622             }
39623             var touch = el.dom.offsetWidth;
39624             this.layout.layout();
39625             // ie requires a double layout on the first pass
39626             if(Roo.isIE && !this.initialized){
39627                 this.initialized = true;
39628                 this.layout.layout();
39629             }
39630         }
39631     },
39632     
39633     // activate all subpanels if not currently active..
39634     
39635     setActiveState : function(active){
39636         this.active = active;
39637         this.setActiveClass(active);
39638         
39639         if(!active){
39640             this.fireEvent("deactivate", this);
39641             return;
39642         }
39643         
39644         this.fireEvent("activate", this);
39645         // not sure if this should happen before or after..
39646         if (!this.layout) {
39647             return; // should not happen..
39648         }
39649         var reg = false;
39650         for (var r in this.layout.regions) {
39651             reg = this.layout.getRegion(r);
39652             if (reg.getActivePanel()) {
39653                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
39654                 reg.setActivePanel(reg.getActivePanel());
39655                 continue;
39656             }
39657             if (!reg.panels.length) {
39658                 continue;
39659             }
39660             reg.showPanel(reg.getPanel(0));
39661         }
39662         
39663         
39664         
39665         
39666     },
39667     
39668     /**
39669      * Returns the nested BorderLayout for this panel
39670      * @return {Roo.BorderLayout} 
39671      */
39672     getLayout : function(){
39673         return this.layout;
39674     },
39675     
39676      /**
39677      * Adds a xtype elements to the layout of the nested panel
39678      * <pre><code>
39679
39680 panel.addxtype({
39681        xtype : 'ContentPanel',
39682        region: 'west',
39683        items: [ .... ]
39684    }
39685 );
39686
39687 panel.addxtype({
39688         xtype : 'NestedLayoutPanel',
39689         region: 'west',
39690         layout: {
39691            center: { },
39692            west: { }   
39693         },
39694         items : [ ... list of content panels or nested layout panels.. ]
39695    }
39696 );
39697 </code></pre>
39698      * @param {Object} cfg Xtype definition of item to add.
39699      */
39700     addxtype : function(cfg) {
39701         return this.layout.addxtype(cfg);
39702     
39703     }
39704 });/*
39705  * Based on:
39706  * Ext JS Library 1.1.1
39707  * Copyright(c) 2006-2007, Ext JS, LLC.
39708  *
39709  * Originally Released Under LGPL - original licence link has changed is not relivant.
39710  *
39711  * Fork - LGPL
39712  * <script type="text/javascript">
39713  */
39714 /**
39715  * @class Roo.TabPanel
39716  * @extends Roo.util.Observable
39717  * A lightweight tab container.
39718  * <br><br>
39719  * Usage:
39720  * <pre><code>
39721 // basic tabs 1, built from existing content
39722 var tabs = new Roo.TabPanel("tabs1");
39723 tabs.addTab("script", "View Script");
39724 tabs.addTab("markup", "View Markup");
39725 tabs.activate("script");
39726
39727 // more advanced tabs, built from javascript
39728 var jtabs = new Roo.TabPanel("jtabs");
39729 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
39730
39731 // set up the UpdateManager
39732 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
39733 var updater = tab2.getUpdateManager();
39734 updater.setDefaultUrl("ajax1.htm");
39735 tab2.on('activate', updater.refresh, updater, true);
39736
39737 // Use setUrl for Ajax loading
39738 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
39739 tab3.setUrl("ajax2.htm", null, true);
39740
39741 // Disabled tab
39742 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
39743 tab4.disable();
39744
39745 jtabs.activate("jtabs-1");
39746  * </code></pre>
39747  * @constructor
39748  * Create a new TabPanel.
39749  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
39750  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
39751  */
39752 Roo.bootstrap.panel.Tabs = function(config){
39753     /**
39754     * The container element for this TabPanel.
39755     * @type Roo.Element
39756     */
39757     this.el = Roo.get(config.el);
39758     delete config.el;
39759     if(config){
39760         if(typeof config == "boolean"){
39761             this.tabPosition = config ? "bottom" : "top";
39762         }else{
39763             Roo.apply(this, config);
39764         }
39765     }
39766     
39767     if(this.tabPosition == "bottom"){
39768         // if tabs are at the bottom = create the body first.
39769         this.bodyEl = Roo.get(this.createBody(this.el.dom));
39770         this.el.addClass("roo-tabs-bottom");
39771     }
39772     // next create the tabs holders
39773     
39774     if (this.tabPosition == "west"){
39775         
39776         var reg = this.region; // fake it..
39777         while (reg) {
39778             if (!reg.mgr.parent) {
39779                 break;
39780             }
39781             reg = reg.mgr.parent.region;
39782         }
39783         Roo.log("got nest?");
39784         Roo.log(reg);
39785         if (reg.mgr.getRegion('west')) {
39786             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
39787             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
39788             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
39789             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
39790             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
39791         
39792             
39793         }
39794         
39795         
39796     } else {
39797      
39798         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
39799         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
39800         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
39801         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
39802     }
39803     
39804     
39805     if(Roo.isIE){
39806         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
39807     }
39808     
39809     // finally - if tabs are at the top, then create the body last..
39810     if(this.tabPosition != "bottom"){
39811         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
39812          * @type Roo.Element
39813          */
39814         this.bodyEl = Roo.get(this.createBody(this.el.dom));
39815         this.el.addClass("roo-tabs-top");
39816     }
39817     this.items = [];
39818
39819     this.bodyEl.setStyle("position", "relative");
39820
39821     this.active = null;
39822     this.activateDelegate = this.activate.createDelegate(this);
39823
39824     this.addEvents({
39825         /**
39826          * @event tabchange
39827          * Fires when the active tab changes
39828          * @param {Roo.TabPanel} this
39829          * @param {Roo.TabPanelItem} activePanel The new active tab
39830          */
39831         "tabchange": true,
39832         /**
39833          * @event beforetabchange
39834          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
39835          * @param {Roo.TabPanel} this
39836          * @param {Object} e Set cancel to true on this object to cancel the tab change
39837          * @param {Roo.TabPanelItem} tab The tab being changed to
39838          */
39839         "beforetabchange" : true
39840     });
39841
39842     Roo.EventManager.onWindowResize(this.onResize, this);
39843     this.cpad = this.el.getPadding("lr");
39844     this.hiddenCount = 0;
39845
39846
39847     // toolbar on the tabbar support...
39848     if (this.toolbar) {
39849         alert("no toolbar support yet");
39850         this.toolbar  = false;
39851         /*
39852         var tcfg = this.toolbar;
39853         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
39854         this.toolbar = new Roo.Toolbar(tcfg);
39855         if (Roo.isSafari) {
39856             var tbl = tcfg.container.child('table', true);
39857             tbl.setAttribute('width', '100%');
39858         }
39859         */
39860         
39861     }
39862    
39863
39864
39865     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
39866 };
39867
39868 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
39869     /*
39870      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
39871      */
39872     tabPosition : "top",
39873     /*
39874      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
39875      */
39876     currentTabWidth : 0,
39877     /*
39878      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
39879      */
39880     minTabWidth : 40,
39881     /*
39882      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
39883      */
39884     maxTabWidth : 250,
39885     /*
39886      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
39887      */
39888     preferredTabWidth : 175,
39889     /*
39890      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
39891      */
39892     resizeTabs : false,
39893     /*
39894      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
39895      */
39896     monitorResize : true,
39897     /*
39898      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
39899      */
39900     toolbar : false,  // set by caller..
39901     
39902     region : false, /// set by caller
39903     
39904     disableTooltips : true, // not used yet...
39905
39906     /**
39907      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
39908      * @param {String} id The id of the div to use <b>or create</b>
39909      * @param {String} text The text for the tab
39910      * @param {String} content (optional) Content to put in the TabPanelItem body
39911      * @param {Boolean} closable (optional) True to create a close icon on the tab
39912      * @return {Roo.TabPanelItem} The created TabPanelItem
39913      */
39914     addTab : function(id, text, content, closable, tpl)
39915     {
39916         var item = new Roo.bootstrap.panel.TabItem({
39917             panel: this,
39918             id : id,
39919             text : text,
39920             closable : closable,
39921             tpl : tpl
39922         });
39923         this.addTabItem(item);
39924         if(content){
39925             item.setContent(content);
39926         }
39927         return item;
39928     },
39929
39930     /**
39931      * Returns the {@link Roo.TabPanelItem} with the specified id/index
39932      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
39933      * @return {Roo.TabPanelItem}
39934      */
39935     getTab : function(id){
39936         return this.items[id];
39937     },
39938
39939     /**
39940      * Hides the {@link Roo.TabPanelItem} with the specified id/index
39941      * @param {String/Number} id The id or index of the TabPanelItem to hide.
39942      */
39943     hideTab : function(id){
39944         var t = this.items[id];
39945         if(!t.isHidden()){
39946            t.setHidden(true);
39947            this.hiddenCount++;
39948            this.autoSizeTabs();
39949         }
39950     },
39951
39952     /**
39953      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
39954      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
39955      */
39956     unhideTab : function(id){
39957         var t = this.items[id];
39958         if(t.isHidden()){
39959            t.setHidden(false);
39960            this.hiddenCount--;
39961            this.autoSizeTabs();
39962         }
39963     },
39964
39965     /**
39966      * Adds an existing {@link Roo.TabPanelItem}.
39967      * @param {Roo.TabPanelItem} item The TabPanelItem to add
39968      */
39969     addTabItem : function(item)
39970     {
39971         this.items[item.id] = item;
39972         this.items.push(item);
39973         this.autoSizeTabs();
39974       //  if(this.resizeTabs){
39975     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
39976   //         this.autoSizeTabs();
39977 //        }else{
39978 //            item.autoSize();
39979        // }
39980     },
39981
39982     /**
39983      * Removes a {@link Roo.TabPanelItem}.
39984      * @param {String/Number} id The id or index of the TabPanelItem to remove.
39985      */
39986     removeTab : function(id){
39987         var items = this.items;
39988         var tab = items[id];
39989         if(!tab) { return; }
39990         var index = items.indexOf(tab);
39991         if(this.active == tab && items.length > 1){
39992             var newTab = this.getNextAvailable(index);
39993             if(newTab) {
39994                 newTab.activate();
39995             }
39996         }
39997         this.stripEl.dom.removeChild(tab.pnode.dom);
39998         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
39999             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40000         }
40001         items.splice(index, 1);
40002         delete this.items[tab.id];
40003         tab.fireEvent("close", tab);
40004         tab.purgeListeners();
40005         this.autoSizeTabs();
40006     },
40007
40008     getNextAvailable : function(start){
40009         var items = this.items;
40010         var index = start;
40011         // look for a next tab that will slide over to
40012         // replace the one being removed
40013         while(index < items.length){
40014             var item = items[++index];
40015             if(item && !item.isHidden()){
40016                 return item;
40017             }
40018         }
40019         // if one isn't found select the previous tab (on the left)
40020         index = start;
40021         while(index >= 0){
40022             var item = items[--index];
40023             if(item && !item.isHidden()){
40024                 return item;
40025             }
40026         }
40027         return null;
40028     },
40029
40030     /**
40031      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40032      * @param {String/Number} id The id or index of the TabPanelItem to disable.
40033      */
40034     disableTab : function(id){
40035         var tab = this.items[id];
40036         if(tab && this.active != tab){
40037             tab.disable();
40038         }
40039     },
40040
40041     /**
40042      * Enables a {@link Roo.TabPanelItem} that is disabled.
40043      * @param {String/Number} id The id or index of the TabPanelItem to enable.
40044      */
40045     enableTab : function(id){
40046         var tab = this.items[id];
40047         tab.enable();
40048     },
40049
40050     /**
40051      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40052      * @param {String/Number} id The id or index of the TabPanelItem to activate.
40053      * @return {Roo.TabPanelItem} The TabPanelItem.
40054      */
40055     activate : function(id)
40056     {
40057         //Roo.log('activite:'  + id);
40058         
40059         var tab = this.items[id];
40060         if(!tab){
40061             return null;
40062         }
40063         if(tab == this.active || tab.disabled){
40064             return tab;
40065         }
40066         var e = {};
40067         this.fireEvent("beforetabchange", this, e, tab);
40068         if(e.cancel !== true && !tab.disabled){
40069             if(this.active){
40070                 this.active.hide();
40071             }
40072             this.active = this.items[id];
40073             this.active.show();
40074             this.fireEvent("tabchange", this, this.active);
40075         }
40076         return tab;
40077     },
40078
40079     /**
40080      * Gets the active {@link Roo.TabPanelItem}.
40081      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40082      */
40083     getActiveTab : function(){
40084         return this.active;
40085     },
40086
40087     /**
40088      * Updates the tab body element to fit the height of the container element
40089      * for overflow scrolling
40090      * @param {Number} targetHeight (optional) Override the starting height from the elements height
40091      */
40092     syncHeight : function(targetHeight){
40093         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40094         var bm = this.bodyEl.getMargins();
40095         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40096         this.bodyEl.setHeight(newHeight);
40097         return newHeight;
40098     },
40099
40100     onResize : function(){
40101         if(this.monitorResize){
40102             this.autoSizeTabs();
40103         }
40104     },
40105
40106     /**
40107      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40108      */
40109     beginUpdate : function(){
40110         this.updating = true;
40111     },
40112
40113     /**
40114      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40115      */
40116     endUpdate : function(){
40117         this.updating = false;
40118         this.autoSizeTabs();
40119     },
40120
40121     /**
40122      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40123      */
40124     autoSizeTabs : function()
40125     {
40126         var count = this.items.length;
40127         var vcount = count - this.hiddenCount;
40128         
40129         if (vcount < 2) {
40130             this.stripEl.hide();
40131         } else {
40132             this.stripEl.show();
40133         }
40134         
40135         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40136             return;
40137         }
40138         
40139         
40140         var w = Math.max(this.el.getWidth() - this.cpad, 10);
40141         var availWidth = Math.floor(w / vcount);
40142         var b = this.stripBody;
40143         if(b.getWidth() > w){
40144             var tabs = this.items;
40145             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40146             if(availWidth < this.minTabWidth){
40147                 /*if(!this.sleft){    // incomplete scrolling code
40148                     this.createScrollButtons();
40149                 }
40150                 this.showScroll();
40151                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40152             }
40153         }else{
40154             if(this.currentTabWidth < this.preferredTabWidth){
40155                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40156             }
40157         }
40158     },
40159
40160     /**
40161      * Returns the number of tabs in this TabPanel.
40162      * @return {Number}
40163      */
40164      getCount : function(){
40165          return this.items.length;
40166      },
40167
40168     /**
40169      * Resizes all the tabs to the passed width
40170      * @param {Number} The new width
40171      */
40172     setTabWidth : function(width){
40173         this.currentTabWidth = width;
40174         for(var i = 0, len = this.items.length; i < len; i++) {
40175                 if(!this.items[i].isHidden()) {
40176                 this.items[i].setWidth(width);
40177             }
40178         }
40179     },
40180
40181     /**
40182      * Destroys this TabPanel
40183      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40184      */
40185     destroy : function(removeEl){
40186         Roo.EventManager.removeResizeListener(this.onResize, this);
40187         for(var i = 0, len = this.items.length; i < len; i++){
40188             this.items[i].purgeListeners();
40189         }
40190         if(removeEl === true){
40191             this.el.update("");
40192             this.el.remove();
40193         }
40194     },
40195     
40196     createStrip : function(container)
40197     {
40198         var strip = document.createElement("nav");
40199         strip.className = Roo.bootstrap.version == 4 ?
40200             "navbar-light bg-light" : 
40201             "navbar navbar-default"; //"x-tabs-wrap";
40202         container.appendChild(strip);
40203         return strip;
40204     },
40205     
40206     createStripList : function(strip)
40207     {
40208         // div wrapper for retard IE
40209         // returns the "tr" element.
40210         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40211         //'<div class="x-tabs-strip-wrap">'+
40212           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40213           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40214         return strip.firstChild; //.firstChild.firstChild.firstChild;
40215     },
40216     createBody : function(container)
40217     {
40218         var body = document.createElement("div");
40219         Roo.id(body, "tab-body");
40220         //Roo.fly(body).addClass("x-tabs-body");
40221         Roo.fly(body).addClass("tab-content");
40222         container.appendChild(body);
40223         return body;
40224     },
40225     createItemBody :function(bodyEl, id){
40226         var body = Roo.getDom(id);
40227         if(!body){
40228             body = document.createElement("div");
40229             body.id = id;
40230         }
40231         //Roo.fly(body).addClass("x-tabs-item-body");
40232         Roo.fly(body).addClass("tab-pane");
40233          bodyEl.insertBefore(body, bodyEl.firstChild);
40234         return body;
40235     },
40236     /** @private */
40237     createStripElements :  function(stripEl, text, closable, tpl)
40238     {
40239         var td = document.createElement("li"); // was td..
40240         td.className = 'nav-item';
40241         
40242         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40243         
40244         
40245         stripEl.appendChild(td);
40246         /*if(closable){
40247             td.className = "x-tabs-closable";
40248             if(!this.closeTpl){
40249                 this.closeTpl = new Roo.Template(
40250                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40251                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40252                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
40253                 );
40254             }
40255             var el = this.closeTpl.overwrite(td, {"text": text});
40256             var close = el.getElementsByTagName("div")[0];
40257             var inner = el.getElementsByTagName("em")[0];
40258             return {"el": el, "close": close, "inner": inner};
40259         } else {
40260         */
40261         // not sure what this is..
40262 //            if(!this.tabTpl){
40263                 //this.tabTpl = new Roo.Template(
40264                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40265                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40266                 //);
40267 //                this.tabTpl = new Roo.Template(
40268 //                   '<a href="#">' +
40269 //                   '<span unselectable="on"' +
40270 //                            (this.disableTooltips ? '' : ' title="{text}"') +
40271 //                            ' >{text}</span></a>'
40272 //                );
40273 //                
40274 //            }
40275
40276
40277             var template = tpl || this.tabTpl || false;
40278             
40279             if(!template){
40280                 template =  new Roo.Template(
40281                         Roo.bootstrap.version == 4 ? 
40282                             (
40283                                 '<a class="nav-link" href="#" unselectable="on"' +
40284                                      (this.disableTooltips ? '' : ' title="{text}"') +
40285                                      ' >{text}</a>'
40286                             ) : (
40287                                 '<a class="nav-link" href="#">' +
40288                                 '<span unselectable="on"' +
40289                                          (this.disableTooltips ? '' : ' title="{text}"') +
40290                                     ' >{text}</span></a>'
40291                             )
40292                 );
40293             }
40294             
40295             switch (typeof(template)) {
40296                 case 'object' :
40297                     break;
40298                 case 'string' :
40299                     template = new Roo.Template(template);
40300                     break;
40301                 default :
40302                     break;
40303             }
40304             
40305             var el = template.overwrite(td, {"text": text});
40306             
40307             var inner = el.getElementsByTagName("span")[0];
40308             
40309             return {"el": el, "inner": inner};
40310             
40311     }
40312         
40313     
40314 });
40315
40316 /**
40317  * @class Roo.TabPanelItem
40318  * @extends Roo.util.Observable
40319  * Represents an individual item (tab plus body) in a TabPanel.
40320  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
40321  * @param {String} id The id of this TabPanelItem
40322  * @param {String} text The text for the tab of this TabPanelItem
40323  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
40324  */
40325 Roo.bootstrap.panel.TabItem = function(config){
40326     /**
40327      * The {@link Roo.TabPanel} this TabPanelItem belongs to
40328      * @type Roo.TabPanel
40329      */
40330     this.tabPanel = config.panel;
40331     /**
40332      * The id for this TabPanelItem
40333      * @type String
40334      */
40335     this.id = config.id;
40336     /** @private */
40337     this.disabled = false;
40338     /** @private */
40339     this.text = config.text;
40340     /** @private */
40341     this.loaded = false;
40342     this.closable = config.closable;
40343
40344     /**
40345      * The body element for this TabPanelItem.
40346      * @type Roo.Element
40347      */
40348     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
40349     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
40350     this.bodyEl.setStyle("display", "block");
40351     this.bodyEl.setStyle("zoom", "1");
40352     //this.hideAction();
40353
40354     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
40355     /** @private */
40356     this.el = Roo.get(els.el);
40357     this.inner = Roo.get(els.inner, true);
40358      this.textEl = Roo.bootstrap.version == 4 ?
40359         this.el : Roo.get(this.el.dom.firstChild, true);
40360
40361     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
40362     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
40363
40364     
40365 //    this.el.on("mousedown", this.onTabMouseDown, this);
40366     this.el.on("click", this.onTabClick, this);
40367     /** @private */
40368     if(config.closable){
40369         var c = Roo.get(els.close, true);
40370         c.dom.title = this.closeText;
40371         c.addClassOnOver("close-over");
40372         c.on("click", this.closeClick, this);
40373      }
40374
40375     this.addEvents({
40376          /**
40377          * @event activate
40378          * Fires when this tab becomes the active tab.
40379          * @param {Roo.TabPanel} tabPanel The parent TabPanel
40380          * @param {Roo.TabPanelItem} this
40381          */
40382         "activate": true,
40383         /**
40384          * @event beforeclose
40385          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
40386          * @param {Roo.TabPanelItem} this
40387          * @param {Object} e Set cancel to true on this object to cancel the close.
40388          */
40389         "beforeclose": true,
40390         /**
40391          * @event close
40392          * Fires when this tab is closed.
40393          * @param {Roo.TabPanelItem} this
40394          */
40395          "close": true,
40396         /**
40397          * @event deactivate
40398          * Fires when this tab is no longer the active tab.
40399          * @param {Roo.TabPanel} tabPanel The parent TabPanel
40400          * @param {Roo.TabPanelItem} this
40401          */
40402          "deactivate" : true
40403     });
40404     this.hidden = false;
40405
40406     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
40407 };
40408
40409 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
40410            {
40411     purgeListeners : function(){
40412        Roo.util.Observable.prototype.purgeListeners.call(this);
40413        this.el.removeAllListeners();
40414     },
40415     /**
40416      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
40417      */
40418     show : function(){
40419         this.status_node.addClass("active");
40420         this.showAction();
40421         if(Roo.isOpera){
40422             this.tabPanel.stripWrap.repaint();
40423         }
40424         this.fireEvent("activate", this.tabPanel, this);
40425     },
40426
40427     /**
40428      * Returns true if this tab is the active tab.
40429      * @return {Boolean}
40430      */
40431     isActive : function(){
40432         return this.tabPanel.getActiveTab() == this;
40433     },
40434
40435     /**
40436      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
40437      */
40438     hide : function(){
40439         this.status_node.removeClass("active");
40440         this.hideAction();
40441         this.fireEvent("deactivate", this.tabPanel, this);
40442     },
40443
40444     hideAction : function(){
40445         this.bodyEl.hide();
40446         this.bodyEl.setStyle("position", "absolute");
40447         this.bodyEl.setLeft("-20000px");
40448         this.bodyEl.setTop("-20000px");
40449     },
40450
40451     showAction : function(){
40452         this.bodyEl.setStyle("position", "relative");
40453         this.bodyEl.setTop("");
40454         this.bodyEl.setLeft("");
40455         this.bodyEl.show();
40456     },
40457
40458     /**
40459      * Set the tooltip for the tab.
40460      * @param {String} tooltip The tab's tooltip
40461      */
40462     setTooltip : function(text){
40463         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
40464             this.textEl.dom.qtip = text;
40465             this.textEl.dom.removeAttribute('title');
40466         }else{
40467             this.textEl.dom.title = text;
40468         }
40469     },
40470
40471     onTabClick : function(e){
40472         e.preventDefault();
40473         this.tabPanel.activate(this.id);
40474     },
40475
40476     onTabMouseDown : function(e){
40477         e.preventDefault();
40478         this.tabPanel.activate(this.id);
40479     },
40480 /*
40481     getWidth : function(){
40482         return this.inner.getWidth();
40483     },
40484
40485     setWidth : function(width){
40486         var iwidth = width - this.linode.getPadding("lr");
40487         this.inner.setWidth(iwidth);
40488         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
40489         this.linode.setWidth(width);
40490     },
40491 */
40492     /**
40493      * Show or hide the tab
40494      * @param {Boolean} hidden True to hide or false to show.
40495      */
40496     setHidden : function(hidden){
40497         this.hidden = hidden;
40498         this.linode.setStyle("display", hidden ? "none" : "");
40499     },
40500
40501     /**
40502      * Returns true if this tab is "hidden"
40503      * @return {Boolean}
40504      */
40505     isHidden : function(){
40506         return this.hidden;
40507     },
40508
40509     /**
40510      * Returns the text for this tab
40511      * @return {String}
40512      */
40513     getText : function(){
40514         return this.text;
40515     },
40516     /*
40517     autoSize : function(){
40518         //this.el.beginMeasure();
40519         this.textEl.setWidth(1);
40520         /*
40521          *  #2804 [new] Tabs in Roojs
40522          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
40523          */
40524         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
40525         //this.el.endMeasure();
40526     //},
40527
40528     /**
40529      * Sets the text for the tab (Note: this also sets the tooltip text)
40530      * @param {String} text The tab's text and tooltip
40531      */
40532     setText : function(text){
40533         this.text = text;
40534         this.textEl.update(text);
40535         this.setTooltip(text);
40536         //if(!this.tabPanel.resizeTabs){
40537         //    this.autoSize();
40538         //}
40539     },
40540     /**
40541      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
40542      */
40543     activate : function(){
40544         this.tabPanel.activate(this.id);
40545     },
40546
40547     /**
40548      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
40549      */
40550     disable : function(){
40551         if(this.tabPanel.active != this){
40552             this.disabled = true;
40553             this.status_node.addClass("disabled");
40554         }
40555     },
40556
40557     /**
40558      * Enables this TabPanelItem if it was previously disabled.
40559      */
40560     enable : function(){
40561         this.disabled = false;
40562         this.status_node.removeClass("disabled");
40563     },
40564
40565     /**
40566      * Sets the content for this TabPanelItem.
40567      * @param {String} content The content
40568      * @param {Boolean} loadScripts true to look for and load scripts
40569      */
40570     setContent : function(content, loadScripts){
40571         this.bodyEl.update(content, loadScripts);
40572     },
40573
40574     /**
40575      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
40576      * @return {Roo.UpdateManager} The UpdateManager
40577      */
40578     getUpdateManager : function(){
40579         return this.bodyEl.getUpdateManager();
40580     },
40581
40582     /**
40583      * Set a URL to be used to load the content for this TabPanelItem.
40584      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
40585      * @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)
40586      * @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)
40587      * @return {Roo.UpdateManager} The UpdateManager
40588      */
40589     setUrl : function(url, params, loadOnce){
40590         if(this.refreshDelegate){
40591             this.un('activate', this.refreshDelegate);
40592         }
40593         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40594         this.on("activate", this.refreshDelegate);
40595         return this.bodyEl.getUpdateManager();
40596     },
40597
40598     /** @private */
40599     _handleRefresh : function(url, params, loadOnce){
40600         if(!loadOnce || !this.loaded){
40601             var updater = this.bodyEl.getUpdateManager();
40602             updater.update(url, params, this._setLoaded.createDelegate(this));
40603         }
40604     },
40605
40606     /**
40607      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
40608      *   Will fail silently if the setUrl method has not been called.
40609      *   This does not activate the panel, just updates its content.
40610      */
40611     refresh : function(){
40612         if(this.refreshDelegate){
40613            this.loaded = false;
40614            this.refreshDelegate();
40615         }
40616     },
40617
40618     /** @private */
40619     _setLoaded : function(){
40620         this.loaded = true;
40621     },
40622
40623     /** @private */
40624     closeClick : function(e){
40625         var o = {};
40626         e.stopEvent();
40627         this.fireEvent("beforeclose", this, o);
40628         if(o.cancel !== true){
40629             this.tabPanel.removeTab(this.id);
40630         }
40631     },
40632     /**
40633      * The text displayed in the tooltip for the close icon.
40634      * @type String
40635      */
40636     closeText : "Close this tab"
40637 });
40638 /**
40639 *    This script refer to:
40640 *    Title: International Telephone Input
40641 *    Author: Jack O'Connor
40642 *    Code version:  v12.1.12
40643 *    Availability: https://github.com/jackocnr/intl-tel-input.git
40644 **/
40645
40646 Roo.bootstrap.PhoneInputData = function() {
40647     var d = [
40648       [
40649         "Afghanistan (‫افغانستان‬‎)",
40650         "af",
40651         "93"
40652       ],
40653       [
40654         "Albania (Shqipëri)",
40655         "al",
40656         "355"
40657       ],
40658       [
40659         "Algeria (‫الجزائر‬‎)",
40660         "dz",
40661         "213"
40662       ],
40663       [
40664         "American Samoa",
40665         "as",
40666         "1684"
40667       ],
40668       [
40669         "Andorra",
40670         "ad",
40671         "376"
40672       ],
40673       [
40674         "Angola",
40675         "ao",
40676         "244"
40677       ],
40678       [
40679         "Anguilla",
40680         "ai",
40681         "1264"
40682       ],
40683       [
40684         "Antigua and Barbuda",
40685         "ag",
40686         "1268"
40687       ],
40688       [
40689         "Argentina",
40690         "ar",
40691         "54"
40692       ],
40693       [
40694         "Armenia (Հայաստան)",
40695         "am",
40696         "374"
40697       ],
40698       [
40699         "Aruba",
40700         "aw",
40701         "297"
40702       ],
40703       [
40704         "Australia",
40705         "au",
40706         "61",
40707         0
40708       ],
40709       [
40710         "Austria (Österreich)",
40711         "at",
40712         "43"
40713       ],
40714       [
40715         "Azerbaijan (Azərbaycan)",
40716         "az",
40717         "994"
40718       ],
40719       [
40720         "Bahamas",
40721         "bs",
40722         "1242"
40723       ],
40724       [
40725         "Bahrain (‫البحرين‬‎)",
40726         "bh",
40727         "973"
40728       ],
40729       [
40730         "Bangladesh (বাংলাদেশ)",
40731         "bd",
40732         "880"
40733       ],
40734       [
40735         "Barbados",
40736         "bb",
40737         "1246"
40738       ],
40739       [
40740         "Belarus (Беларусь)",
40741         "by",
40742         "375"
40743       ],
40744       [
40745         "Belgium (België)",
40746         "be",
40747         "32"
40748       ],
40749       [
40750         "Belize",
40751         "bz",
40752         "501"
40753       ],
40754       [
40755         "Benin (Bénin)",
40756         "bj",
40757         "229"
40758       ],
40759       [
40760         "Bermuda",
40761         "bm",
40762         "1441"
40763       ],
40764       [
40765         "Bhutan (འབྲུག)",
40766         "bt",
40767         "975"
40768       ],
40769       [
40770         "Bolivia",
40771         "bo",
40772         "591"
40773       ],
40774       [
40775         "Bosnia and Herzegovina (Босна и Херцеговина)",
40776         "ba",
40777         "387"
40778       ],
40779       [
40780         "Botswana",
40781         "bw",
40782         "267"
40783       ],
40784       [
40785         "Brazil (Brasil)",
40786         "br",
40787         "55"
40788       ],
40789       [
40790         "British Indian Ocean Territory",
40791         "io",
40792         "246"
40793       ],
40794       [
40795         "British Virgin Islands",
40796         "vg",
40797         "1284"
40798       ],
40799       [
40800         "Brunei",
40801         "bn",
40802         "673"
40803       ],
40804       [
40805         "Bulgaria (България)",
40806         "bg",
40807         "359"
40808       ],
40809       [
40810         "Burkina Faso",
40811         "bf",
40812         "226"
40813       ],
40814       [
40815         "Burundi (Uburundi)",
40816         "bi",
40817         "257"
40818       ],
40819       [
40820         "Cambodia (កម្ពុជា)",
40821         "kh",
40822         "855"
40823       ],
40824       [
40825         "Cameroon (Cameroun)",
40826         "cm",
40827         "237"
40828       ],
40829       [
40830         "Canada",
40831         "ca",
40832         "1",
40833         1,
40834         ["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"]
40835       ],
40836       [
40837         "Cape Verde (Kabu Verdi)",
40838         "cv",
40839         "238"
40840       ],
40841       [
40842         "Caribbean Netherlands",
40843         "bq",
40844         "599",
40845         1
40846       ],
40847       [
40848         "Cayman Islands",
40849         "ky",
40850         "1345"
40851       ],
40852       [
40853         "Central African Republic (République centrafricaine)",
40854         "cf",
40855         "236"
40856       ],
40857       [
40858         "Chad (Tchad)",
40859         "td",
40860         "235"
40861       ],
40862       [
40863         "Chile",
40864         "cl",
40865         "56"
40866       ],
40867       [
40868         "China (中国)",
40869         "cn",
40870         "86"
40871       ],
40872       [
40873         "Christmas Island",
40874         "cx",
40875         "61",
40876         2
40877       ],
40878       [
40879         "Cocos (Keeling) Islands",
40880         "cc",
40881         "61",
40882         1
40883       ],
40884       [
40885         "Colombia",
40886         "co",
40887         "57"
40888       ],
40889       [
40890         "Comoros (‫جزر القمر‬‎)",
40891         "km",
40892         "269"
40893       ],
40894       [
40895         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
40896         "cd",
40897         "243"
40898       ],
40899       [
40900         "Congo (Republic) (Congo-Brazzaville)",
40901         "cg",
40902         "242"
40903       ],
40904       [
40905         "Cook Islands",
40906         "ck",
40907         "682"
40908       ],
40909       [
40910         "Costa Rica",
40911         "cr",
40912         "506"
40913       ],
40914       [
40915         "Côte d’Ivoire",
40916         "ci",
40917         "225"
40918       ],
40919       [
40920         "Croatia (Hrvatska)",
40921         "hr",
40922         "385"
40923       ],
40924       [
40925         "Cuba",
40926         "cu",
40927         "53"
40928       ],
40929       [
40930         "Curaçao",
40931         "cw",
40932         "599",
40933         0
40934       ],
40935       [
40936         "Cyprus (Κύπρος)",
40937         "cy",
40938         "357"
40939       ],
40940       [
40941         "Czech Republic (Česká republika)",
40942         "cz",
40943         "420"
40944       ],
40945       [
40946         "Denmark (Danmark)",
40947         "dk",
40948         "45"
40949       ],
40950       [
40951         "Djibouti",
40952         "dj",
40953         "253"
40954       ],
40955       [
40956         "Dominica",
40957         "dm",
40958         "1767"
40959       ],
40960       [
40961         "Dominican Republic (República Dominicana)",
40962         "do",
40963         "1",
40964         2,
40965         ["809", "829", "849"]
40966       ],
40967       [
40968         "Ecuador",
40969         "ec",
40970         "593"
40971       ],
40972       [
40973         "Egypt (‫مصر‬‎)",
40974         "eg",
40975         "20"
40976       ],
40977       [
40978         "El Salvador",
40979         "sv",
40980         "503"
40981       ],
40982       [
40983         "Equatorial Guinea (Guinea Ecuatorial)",
40984         "gq",
40985         "240"
40986       ],
40987       [
40988         "Eritrea",
40989         "er",
40990         "291"
40991       ],
40992       [
40993         "Estonia (Eesti)",
40994         "ee",
40995         "372"
40996       ],
40997       [
40998         "Ethiopia",
40999         "et",
41000         "251"
41001       ],
41002       [
41003         "Falkland Islands (Islas Malvinas)",
41004         "fk",
41005         "500"
41006       ],
41007       [
41008         "Faroe Islands (Føroyar)",
41009         "fo",
41010         "298"
41011       ],
41012       [
41013         "Fiji",
41014         "fj",
41015         "679"
41016       ],
41017       [
41018         "Finland (Suomi)",
41019         "fi",
41020         "358",
41021         0
41022       ],
41023       [
41024         "France",
41025         "fr",
41026         "33"
41027       ],
41028       [
41029         "French Guiana (Guyane française)",
41030         "gf",
41031         "594"
41032       ],
41033       [
41034         "French Polynesia (Polynésie française)",
41035         "pf",
41036         "689"
41037       ],
41038       [
41039         "Gabon",
41040         "ga",
41041         "241"
41042       ],
41043       [
41044         "Gambia",
41045         "gm",
41046         "220"
41047       ],
41048       [
41049         "Georgia (საქართველო)",
41050         "ge",
41051         "995"
41052       ],
41053       [
41054         "Germany (Deutschland)",
41055         "de",
41056         "49"
41057       ],
41058       [
41059         "Ghana (Gaana)",
41060         "gh",
41061         "233"
41062       ],
41063       [
41064         "Gibraltar",
41065         "gi",
41066         "350"
41067       ],
41068       [
41069         "Greece (Ελλάδα)",
41070         "gr",
41071         "30"
41072       ],
41073       [
41074         "Greenland (Kalaallit Nunaat)",
41075         "gl",
41076         "299"
41077       ],
41078       [
41079         "Grenada",
41080         "gd",
41081         "1473"
41082       ],
41083       [
41084         "Guadeloupe",
41085         "gp",
41086         "590",
41087         0
41088       ],
41089       [
41090         "Guam",
41091         "gu",
41092         "1671"
41093       ],
41094       [
41095         "Guatemala",
41096         "gt",
41097         "502"
41098       ],
41099       [
41100         "Guernsey",
41101         "gg",
41102         "44",
41103         1
41104       ],
41105       [
41106         "Guinea (Guinée)",
41107         "gn",
41108         "224"
41109       ],
41110       [
41111         "Guinea-Bissau (Guiné Bissau)",
41112         "gw",
41113         "245"
41114       ],
41115       [
41116         "Guyana",
41117         "gy",
41118         "592"
41119       ],
41120       [
41121         "Haiti",
41122         "ht",
41123         "509"
41124       ],
41125       [
41126         "Honduras",
41127         "hn",
41128         "504"
41129       ],
41130       [
41131         "Hong Kong (香港)",
41132         "hk",
41133         "852"
41134       ],
41135       [
41136         "Hungary (Magyarország)",
41137         "hu",
41138         "36"
41139       ],
41140       [
41141         "Iceland (Ísland)",
41142         "is",
41143         "354"
41144       ],
41145       [
41146         "India (भारत)",
41147         "in",
41148         "91"
41149       ],
41150       [
41151         "Indonesia",
41152         "id",
41153         "62"
41154       ],
41155       [
41156         "Iran (‫ایران‬‎)",
41157         "ir",
41158         "98"
41159       ],
41160       [
41161         "Iraq (‫العراق‬‎)",
41162         "iq",
41163         "964"
41164       ],
41165       [
41166         "Ireland",
41167         "ie",
41168         "353"
41169       ],
41170       [
41171         "Isle of Man",
41172         "im",
41173         "44",
41174         2
41175       ],
41176       [
41177         "Israel (‫ישראל‬‎)",
41178         "il",
41179         "972"
41180       ],
41181       [
41182         "Italy (Italia)",
41183         "it",
41184         "39",
41185         0
41186       ],
41187       [
41188         "Jamaica",
41189         "jm",
41190         "1876"
41191       ],
41192       [
41193         "Japan (日本)",
41194         "jp",
41195         "81"
41196       ],
41197       [
41198         "Jersey",
41199         "je",
41200         "44",
41201         3
41202       ],
41203       [
41204         "Jordan (‫الأردن‬‎)",
41205         "jo",
41206         "962"
41207       ],
41208       [
41209         "Kazakhstan (Казахстан)",
41210         "kz",
41211         "7",
41212         1
41213       ],
41214       [
41215         "Kenya",
41216         "ke",
41217         "254"
41218       ],
41219       [
41220         "Kiribati",
41221         "ki",
41222         "686"
41223       ],
41224       [
41225         "Kosovo",
41226         "xk",
41227         "383"
41228       ],
41229       [
41230         "Kuwait (‫الكويت‬‎)",
41231         "kw",
41232         "965"
41233       ],
41234       [
41235         "Kyrgyzstan (Кыргызстан)",
41236         "kg",
41237         "996"
41238       ],
41239       [
41240         "Laos (ລາວ)",
41241         "la",
41242         "856"
41243       ],
41244       [
41245         "Latvia (Latvija)",
41246         "lv",
41247         "371"
41248       ],
41249       [
41250         "Lebanon (‫لبنان‬‎)",
41251         "lb",
41252         "961"
41253       ],
41254       [
41255         "Lesotho",
41256         "ls",
41257         "266"
41258       ],
41259       [
41260         "Liberia",
41261         "lr",
41262         "231"
41263       ],
41264       [
41265         "Libya (‫ليبيا‬‎)",
41266         "ly",
41267         "218"
41268       ],
41269       [
41270         "Liechtenstein",
41271         "li",
41272         "423"
41273       ],
41274       [
41275         "Lithuania (Lietuva)",
41276         "lt",
41277         "370"
41278       ],
41279       [
41280         "Luxembourg",
41281         "lu",
41282         "352"
41283       ],
41284       [
41285         "Macau (澳門)",
41286         "mo",
41287         "853"
41288       ],
41289       [
41290         "Macedonia (FYROM) (Македонија)",
41291         "mk",
41292         "389"
41293       ],
41294       [
41295         "Madagascar (Madagasikara)",
41296         "mg",
41297         "261"
41298       ],
41299       [
41300         "Malawi",
41301         "mw",
41302         "265"
41303       ],
41304       [
41305         "Malaysia",
41306         "my",
41307         "60"
41308       ],
41309       [
41310         "Maldives",
41311         "mv",
41312         "960"
41313       ],
41314       [
41315         "Mali",
41316         "ml",
41317         "223"
41318       ],
41319       [
41320         "Malta",
41321         "mt",
41322         "356"
41323       ],
41324       [
41325         "Marshall Islands",
41326         "mh",
41327         "692"
41328       ],
41329       [
41330         "Martinique",
41331         "mq",
41332         "596"
41333       ],
41334       [
41335         "Mauritania (‫موريتانيا‬‎)",
41336         "mr",
41337         "222"
41338       ],
41339       [
41340         "Mauritius (Moris)",
41341         "mu",
41342         "230"
41343       ],
41344       [
41345         "Mayotte",
41346         "yt",
41347         "262",
41348         1
41349       ],
41350       [
41351         "Mexico (México)",
41352         "mx",
41353         "52"
41354       ],
41355       [
41356         "Micronesia",
41357         "fm",
41358         "691"
41359       ],
41360       [
41361         "Moldova (Republica Moldova)",
41362         "md",
41363         "373"
41364       ],
41365       [
41366         "Monaco",
41367         "mc",
41368         "377"
41369       ],
41370       [
41371         "Mongolia (Монгол)",
41372         "mn",
41373         "976"
41374       ],
41375       [
41376         "Montenegro (Crna Gora)",
41377         "me",
41378         "382"
41379       ],
41380       [
41381         "Montserrat",
41382         "ms",
41383         "1664"
41384       ],
41385       [
41386         "Morocco (‫المغرب‬‎)",
41387         "ma",
41388         "212",
41389         0
41390       ],
41391       [
41392         "Mozambique (Moçambique)",
41393         "mz",
41394         "258"
41395       ],
41396       [
41397         "Myanmar (Burma) (မြန်မာ)",
41398         "mm",
41399         "95"
41400       ],
41401       [
41402         "Namibia (Namibië)",
41403         "na",
41404         "264"
41405       ],
41406       [
41407         "Nauru",
41408         "nr",
41409         "674"
41410       ],
41411       [
41412         "Nepal (नेपाल)",
41413         "np",
41414         "977"
41415       ],
41416       [
41417         "Netherlands (Nederland)",
41418         "nl",
41419         "31"
41420       ],
41421       [
41422         "New Caledonia (Nouvelle-Calédonie)",
41423         "nc",
41424         "687"
41425       ],
41426       [
41427         "New Zealand",
41428         "nz",
41429         "64"
41430       ],
41431       [
41432         "Nicaragua",
41433         "ni",
41434         "505"
41435       ],
41436       [
41437         "Niger (Nijar)",
41438         "ne",
41439         "227"
41440       ],
41441       [
41442         "Nigeria",
41443         "ng",
41444         "234"
41445       ],
41446       [
41447         "Niue",
41448         "nu",
41449         "683"
41450       ],
41451       [
41452         "Norfolk Island",
41453         "nf",
41454         "672"
41455       ],
41456       [
41457         "North Korea (조선 민주주의 인민 공화국)",
41458         "kp",
41459         "850"
41460       ],
41461       [
41462         "Northern Mariana Islands",
41463         "mp",
41464         "1670"
41465       ],
41466       [
41467         "Norway (Norge)",
41468         "no",
41469         "47",
41470         0
41471       ],
41472       [
41473         "Oman (‫عُمان‬‎)",
41474         "om",
41475         "968"
41476       ],
41477       [
41478         "Pakistan (‫پاکستان‬‎)",
41479         "pk",
41480         "92"
41481       ],
41482       [
41483         "Palau",
41484         "pw",
41485         "680"
41486       ],
41487       [
41488         "Palestine (‫فلسطين‬‎)",
41489         "ps",
41490         "970"
41491       ],
41492       [
41493         "Panama (Panamá)",
41494         "pa",
41495         "507"
41496       ],
41497       [
41498         "Papua New Guinea",
41499         "pg",
41500         "675"
41501       ],
41502       [
41503         "Paraguay",
41504         "py",
41505         "595"
41506       ],
41507       [
41508         "Peru (Perú)",
41509         "pe",
41510         "51"
41511       ],
41512       [
41513         "Philippines",
41514         "ph",
41515         "63"
41516       ],
41517       [
41518         "Poland (Polska)",
41519         "pl",
41520         "48"
41521       ],
41522       [
41523         "Portugal",
41524         "pt",
41525         "351"
41526       ],
41527       [
41528         "Puerto Rico",
41529         "pr",
41530         "1",
41531         3,
41532         ["787", "939"]
41533       ],
41534       [
41535         "Qatar (‫قطر‬‎)",
41536         "qa",
41537         "974"
41538       ],
41539       [
41540         "Réunion (La Réunion)",
41541         "re",
41542         "262",
41543         0
41544       ],
41545       [
41546         "Romania (România)",
41547         "ro",
41548         "40"
41549       ],
41550       [
41551         "Russia (Россия)",
41552         "ru",
41553         "7",
41554         0
41555       ],
41556       [
41557         "Rwanda",
41558         "rw",
41559         "250"
41560       ],
41561       [
41562         "Saint Barthélemy",
41563         "bl",
41564         "590",
41565         1
41566       ],
41567       [
41568         "Saint Helena",
41569         "sh",
41570         "290"
41571       ],
41572       [
41573         "Saint Kitts and Nevis",
41574         "kn",
41575         "1869"
41576       ],
41577       [
41578         "Saint Lucia",
41579         "lc",
41580         "1758"
41581       ],
41582       [
41583         "Saint Martin (Saint-Martin (partie française))",
41584         "mf",
41585         "590",
41586         2
41587       ],
41588       [
41589         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
41590         "pm",
41591         "508"
41592       ],
41593       [
41594         "Saint Vincent and the Grenadines",
41595         "vc",
41596         "1784"
41597       ],
41598       [
41599         "Samoa",
41600         "ws",
41601         "685"
41602       ],
41603       [
41604         "San Marino",
41605         "sm",
41606         "378"
41607       ],
41608       [
41609         "São Tomé and Príncipe (São Tomé e Príncipe)",
41610         "st",
41611         "239"
41612       ],
41613       [
41614         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
41615         "sa",
41616         "966"
41617       ],
41618       [
41619         "Senegal (Sénégal)",
41620         "sn",
41621         "221"
41622       ],
41623       [
41624         "Serbia (Србија)",
41625         "rs",
41626         "381"
41627       ],
41628       [
41629         "Seychelles",
41630         "sc",
41631         "248"
41632       ],
41633       [
41634         "Sierra Leone",
41635         "sl",
41636         "232"
41637       ],
41638       [
41639         "Singapore",
41640         "sg",
41641         "65"
41642       ],
41643       [
41644         "Sint Maarten",
41645         "sx",
41646         "1721"
41647       ],
41648       [
41649         "Slovakia (Slovensko)",
41650         "sk",
41651         "421"
41652       ],
41653       [
41654         "Slovenia (Slovenija)",
41655         "si",
41656         "386"
41657       ],
41658       [
41659         "Solomon Islands",
41660         "sb",
41661         "677"
41662       ],
41663       [
41664         "Somalia (Soomaaliya)",
41665         "so",
41666         "252"
41667       ],
41668       [
41669         "South Africa",
41670         "za",
41671         "27"
41672       ],
41673       [
41674         "South Korea (대한민국)",
41675         "kr",
41676         "82"
41677       ],
41678       [
41679         "South Sudan (‫جنوب السودان‬‎)",
41680         "ss",
41681         "211"
41682       ],
41683       [
41684         "Spain (España)",
41685         "es",
41686         "34"
41687       ],
41688       [
41689         "Sri Lanka (ශ්‍රී ලංකාව)",
41690         "lk",
41691         "94"
41692       ],
41693       [
41694         "Sudan (‫السودان‬‎)",
41695         "sd",
41696         "249"
41697       ],
41698       [
41699         "Suriname",
41700         "sr",
41701         "597"
41702       ],
41703       [
41704         "Svalbard and Jan Mayen",
41705         "sj",
41706         "47",
41707         1
41708       ],
41709       [
41710         "Swaziland",
41711         "sz",
41712         "268"
41713       ],
41714       [
41715         "Sweden (Sverige)",
41716         "se",
41717         "46"
41718       ],
41719       [
41720         "Switzerland (Schweiz)",
41721         "ch",
41722         "41"
41723       ],
41724       [
41725         "Syria (‫سوريا‬‎)",
41726         "sy",
41727         "963"
41728       ],
41729       [
41730         "Taiwan (台灣)",
41731         "tw",
41732         "886"
41733       ],
41734       [
41735         "Tajikistan",
41736         "tj",
41737         "992"
41738       ],
41739       [
41740         "Tanzania",
41741         "tz",
41742         "255"
41743       ],
41744       [
41745         "Thailand (ไทย)",
41746         "th",
41747         "66"
41748       ],
41749       [
41750         "Timor-Leste",
41751         "tl",
41752         "670"
41753       ],
41754       [
41755         "Togo",
41756         "tg",
41757         "228"
41758       ],
41759       [
41760         "Tokelau",
41761         "tk",
41762         "690"
41763       ],
41764       [
41765         "Tonga",
41766         "to",
41767         "676"
41768       ],
41769       [
41770         "Trinidad and Tobago",
41771         "tt",
41772         "1868"
41773       ],
41774       [
41775         "Tunisia (‫تونس‬‎)",
41776         "tn",
41777         "216"
41778       ],
41779       [
41780         "Turkey (Türkiye)",
41781         "tr",
41782         "90"
41783       ],
41784       [
41785         "Turkmenistan",
41786         "tm",
41787         "993"
41788       ],
41789       [
41790         "Turks and Caicos Islands",
41791         "tc",
41792         "1649"
41793       ],
41794       [
41795         "Tuvalu",
41796         "tv",
41797         "688"
41798       ],
41799       [
41800         "U.S. Virgin Islands",
41801         "vi",
41802         "1340"
41803       ],
41804       [
41805         "Uganda",
41806         "ug",
41807         "256"
41808       ],
41809       [
41810         "Ukraine (Україна)",
41811         "ua",
41812         "380"
41813       ],
41814       [
41815         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
41816         "ae",
41817         "971"
41818       ],
41819       [
41820         "United Kingdom",
41821         "gb",
41822         "44",
41823         0
41824       ],
41825       [
41826         "United States",
41827         "us",
41828         "1",
41829         0
41830       ],
41831       [
41832         "Uruguay",
41833         "uy",
41834         "598"
41835       ],
41836       [
41837         "Uzbekistan (Oʻzbekiston)",
41838         "uz",
41839         "998"
41840       ],
41841       [
41842         "Vanuatu",
41843         "vu",
41844         "678"
41845       ],
41846       [
41847         "Vatican City (Città del Vaticano)",
41848         "va",
41849         "39",
41850         1
41851       ],
41852       [
41853         "Venezuela",
41854         "ve",
41855         "58"
41856       ],
41857       [
41858         "Vietnam (Việt Nam)",
41859         "vn",
41860         "84"
41861       ],
41862       [
41863         "Wallis and Futuna (Wallis-et-Futuna)",
41864         "wf",
41865         "681"
41866       ],
41867       [
41868         "Western Sahara (‫الصحراء الغربية‬‎)",
41869         "eh",
41870         "212",
41871         1
41872       ],
41873       [
41874         "Yemen (‫اليمن‬‎)",
41875         "ye",
41876         "967"
41877       ],
41878       [
41879         "Zambia",
41880         "zm",
41881         "260"
41882       ],
41883       [
41884         "Zimbabwe",
41885         "zw",
41886         "263"
41887       ],
41888       [
41889         "Åland Islands",
41890         "ax",
41891         "358",
41892         1
41893       ]
41894   ];
41895   
41896   return d;
41897 }/**
41898 *    This script refer to:
41899 *    Title: International Telephone Input
41900 *    Author: Jack O'Connor
41901 *    Code version:  v12.1.12
41902 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41903 **/
41904
41905 /**
41906  * @class Roo.bootstrap.PhoneInput
41907  * @extends Roo.bootstrap.TriggerField
41908  * An input with International dial-code selection
41909  
41910  * @cfg {String} defaultDialCode default '+852'
41911  * @cfg {Array} preferedCountries default []
41912   
41913  * @constructor
41914  * Create a new PhoneInput.
41915  * @param {Object} config Configuration options
41916  */
41917
41918 Roo.bootstrap.PhoneInput = function(config) {
41919     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
41920 };
41921
41922 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
41923         
41924         listWidth: undefined,
41925         
41926         selectedClass: 'active',
41927         
41928         invalidClass : "has-warning",
41929         
41930         validClass: 'has-success',
41931         
41932         allowed: '0123456789',
41933         
41934         max_length: 15,
41935         
41936         /**
41937          * @cfg {String} defaultDialCode The default dial code when initializing the input
41938          */
41939         defaultDialCode: '+852',
41940         
41941         /**
41942          * @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
41943          */
41944         preferedCountries: false,
41945         
41946         getAutoCreate : function()
41947         {
41948             var data = Roo.bootstrap.PhoneInputData();
41949             var align = this.labelAlign || this.parentLabelAlign();
41950             var id = Roo.id();
41951             
41952             this.allCountries = [];
41953             this.dialCodeMapping = [];
41954             
41955             for (var i = 0; i < data.length; i++) {
41956               var c = data[i];
41957               this.allCountries[i] = {
41958                 name: c[0],
41959                 iso2: c[1],
41960                 dialCode: c[2],
41961                 priority: c[3] || 0,
41962                 areaCodes: c[4] || null
41963               };
41964               this.dialCodeMapping[c[2]] = {
41965                   name: c[0],
41966                   iso2: c[1],
41967                   priority: c[3] || 0,
41968                   areaCodes: c[4] || null
41969               };
41970             }
41971             
41972             var cfg = {
41973                 cls: 'form-group',
41974                 cn: []
41975             };
41976             
41977             var input =  {
41978                 tag: 'input',
41979                 id : id,
41980                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
41981                 maxlength: this.max_length,
41982                 cls : 'form-control tel-input',
41983                 autocomplete: 'new-password'
41984             };
41985             
41986             var hiddenInput = {
41987                 tag: 'input',
41988                 type: 'hidden',
41989                 cls: 'hidden-tel-input'
41990             };
41991             
41992             if (this.name) {
41993                 hiddenInput.name = this.name;
41994             }
41995             
41996             if (this.disabled) {
41997                 input.disabled = true;
41998             }
41999             
42000             var flag_container = {
42001                 tag: 'div',
42002                 cls: 'flag-box',
42003                 cn: [
42004                     {
42005                         tag: 'div',
42006                         cls: 'flag'
42007                     },
42008                     {
42009                         tag: 'div',
42010                         cls: 'caret'
42011                     }
42012                 ]
42013             };
42014             
42015             var box = {
42016                 tag: 'div',
42017                 cls: this.hasFeedback ? 'has-feedback' : '',
42018                 cn: [
42019                     hiddenInput,
42020                     input,
42021                     {
42022                         tag: 'input',
42023                         cls: 'dial-code-holder',
42024                         disabled: true
42025                     }
42026                 ]
42027             };
42028             
42029             var container = {
42030                 cls: 'roo-select2-container input-group',
42031                 cn: [
42032                     flag_container,
42033                     box
42034                 ]
42035             };
42036             
42037             if (this.fieldLabel.length) {
42038                 var indicator = {
42039                     tag: 'i',
42040                     tooltip: 'This field is required'
42041                 };
42042                 
42043                 var label = {
42044                     tag: 'label',
42045                     'for':  id,
42046                     cls: 'control-label',
42047                     cn: []
42048                 };
42049                 
42050                 var label_text = {
42051                     tag: 'span',
42052                     html: this.fieldLabel
42053                 };
42054                 
42055                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42056                 label.cn = [
42057                     indicator,
42058                     label_text
42059                 ];
42060                 
42061                 if(this.indicatorpos == 'right') {
42062                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42063                     label.cn = [
42064                         label_text,
42065                         indicator
42066                     ];
42067                 }
42068                 
42069                 if(align == 'left') {
42070                     container = {
42071                         tag: 'div',
42072                         cn: [
42073                             container
42074                         ]
42075                     };
42076                     
42077                     if(this.labelWidth > 12){
42078                         label.style = "width: " + this.labelWidth + 'px';
42079                     }
42080                     if(this.labelWidth < 13 && this.labelmd == 0){
42081                         this.labelmd = this.labelWidth;
42082                     }
42083                     if(this.labellg > 0){
42084                         label.cls += ' col-lg-' + this.labellg;
42085                         input.cls += ' col-lg-' + (12 - this.labellg);
42086                     }
42087                     if(this.labelmd > 0){
42088                         label.cls += ' col-md-' + this.labelmd;
42089                         container.cls += ' col-md-' + (12 - this.labelmd);
42090                     }
42091                     if(this.labelsm > 0){
42092                         label.cls += ' col-sm-' + this.labelsm;
42093                         container.cls += ' col-sm-' + (12 - this.labelsm);
42094                     }
42095                     if(this.labelxs > 0){
42096                         label.cls += ' col-xs-' + this.labelxs;
42097                         container.cls += ' col-xs-' + (12 - this.labelxs);
42098                     }
42099                 }
42100             }
42101             
42102             cfg.cn = [
42103                 label,
42104                 container
42105             ];
42106             
42107             var settings = this;
42108             
42109             ['xs','sm','md','lg'].map(function(size){
42110                 if (settings[size]) {
42111                     cfg.cls += ' col-' + size + '-' + settings[size];
42112                 }
42113             });
42114             
42115             this.store = new Roo.data.Store({
42116                 proxy : new Roo.data.MemoryProxy({}),
42117                 reader : new Roo.data.JsonReader({
42118                     fields : [
42119                         {
42120                             'name' : 'name',
42121                             'type' : 'string'
42122                         },
42123                         {
42124                             'name' : 'iso2',
42125                             'type' : 'string'
42126                         },
42127                         {
42128                             'name' : 'dialCode',
42129                             'type' : 'string'
42130                         },
42131                         {
42132                             'name' : 'priority',
42133                             'type' : 'string'
42134                         },
42135                         {
42136                             'name' : 'areaCodes',
42137                             'type' : 'string'
42138                         }
42139                     ]
42140                 })
42141             });
42142             
42143             if(!this.preferedCountries) {
42144                 this.preferedCountries = [
42145                     'hk',
42146                     'gb',
42147                     'us'
42148                 ];
42149             }
42150             
42151             var p = this.preferedCountries.reverse();
42152             
42153             if(p) {
42154                 for (var i = 0; i < p.length; i++) {
42155                     for (var j = 0; j < this.allCountries.length; j++) {
42156                         if(this.allCountries[j].iso2 == p[i]) {
42157                             var t = this.allCountries[j];
42158                             this.allCountries.splice(j,1);
42159                             this.allCountries.unshift(t);
42160                         }
42161                     } 
42162                 }
42163             }
42164             
42165             this.store.proxy.data = {
42166                 success: true,
42167                 data: this.allCountries
42168             };
42169             
42170             return cfg;
42171         },
42172         
42173         initEvents : function()
42174         {
42175             this.createList();
42176             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42177             
42178             this.indicator = this.indicatorEl();
42179             this.flag = this.flagEl();
42180             this.dialCodeHolder = this.dialCodeHolderEl();
42181             
42182             this.trigger = this.el.select('div.flag-box',true).first();
42183             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42184             
42185             var _this = this;
42186             
42187             (function(){
42188                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42189                 _this.list.setWidth(lw);
42190             }).defer(100);
42191             
42192             this.list.on('mouseover', this.onViewOver, this);
42193             this.list.on('mousemove', this.onViewMove, this);
42194             this.inputEl().on("keyup", this.onKeyUp, this);
42195             this.inputEl().on("keypress", this.onKeyPress, this);
42196             
42197             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42198
42199             this.view = new Roo.View(this.list, this.tpl, {
42200                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42201             });
42202             
42203             this.view.on('click', this.onViewClick, this);
42204             this.setValue(this.defaultDialCode);
42205         },
42206         
42207         onTriggerClick : function(e)
42208         {
42209             Roo.log('trigger click');
42210             if(this.disabled){
42211                 return;
42212             }
42213             
42214             if(this.isExpanded()){
42215                 this.collapse();
42216                 this.hasFocus = false;
42217             }else {
42218                 this.store.load({});
42219                 this.hasFocus = true;
42220                 this.expand();
42221             }
42222         },
42223         
42224         isExpanded : function()
42225         {
42226             return this.list.isVisible();
42227         },
42228         
42229         collapse : function()
42230         {
42231             if(!this.isExpanded()){
42232                 return;
42233             }
42234             this.list.hide();
42235             Roo.get(document).un('mousedown', this.collapseIf, this);
42236             Roo.get(document).un('mousewheel', this.collapseIf, this);
42237             this.fireEvent('collapse', this);
42238             this.validate();
42239         },
42240         
42241         expand : function()
42242         {
42243             Roo.log('expand');
42244
42245             if(this.isExpanded() || !this.hasFocus){
42246                 return;
42247             }
42248             
42249             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42250             this.list.setWidth(lw);
42251             
42252             this.list.show();
42253             this.restrictHeight();
42254             
42255             Roo.get(document).on('mousedown', this.collapseIf, this);
42256             Roo.get(document).on('mousewheel', this.collapseIf, this);
42257             
42258             this.fireEvent('expand', this);
42259         },
42260         
42261         restrictHeight : function()
42262         {
42263             this.list.alignTo(this.inputEl(), this.listAlign);
42264             this.list.alignTo(this.inputEl(), this.listAlign);
42265         },
42266         
42267         onViewOver : function(e, t)
42268         {
42269             if(this.inKeyMode){
42270                 return;
42271             }
42272             var item = this.view.findItemFromChild(t);
42273             
42274             if(item){
42275                 var index = this.view.indexOf(item);
42276                 this.select(index, false);
42277             }
42278         },
42279
42280         // private
42281         onViewClick : function(view, doFocus, el, e)
42282         {
42283             var index = this.view.getSelectedIndexes()[0];
42284             
42285             var r = this.store.getAt(index);
42286             
42287             if(r){
42288                 this.onSelect(r, index);
42289             }
42290             if(doFocus !== false && !this.blockFocus){
42291                 this.inputEl().focus();
42292             }
42293         },
42294         
42295         onViewMove : function(e, t)
42296         {
42297             this.inKeyMode = false;
42298         },
42299         
42300         select : function(index, scrollIntoView)
42301         {
42302             this.selectedIndex = index;
42303             this.view.select(index);
42304             if(scrollIntoView !== false){
42305                 var el = this.view.getNode(index);
42306                 if(el){
42307                     this.list.scrollChildIntoView(el, false);
42308                 }
42309             }
42310         },
42311         
42312         createList : function()
42313         {
42314             this.list = Roo.get(document.body).createChild({
42315                 tag: 'ul',
42316                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
42317                 style: 'display:none'
42318             });
42319             
42320             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
42321         },
42322         
42323         collapseIf : function(e)
42324         {
42325             var in_combo  = e.within(this.el);
42326             var in_list =  e.within(this.list);
42327             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
42328             
42329             if (in_combo || in_list || is_list) {
42330                 return;
42331             }
42332             this.collapse();
42333         },
42334         
42335         onSelect : function(record, index)
42336         {
42337             if(this.fireEvent('beforeselect', this, record, index) !== false){
42338                 
42339                 this.setFlagClass(record.data.iso2);
42340                 this.setDialCode(record.data.dialCode);
42341                 this.hasFocus = false;
42342                 this.collapse();
42343                 this.fireEvent('select', this, record, index);
42344             }
42345         },
42346         
42347         flagEl : function()
42348         {
42349             var flag = this.el.select('div.flag',true).first();
42350             if(!flag){
42351                 return false;
42352             }
42353             return flag;
42354         },
42355         
42356         dialCodeHolderEl : function()
42357         {
42358             var d = this.el.select('input.dial-code-holder',true).first();
42359             if(!d){
42360                 return false;
42361             }
42362             return d;
42363         },
42364         
42365         setDialCode : function(v)
42366         {
42367             this.dialCodeHolder.dom.value = '+'+v;
42368         },
42369         
42370         setFlagClass : function(n)
42371         {
42372             this.flag.dom.className = 'flag '+n;
42373         },
42374         
42375         getValue : function()
42376         {
42377             var v = this.inputEl().getValue();
42378             if(this.dialCodeHolder) {
42379                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
42380             }
42381             return v;
42382         },
42383         
42384         setValue : function(v)
42385         {
42386             var d = this.getDialCode(v);
42387             
42388             //invalid dial code
42389             if(v.length == 0 || !d || d.length == 0) {
42390                 if(this.rendered){
42391                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
42392                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
42393                 }
42394                 return;
42395             }
42396             
42397             //valid dial code
42398             this.setFlagClass(this.dialCodeMapping[d].iso2);
42399             this.setDialCode(d);
42400             this.inputEl().dom.value = v.replace('+'+d,'');
42401             this.hiddenEl().dom.value = this.getValue();
42402             
42403             this.validate();
42404         },
42405         
42406         getDialCode : function(v)
42407         {
42408             v = v ||  '';
42409             
42410             if (v.length == 0) {
42411                 return this.dialCodeHolder.dom.value;
42412             }
42413             
42414             var dialCode = "";
42415             if (v.charAt(0) != "+") {
42416                 return false;
42417             }
42418             var numericChars = "";
42419             for (var i = 1; i < v.length; i++) {
42420               var c = v.charAt(i);
42421               if (!isNaN(c)) {
42422                 numericChars += c;
42423                 if (this.dialCodeMapping[numericChars]) {
42424                   dialCode = v.substr(1, i);
42425                 }
42426                 if (numericChars.length == 4) {
42427                   break;
42428                 }
42429               }
42430             }
42431             return dialCode;
42432         },
42433         
42434         reset : function()
42435         {
42436             this.setValue(this.defaultDialCode);
42437             this.validate();
42438         },
42439         
42440         hiddenEl : function()
42441         {
42442             return this.el.select('input.hidden-tel-input',true).first();
42443         },
42444         
42445         // after setting val
42446         onKeyUp : function(e){
42447             this.setValue(this.getValue());
42448         },
42449         
42450         onKeyPress : function(e){
42451             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
42452                 e.stopEvent();
42453             }
42454         }
42455         
42456 });
42457 /**
42458  * @class Roo.bootstrap.MoneyField
42459  * @extends Roo.bootstrap.ComboBox
42460  * Bootstrap MoneyField class
42461  * 
42462  * @constructor
42463  * Create a new MoneyField.
42464  * @param {Object} config Configuration options
42465  */
42466
42467 Roo.bootstrap.MoneyField = function(config) {
42468     
42469     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
42470     
42471 };
42472
42473 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
42474     
42475     /**
42476      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
42477      */
42478     allowDecimals : true,
42479     /**
42480      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
42481      */
42482     decimalSeparator : ".",
42483     /**
42484      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
42485      */
42486     decimalPrecision : 0,
42487     /**
42488      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
42489      */
42490     allowNegative : true,
42491     /**
42492      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
42493      */
42494     allowZero: true,
42495     /**
42496      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
42497      */
42498     minValue : Number.NEGATIVE_INFINITY,
42499     /**
42500      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
42501      */
42502     maxValue : Number.MAX_VALUE,
42503     /**
42504      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
42505      */
42506     minText : "The minimum value for this field is {0}",
42507     /**
42508      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
42509      */
42510     maxText : "The maximum value for this field is {0}",
42511     /**
42512      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
42513      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
42514      */
42515     nanText : "{0} is not a valid number",
42516     /**
42517      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
42518      */
42519     castInt : true,
42520     /**
42521      * @cfg {String} defaults currency of the MoneyField
42522      * value should be in lkey
42523      */
42524     defaultCurrency : false,
42525     /**
42526      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
42527      */
42528     thousandsDelimiter : false,
42529     /**
42530      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
42531      */
42532     max_length: false,
42533     
42534     inputlg : 9,
42535     inputmd : 9,
42536     inputsm : 9,
42537     inputxs : 6,
42538     
42539     store : false,
42540     
42541     getAutoCreate : function()
42542     {
42543         var align = this.labelAlign || this.parentLabelAlign();
42544         
42545         var id = Roo.id();
42546
42547         var cfg = {
42548             cls: 'form-group',
42549             cn: []
42550         };
42551
42552         var input =  {
42553             tag: 'input',
42554             id : id,
42555             cls : 'form-control roo-money-amount-input',
42556             autocomplete: 'new-password'
42557         };
42558         
42559         var hiddenInput = {
42560             tag: 'input',
42561             type: 'hidden',
42562             id: Roo.id(),
42563             cls: 'hidden-number-input'
42564         };
42565         
42566         if(this.max_length) {
42567             input.maxlength = this.max_length; 
42568         }
42569         
42570         if (this.name) {
42571             hiddenInput.name = this.name;
42572         }
42573
42574         if (this.disabled) {
42575             input.disabled = true;
42576         }
42577
42578         var clg = 12 - this.inputlg;
42579         var cmd = 12 - this.inputmd;
42580         var csm = 12 - this.inputsm;
42581         var cxs = 12 - this.inputxs;
42582         
42583         var container = {
42584             tag : 'div',
42585             cls : 'row roo-money-field',
42586             cn : [
42587                 {
42588                     tag : 'div',
42589                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
42590                     cn : [
42591                         {
42592                             tag : 'div',
42593                             cls: 'roo-select2-container input-group',
42594                             cn: [
42595                                 {
42596                                     tag : 'input',
42597                                     cls : 'form-control roo-money-currency-input',
42598                                     autocomplete: 'new-password',
42599                                     readOnly : 1,
42600                                     name : this.currencyName
42601                                 },
42602                                 {
42603                                     tag :'span',
42604                                     cls : 'input-group-addon',
42605                                     cn : [
42606                                         {
42607                                             tag: 'span',
42608                                             cls: 'caret'
42609                                         }
42610                                     ]
42611                                 }
42612                             ]
42613                         }
42614                     ]
42615                 },
42616                 {
42617                     tag : 'div',
42618                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
42619                     cn : [
42620                         {
42621                             tag: 'div',
42622                             cls: this.hasFeedback ? 'has-feedback' : '',
42623                             cn: [
42624                                 input
42625                             ]
42626                         }
42627                     ]
42628                 }
42629             ]
42630             
42631         };
42632         
42633         if (this.fieldLabel.length) {
42634             var indicator = {
42635                 tag: 'i',
42636                 tooltip: 'This field is required'
42637             };
42638
42639             var label = {
42640                 tag: 'label',
42641                 'for':  id,
42642                 cls: 'control-label',
42643                 cn: []
42644             };
42645
42646             var label_text = {
42647                 tag: 'span',
42648                 html: this.fieldLabel
42649             };
42650
42651             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42652             label.cn = [
42653                 indicator,
42654                 label_text
42655             ];
42656
42657             if(this.indicatorpos == 'right') {
42658                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42659                 label.cn = [
42660                     label_text,
42661                     indicator
42662                 ];
42663             }
42664
42665             if(align == 'left') {
42666                 container = {
42667                     tag: 'div',
42668                     cn: [
42669                         container
42670                     ]
42671                 };
42672
42673                 if(this.labelWidth > 12){
42674                     label.style = "width: " + this.labelWidth + 'px';
42675                 }
42676                 if(this.labelWidth < 13 && this.labelmd == 0){
42677                     this.labelmd = this.labelWidth;
42678                 }
42679                 if(this.labellg > 0){
42680                     label.cls += ' col-lg-' + this.labellg;
42681                     input.cls += ' col-lg-' + (12 - this.labellg);
42682                 }
42683                 if(this.labelmd > 0){
42684                     label.cls += ' col-md-' + this.labelmd;
42685                     container.cls += ' col-md-' + (12 - this.labelmd);
42686                 }
42687                 if(this.labelsm > 0){
42688                     label.cls += ' col-sm-' + this.labelsm;
42689                     container.cls += ' col-sm-' + (12 - this.labelsm);
42690                 }
42691                 if(this.labelxs > 0){
42692                     label.cls += ' col-xs-' + this.labelxs;
42693                     container.cls += ' col-xs-' + (12 - this.labelxs);
42694                 }
42695             }
42696         }
42697
42698         cfg.cn = [
42699             label,
42700             container,
42701             hiddenInput
42702         ];
42703         
42704         var settings = this;
42705
42706         ['xs','sm','md','lg'].map(function(size){
42707             if (settings[size]) {
42708                 cfg.cls += ' col-' + size + '-' + settings[size];
42709             }
42710         });
42711         
42712         return cfg;
42713     },
42714     
42715     initEvents : function()
42716     {
42717         this.indicator = this.indicatorEl();
42718         
42719         this.initCurrencyEvent();
42720         
42721         this.initNumberEvent();
42722     },
42723     
42724     initCurrencyEvent : function()
42725     {
42726         if (!this.store) {
42727             throw "can not find store for combo";
42728         }
42729         
42730         this.store = Roo.factory(this.store, Roo.data);
42731         this.store.parent = this;
42732         
42733         this.createList();
42734         
42735         this.triggerEl = this.el.select('.input-group-addon', true).first();
42736         
42737         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
42738         
42739         var _this = this;
42740         
42741         (function(){
42742             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42743             _this.list.setWidth(lw);
42744         }).defer(100);
42745         
42746         this.list.on('mouseover', this.onViewOver, this);
42747         this.list.on('mousemove', this.onViewMove, this);
42748         this.list.on('scroll', this.onViewScroll, this);
42749         
42750         if(!this.tpl){
42751             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
42752         }
42753         
42754         this.view = new Roo.View(this.list, this.tpl, {
42755             singleSelect:true, store: this.store, selectedClass: this.selectedClass
42756         });
42757         
42758         this.view.on('click', this.onViewClick, this);
42759         
42760         this.store.on('beforeload', this.onBeforeLoad, this);
42761         this.store.on('load', this.onLoad, this);
42762         this.store.on('loadexception', this.onLoadException, this);
42763         
42764         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
42765             "up" : function(e){
42766                 this.inKeyMode = true;
42767                 this.selectPrev();
42768             },
42769
42770             "down" : function(e){
42771                 if(!this.isExpanded()){
42772                     this.onTriggerClick();
42773                 }else{
42774                     this.inKeyMode = true;
42775                     this.selectNext();
42776                 }
42777             },
42778
42779             "enter" : function(e){
42780                 this.collapse();
42781                 
42782                 if(this.fireEvent("specialkey", this, e)){
42783                     this.onViewClick(false);
42784                 }
42785                 
42786                 return true;
42787             },
42788
42789             "esc" : function(e){
42790                 this.collapse();
42791             },
42792
42793             "tab" : function(e){
42794                 this.collapse();
42795                 
42796                 if(this.fireEvent("specialkey", this, e)){
42797                     this.onViewClick(false);
42798                 }
42799                 
42800                 return true;
42801             },
42802
42803             scope : this,
42804
42805             doRelay : function(foo, bar, hname){
42806                 if(hname == 'down' || this.scope.isExpanded()){
42807                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
42808                 }
42809                 return true;
42810             },
42811
42812             forceKeyDown: true
42813         });
42814         
42815         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
42816         
42817     },
42818     
42819     initNumberEvent : function(e)
42820     {
42821         this.inputEl().on("keydown" , this.fireKey,  this);
42822         this.inputEl().on("focus", this.onFocus,  this);
42823         this.inputEl().on("blur", this.onBlur,  this);
42824         
42825         this.inputEl().relayEvent('keyup', this);
42826         
42827         if(this.indicator){
42828             this.indicator.addClass('invisible');
42829         }
42830  
42831         this.originalValue = this.getValue();
42832         
42833         if(this.validationEvent == 'keyup'){
42834             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
42835             this.inputEl().on('keyup', this.filterValidation, this);
42836         }
42837         else if(this.validationEvent !== false){
42838             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
42839         }
42840         
42841         if(this.selectOnFocus){
42842             this.on("focus", this.preFocus, this);
42843             
42844         }
42845         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
42846             this.inputEl().on("keypress", this.filterKeys, this);
42847         } else {
42848             this.inputEl().relayEvent('keypress', this);
42849         }
42850         
42851         var allowed = "0123456789";
42852         
42853         if(this.allowDecimals){
42854             allowed += this.decimalSeparator;
42855         }
42856         
42857         if(this.allowNegative){
42858             allowed += "-";
42859         }
42860         
42861         if(this.thousandsDelimiter) {
42862             allowed += ",";
42863         }
42864         
42865         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
42866         
42867         var keyPress = function(e){
42868             
42869             var k = e.getKey();
42870             
42871             var c = e.getCharCode();
42872             
42873             if(
42874                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
42875                     allowed.indexOf(String.fromCharCode(c)) === -1
42876             ){
42877                 e.stopEvent();
42878                 return;
42879             }
42880             
42881             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
42882                 return;
42883             }
42884             
42885             if(allowed.indexOf(String.fromCharCode(c)) === -1){
42886                 e.stopEvent();
42887             }
42888         };
42889         
42890         this.inputEl().on("keypress", keyPress, this);
42891         
42892     },
42893     
42894     onTriggerClick : function(e)
42895     {   
42896         if(this.disabled){
42897             return;
42898         }
42899         
42900         this.page = 0;
42901         this.loadNext = false;
42902         
42903         if(this.isExpanded()){
42904             this.collapse();
42905             return;
42906         }
42907         
42908         this.hasFocus = true;
42909         
42910         if(this.triggerAction == 'all') {
42911             this.doQuery(this.allQuery, true);
42912             return;
42913         }
42914         
42915         this.doQuery(this.getRawValue());
42916     },
42917     
42918     getCurrency : function()
42919     {   
42920         var v = this.currencyEl().getValue();
42921         
42922         return v;
42923     },
42924     
42925     restrictHeight : function()
42926     {
42927         this.list.alignTo(this.currencyEl(), this.listAlign);
42928         this.list.alignTo(this.currencyEl(), this.listAlign);
42929     },
42930     
42931     onViewClick : function(view, doFocus, el, e)
42932     {
42933         var index = this.view.getSelectedIndexes()[0];
42934         
42935         var r = this.store.getAt(index);
42936         
42937         if(r){
42938             this.onSelect(r, index);
42939         }
42940     },
42941     
42942     onSelect : function(record, index){
42943         
42944         if(this.fireEvent('beforeselect', this, record, index) !== false){
42945         
42946             this.setFromCurrencyData(index > -1 ? record.data : false);
42947             
42948             this.collapse();
42949             
42950             this.fireEvent('select', this, record, index);
42951         }
42952     },
42953     
42954     setFromCurrencyData : function(o)
42955     {
42956         var currency = '';
42957         
42958         this.lastCurrency = o;
42959         
42960         if (this.currencyField) {
42961             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
42962         } else {
42963             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
42964         }
42965         
42966         this.lastSelectionText = currency;
42967         
42968         //setting default currency
42969         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
42970             this.setCurrency(this.defaultCurrency);
42971             return;
42972         }
42973         
42974         this.setCurrency(currency);
42975     },
42976     
42977     setFromData : function(o)
42978     {
42979         var c = {};
42980         
42981         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
42982         
42983         this.setFromCurrencyData(c);
42984         
42985         var value = '';
42986         
42987         if (this.name) {
42988             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
42989         } else {
42990             Roo.log('no value set for '+ (this.name ? this.name : this.id));
42991         }
42992         
42993         this.setValue(value);
42994         
42995     },
42996     
42997     setCurrency : function(v)
42998     {   
42999         this.currencyValue = v;
43000         
43001         if(this.rendered){
43002             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43003             this.validate();
43004         }
43005     },
43006     
43007     setValue : function(v)
43008     {
43009         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43010         
43011         this.value = v;
43012         
43013         if(this.rendered){
43014             
43015             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43016             
43017             this.inputEl().dom.value = (v == '') ? '' :
43018                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43019             
43020             if(!this.allowZero && v === '0') {
43021                 this.hiddenEl().dom.value = '';
43022                 this.inputEl().dom.value = '';
43023             }
43024             
43025             this.validate();
43026         }
43027     },
43028     
43029     getRawValue : function()
43030     {
43031         var v = this.inputEl().getValue();
43032         
43033         return v;
43034     },
43035     
43036     getValue : function()
43037     {
43038         return this.fixPrecision(this.parseValue(this.getRawValue()));
43039     },
43040     
43041     parseValue : function(value)
43042     {
43043         if(this.thousandsDelimiter) {
43044             value += "";
43045             r = new RegExp(",", "g");
43046             value = value.replace(r, "");
43047         }
43048         
43049         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43050         return isNaN(value) ? '' : value;
43051         
43052     },
43053     
43054     fixPrecision : function(value)
43055     {
43056         if(this.thousandsDelimiter) {
43057             value += "";
43058             r = new RegExp(",", "g");
43059             value = value.replace(r, "");
43060         }
43061         
43062         var nan = isNaN(value);
43063         
43064         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43065             return nan ? '' : value;
43066         }
43067         return parseFloat(value).toFixed(this.decimalPrecision);
43068     },
43069     
43070     decimalPrecisionFcn : function(v)
43071     {
43072         return Math.floor(v);
43073     },
43074     
43075     validateValue : function(value)
43076     {
43077         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43078             return false;
43079         }
43080         
43081         var num = this.parseValue(value);
43082         
43083         if(isNaN(num)){
43084             this.markInvalid(String.format(this.nanText, value));
43085             return false;
43086         }
43087         
43088         if(num < this.minValue){
43089             this.markInvalid(String.format(this.minText, this.minValue));
43090             return false;
43091         }
43092         
43093         if(num > this.maxValue){
43094             this.markInvalid(String.format(this.maxText, this.maxValue));
43095             return false;
43096         }
43097         
43098         return true;
43099     },
43100     
43101     validate : function()
43102     {
43103         if(this.disabled || this.allowBlank){
43104             this.markValid();
43105             return true;
43106         }
43107         
43108         var currency = this.getCurrency();
43109         
43110         if(this.validateValue(this.getRawValue()) && currency.length){
43111             this.markValid();
43112             return true;
43113         }
43114         
43115         this.markInvalid();
43116         return false;
43117     },
43118     
43119     getName: function()
43120     {
43121         return this.name;
43122     },
43123     
43124     beforeBlur : function()
43125     {
43126         if(!this.castInt){
43127             return;
43128         }
43129         
43130         var v = this.parseValue(this.getRawValue());
43131         
43132         if(v || v == 0){
43133             this.setValue(v);
43134         }
43135     },
43136     
43137     onBlur : function()
43138     {
43139         this.beforeBlur();
43140         
43141         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43142             //this.el.removeClass(this.focusClass);
43143         }
43144         
43145         this.hasFocus = false;
43146         
43147         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43148             this.validate();
43149         }
43150         
43151         var v = this.getValue();
43152         
43153         if(String(v) !== String(this.startValue)){
43154             this.fireEvent('change', this, v, this.startValue);
43155         }
43156         
43157         this.fireEvent("blur", this);
43158     },
43159     
43160     inputEl : function()
43161     {
43162         return this.el.select('.roo-money-amount-input', true).first();
43163     },
43164     
43165     currencyEl : function()
43166     {
43167         return this.el.select('.roo-money-currency-input', true).first();
43168     },
43169     
43170     hiddenEl : function()
43171     {
43172         return this.el.select('input.hidden-number-input',true).first();
43173     }
43174     
43175 });/**
43176  * @class Roo.bootstrap.BezierSignature
43177  * @extends Roo.bootstrap.Component
43178  * Bootstrap BezierSignature class
43179  * This script refer to:
43180  *    Title: Signature Pad
43181  *    Author: szimek
43182  *    Availability: https://github.com/szimek/signature_pad
43183  *
43184  * @constructor
43185  * Create a new BezierSignature
43186  * @param {Object} config The config object
43187  */
43188
43189 Roo.bootstrap.BezierSignature = function(config){
43190     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43191     this.addEvents({
43192         "resize" : true
43193     });
43194 };
43195
43196 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43197 {
43198      
43199     curve_data: [],
43200     
43201     is_empty: true,
43202     
43203     mouse_btn_down: true,
43204     
43205     /**
43206      * @cfg {int} canvas height
43207      */
43208     canvas_height: '200px',
43209     
43210     /**
43211      * @cfg {float|function} Radius of a single dot.
43212      */ 
43213     dot_size: false,
43214     
43215     /**
43216      * @cfg {float} Minimum width of a line. Defaults to 0.5.
43217      */
43218     min_width: 0.5,
43219     
43220     /**
43221      * @cfg {float} Maximum width of a line. Defaults to 2.5.
43222      */
43223     max_width: 2.5,
43224     
43225     /**
43226      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43227      */
43228     throttle: 16,
43229     
43230     /**
43231      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43232      */
43233     min_distance: 5,
43234     
43235     /**
43236      * @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.
43237      */
43238     bg_color: 'rgba(0, 0, 0, 0)',
43239     
43240     /**
43241      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43242      */
43243     dot_color: 'black',
43244     
43245     /**
43246      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43247      */ 
43248     velocity_filter_weight: 0.7,
43249     
43250     /**
43251      * @cfg {function} Callback when stroke begin. 
43252      */
43253     onBegin: false,
43254     
43255     /**
43256      * @cfg {function} Callback when stroke end.
43257      */
43258     onEnd: false,
43259     
43260     getAutoCreate : function()
43261     {
43262         var cls = 'roo-signature column';
43263         
43264         if(this.cls){
43265             cls += ' ' + this.cls;
43266         }
43267         
43268         var col_sizes = [
43269             'lg',
43270             'md',
43271             'sm',
43272             'xs'
43273         ];
43274         
43275         for(var i = 0; i < col_sizes.length; i++) {
43276             if(this[col_sizes[i]]) {
43277                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
43278             }
43279         }
43280         
43281         var cfg = {
43282             tag: 'div',
43283             cls: cls,
43284             cn: [
43285                 {
43286                     tag: 'div',
43287                     cls: 'roo-signature-body',
43288                     cn: [
43289                         {
43290                             tag: 'canvas',
43291                             cls: 'roo-signature-body-canvas',
43292                             height: this.canvas_height,
43293                             width: this.canvas_width
43294                         }
43295                     ]
43296                 },
43297                 {
43298                     tag: 'input',
43299                     type: 'file',
43300                     style: 'display: none'
43301                 }
43302             ]
43303         };
43304         
43305         return cfg;
43306     },
43307     
43308     initEvents: function() 
43309     {
43310         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
43311         
43312         var canvas = this.canvasEl();
43313         
43314         // mouse && touch event swapping...
43315         canvas.dom.style.touchAction = 'none';
43316         canvas.dom.style.msTouchAction = 'none';
43317         
43318         this.mouse_btn_down = false;
43319         canvas.on('mousedown', this._handleMouseDown, this);
43320         canvas.on('mousemove', this._handleMouseMove, this);
43321         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
43322         
43323         if (window.PointerEvent) {
43324             canvas.on('pointerdown', this._handleMouseDown, this);
43325             canvas.on('pointermove', this._handleMouseMove, this);
43326             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
43327         }
43328         
43329         if ('ontouchstart' in window) {
43330             canvas.on('touchstart', this._handleTouchStart, this);
43331             canvas.on('touchmove', this._handleTouchMove, this);
43332             canvas.on('touchend', this._handleTouchEnd, this);
43333         }
43334         
43335         Roo.EventManager.onWindowResize(this.resize, this, true);
43336         
43337         // file input event
43338         this.fileEl().on('change', this.uploadImage, this);
43339         
43340         this.clear();
43341         
43342         this.resize();
43343     },
43344     
43345     resize: function(){
43346         
43347         var canvas = this.canvasEl().dom;
43348         var ctx = this.canvasElCtx();
43349         var img_data = false;
43350         
43351         if(canvas.width > 0) {
43352             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
43353         }
43354         // setting canvas width will clean img data
43355         canvas.width = 0;
43356         
43357         var style = window.getComputedStyle ? 
43358             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
43359             
43360         var padding_left = parseInt(style.paddingLeft) || 0;
43361         var padding_right = parseInt(style.paddingRight) || 0;
43362         
43363         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
43364         
43365         if(img_data) {
43366             ctx.putImageData(img_data, 0, 0);
43367         }
43368     },
43369     
43370     _handleMouseDown: function(e)
43371     {
43372         if (e.browserEvent.which === 1) {
43373             this.mouse_btn_down = true;
43374             this.strokeBegin(e);
43375         }
43376     },
43377     
43378     _handleMouseMove: function (e)
43379     {
43380         if (this.mouse_btn_down) {
43381             this.strokeMoveUpdate(e);
43382         }
43383     },
43384     
43385     _handleMouseUp: function (e)
43386     {
43387         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
43388             this.mouse_btn_down = false;
43389             this.strokeEnd(e);
43390         }
43391     },
43392     
43393     _handleTouchStart: function (e) {
43394         
43395         e.preventDefault();
43396         if (e.browserEvent.targetTouches.length === 1) {
43397             // var touch = e.browserEvent.changedTouches[0];
43398             // this.strokeBegin(touch);
43399             
43400              this.strokeBegin(e); // assume e catching the correct xy...
43401         }
43402     },
43403     
43404     _handleTouchMove: function (e) {
43405         e.preventDefault();
43406         // var touch = event.targetTouches[0];
43407         // _this._strokeMoveUpdate(touch);
43408         this.strokeMoveUpdate(e);
43409     },
43410     
43411     _handleTouchEnd: function (e) {
43412         var wasCanvasTouched = e.target === this.canvasEl().dom;
43413         if (wasCanvasTouched) {
43414             e.preventDefault();
43415             // var touch = event.changedTouches[0];
43416             // _this._strokeEnd(touch);
43417             this.strokeEnd(e);
43418         }
43419     },
43420     
43421     reset: function () {
43422         this._lastPoints = [];
43423         this._lastVelocity = 0;
43424         this._lastWidth = (this.min_width + this.max_width) / 2;
43425         this.canvasElCtx().fillStyle = this.dot_color;
43426     },
43427     
43428     strokeMoveUpdate: function(e)
43429     {
43430         this.strokeUpdate(e);
43431         
43432         if (this.throttle) {
43433             this.throttleStroke(this.strokeUpdate, this.throttle);
43434         }
43435         else {
43436             this.strokeUpdate(e);
43437         }
43438     },
43439     
43440     strokeBegin: function(e)
43441     {
43442         var newPointGroup = {
43443             color: this.dot_color,
43444             points: []
43445         };
43446         
43447         if (typeof this.onBegin === 'function') {
43448             this.onBegin(e);
43449         }
43450         
43451         this.curve_data.push(newPointGroup);
43452         this.reset();
43453         this.strokeUpdate(e);
43454     },
43455     
43456     strokeUpdate: function(e)
43457     {
43458         var rect = this.canvasEl().dom.getBoundingClientRect();
43459         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
43460         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
43461         var lastPoints = lastPointGroup.points;
43462         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
43463         var isLastPointTooClose = lastPoint
43464             ? point.distanceTo(lastPoint) <= this.min_distance
43465             : false;
43466         var color = lastPointGroup.color;
43467         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
43468             var curve = this.addPoint(point);
43469             if (!lastPoint) {
43470                 this.drawDot({color: color, point: point});
43471             }
43472             else if (curve) {
43473                 this.drawCurve({color: color, curve: curve});
43474             }
43475             lastPoints.push({
43476                 time: point.time,
43477                 x: point.x,
43478                 y: point.y
43479             });
43480         }
43481     },
43482     
43483     strokeEnd: function(e)
43484     {
43485         this.strokeUpdate(e);
43486         if (typeof this.onEnd === 'function') {
43487             this.onEnd(e);
43488         }
43489     },
43490     
43491     addPoint:  function (point) {
43492         var _lastPoints = this._lastPoints;
43493         _lastPoints.push(point);
43494         if (_lastPoints.length > 2) {
43495             if (_lastPoints.length === 3) {
43496                 _lastPoints.unshift(_lastPoints[0]);
43497             }
43498             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
43499             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
43500             _lastPoints.shift();
43501             return curve;
43502         }
43503         return null;
43504     },
43505     
43506     calculateCurveWidths: function (startPoint, endPoint) {
43507         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
43508             (1 - this.velocity_filter_weight) * this._lastVelocity;
43509
43510         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
43511         var widths = {
43512             end: newWidth,
43513             start: this._lastWidth
43514         };
43515         
43516         this._lastVelocity = velocity;
43517         this._lastWidth = newWidth;
43518         return widths;
43519     },
43520     
43521     drawDot: function (_a) {
43522         var color = _a.color, point = _a.point;
43523         var ctx = this.canvasElCtx();
43524         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
43525         ctx.beginPath();
43526         this.drawCurveSegment(point.x, point.y, width);
43527         ctx.closePath();
43528         ctx.fillStyle = color;
43529         ctx.fill();
43530     },
43531     
43532     drawCurve: function (_a) {
43533         var color = _a.color, curve = _a.curve;
43534         var ctx = this.canvasElCtx();
43535         var widthDelta = curve.endWidth - curve.startWidth;
43536         var drawSteps = Math.floor(curve.length()) * 2;
43537         ctx.beginPath();
43538         ctx.fillStyle = color;
43539         for (var i = 0; i < drawSteps; i += 1) {
43540         var t = i / drawSteps;
43541         var tt = t * t;
43542         var ttt = tt * t;
43543         var u = 1 - t;
43544         var uu = u * u;
43545         var uuu = uu * u;
43546         var x = uuu * curve.startPoint.x;
43547         x += 3 * uu * t * curve.control1.x;
43548         x += 3 * u * tt * curve.control2.x;
43549         x += ttt * curve.endPoint.x;
43550         var y = uuu * curve.startPoint.y;
43551         y += 3 * uu * t * curve.control1.y;
43552         y += 3 * u * tt * curve.control2.y;
43553         y += ttt * curve.endPoint.y;
43554         var width = curve.startWidth + ttt * widthDelta;
43555         this.drawCurveSegment(x, y, width);
43556         }
43557         ctx.closePath();
43558         ctx.fill();
43559     },
43560     
43561     drawCurveSegment: function (x, y, width) {
43562         var ctx = this.canvasElCtx();
43563         ctx.moveTo(x, y);
43564         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
43565         this.is_empty = false;
43566     },
43567     
43568     clear: function()
43569     {
43570         var ctx = this.canvasElCtx();
43571         var canvas = this.canvasEl().dom;
43572         ctx.fillStyle = this.bg_color;
43573         ctx.clearRect(0, 0, canvas.width, canvas.height);
43574         ctx.fillRect(0, 0, canvas.width, canvas.height);
43575         this.curve_data = [];
43576         this.reset();
43577         this.is_empty = true;
43578     },
43579     
43580     fileEl: function()
43581     {
43582         return  this.el.select('input',true).first();
43583     },
43584     
43585     canvasEl: function()
43586     {
43587         return this.el.select('canvas',true).first();
43588     },
43589     
43590     canvasElCtx: function()
43591     {
43592         return this.el.select('canvas',true).first().dom.getContext('2d');
43593     },
43594     
43595     getImage: function(type)
43596     {
43597         if(this.is_empty) {
43598             return false;
43599         }
43600         
43601         // encryption ?
43602         return this.canvasEl().dom.toDataURL('image/'+type, 1);
43603     },
43604     
43605     drawFromImage: function(img_src)
43606     {
43607         var img = new Image();
43608         
43609         img.onload = function(){
43610             this.canvasElCtx().drawImage(img, 0, 0);
43611         }.bind(this);
43612         
43613         img.src = img_src;
43614         
43615         this.is_empty = false;
43616     },
43617     
43618     selectImage: function()
43619     {
43620         this.fileEl().dom.click();
43621     },
43622     
43623     uploadImage: function(e)
43624     {
43625         var reader = new FileReader();
43626         
43627         reader.onload = function(e){
43628             var img = new Image();
43629             img.onload = function(){
43630                 this.reset();
43631                 this.canvasElCtx().drawImage(img, 0, 0);
43632             }.bind(this);
43633             img.src = e.target.result;
43634         }.bind(this);
43635         
43636         reader.readAsDataURL(e.target.files[0]);
43637     },
43638     
43639     // Bezier Point Constructor
43640     Point: (function () {
43641         function Point(x, y, time) {
43642             this.x = x;
43643             this.y = y;
43644             this.time = time || Date.now();
43645         }
43646         Point.prototype.distanceTo = function (start) {
43647             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
43648         };
43649         Point.prototype.equals = function (other) {
43650             return this.x === other.x && this.y === other.y && this.time === other.time;
43651         };
43652         Point.prototype.velocityFrom = function (start) {
43653             return this.time !== start.time
43654             ? this.distanceTo(start) / (this.time - start.time)
43655             : 0;
43656         };
43657         return Point;
43658     }()),
43659     
43660     
43661     // Bezier Constructor
43662     Bezier: (function () {
43663         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
43664             this.startPoint = startPoint;
43665             this.control2 = control2;
43666             this.control1 = control1;
43667             this.endPoint = endPoint;
43668             this.startWidth = startWidth;
43669             this.endWidth = endWidth;
43670         }
43671         Bezier.fromPoints = function (points, widths, scope) {
43672             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
43673             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
43674             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
43675         };
43676         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
43677             var dx1 = s1.x - s2.x;
43678             var dy1 = s1.y - s2.y;
43679             var dx2 = s2.x - s3.x;
43680             var dy2 = s2.y - s3.y;
43681             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
43682             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
43683             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
43684             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
43685             var dxm = m1.x - m2.x;
43686             var dym = m1.y - m2.y;
43687             var k = l2 / (l1 + l2);
43688             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
43689             var tx = s2.x - cm.x;
43690             var ty = s2.y - cm.y;
43691             return {
43692                 c1: new scope.Point(m1.x + tx, m1.y + ty),
43693                 c2: new scope.Point(m2.x + tx, m2.y + ty)
43694             };
43695         };
43696         Bezier.prototype.length = function () {
43697             var steps = 10;
43698             var length = 0;
43699             var px;
43700             var py;
43701             for (var i = 0; i <= steps; i += 1) {
43702                 var t = i / steps;
43703                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
43704                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
43705                 if (i > 0) {
43706                     var xdiff = cx - px;
43707                     var ydiff = cy - py;
43708                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
43709                 }
43710                 px = cx;
43711                 py = cy;
43712             }
43713             return length;
43714         };
43715         Bezier.prototype.point = function (t, start, c1, c2, end) {
43716             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
43717             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
43718             + (3.0 * c2 * (1.0 - t) * t * t)
43719             + (end * t * t * t);
43720         };
43721         return Bezier;
43722     }()),
43723     
43724     throttleStroke: function(fn, wait) {
43725       if (wait === void 0) { wait = 250; }
43726       var previous = 0;
43727       var timeout = null;
43728       var result;
43729       var storedContext;
43730       var storedArgs;
43731       var later = function () {
43732           previous = Date.now();
43733           timeout = null;
43734           result = fn.apply(storedContext, storedArgs);
43735           if (!timeout) {
43736               storedContext = null;
43737               storedArgs = [];
43738           }
43739       };
43740       return function wrapper() {
43741           var args = [];
43742           for (var _i = 0; _i < arguments.length; _i++) {
43743               args[_i] = arguments[_i];
43744           }
43745           var now = Date.now();
43746           var remaining = wait - (now - previous);
43747           storedContext = this;
43748           storedArgs = args;
43749           if (remaining <= 0 || remaining > wait) {
43750               if (timeout) {
43751                   clearTimeout(timeout);
43752                   timeout = null;
43753               }
43754               previous = now;
43755               result = fn.apply(storedContext, storedArgs);
43756               if (!timeout) {
43757                   storedContext = null;
43758                   storedArgs = [];
43759               }
43760           }
43761           else if (!timeout) {
43762               timeout = window.setTimeout(later, remaining);
43763           }
43764           return result;
43765       };
43766   }
43767   
43768 });
43769
43770  
43771
43772