complied results
[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|light|dark) default
965  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) 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     
994     this.addEvents({
995         // raw events
996         /**
997          * @event click
998          * When a butotn is pressed
999          * @param {Roo.bootstrap.Button} btn
1000          * @param {Roo.EventObject} e
1001          */
1002         "click" : true,
1003          /**
1004          * @event toggle
1005          * After the button has been toggles
1006          * @param {Roo.bootstrap.Button} btn
1007          * @param {Roo.EventObject} e
1008          * @param {boolean} pressed (also available as button.pressed)
1009          */
1010         "toggle" : true
1011     });
1012 };
1013
1014 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1015     html: false,
1016     active: false,
1017     weight: '',
1018     badge_weight: '',
1019     outline : false,
1020     size: '',
1021     tag: 'button',
1022     href: '',
1023     disabled: false,
1024     isClose: false,
1025     glyphicon: '',
1026     fa: '',
1027     badge: '',
1028     theme: 'default',
1029     inverse: false,
1030     
1031     toggle: false,
1032     ontext: 'ON',
1033     offtext: 'OFF',
1034     defaulton: true,
1035     preventDefault: true,
1036     removeClass: false,
1037     name: false,
1038     target: false,
1039      
1040     pressed : null,
1041      
1042     
1043     getAutoCreate : function(){
1044         
1045         var cfg = {
1046             tag : 'button',
1047             cls : 'roo-button',
1048             html: ''
1049         };
1050         
1051         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1052             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1053             this.tag = 'button';
1054         } else {
1055             cfg.tag = this.tag;
1056         }
1057         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1058         
1059         if (this.toggle == true) {
1060             cfg={
1061                 tag: 'div',
1062                 cls: 'slider-frame roo-button',
1063                 cn: [
1064                     {
1065                         tag: 'span',
1066                         'data-on-text':'ON',
1067                         'data-off-text':'OFF',
1068                         cls: 'slider-button',
1069                         html: this.offtext
1070                     }
1071                 ]
1072             };
1073             // why are we validating the weights?
1074             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1075                 cfg.cls +=  ' ' + this.weight;
1076             }
1077             
1078             return cfg;
1079         }
1080         
1081         if (this.isClose) {
1082             cfg.cls += ' close';
1083             
1084             cfg["aria-hidden"] = true;
1085             
1086             cfg.html = "&times;";
1087             
1088             return cfg;
1089         }
1090              
1091         
1092         if (this.theme==='default') {
1093             cfg.cls = 'btn roo-button';
1094             
1095             //if (this.parentType != 'Navbar') {
1096             this.weight = this.weight.length ?  this.weight : 'default';
1097             //}
1098             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1099                 
1100                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1101                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1102                 cfg.cls += ' btn-' + outline + weight;
1103                 if (this.weight == 'default') {
1104                     // BC
1105                     cfg.cls += ' btn-' + this.weight;
1106                 }
1107             }
1108         } else if (this.theme==='glow') {
1109             
1110             cfg.tag = 'a';
1111             cfg.cls = 'btn-glow roo-button';
1112             
1113             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1114                 
1115                 cfg.cls += ' ' + this.weight;
1116             }
1117         }
1118    
1119         
1120         if (this.inverse) {
1121             this.cls += ' inverse';
1122         }
1123         
1124         
1125         if (this.active || this.pressed === true) {
1126             cfg.cls += ' active';
1127         }
1128         
1129         if (this.disabled) {
1130             cfg.disabled = 'disabled';
1131         }
1132         
1133         if (this.items) {
1134             Roo.log('changing to ul' );
1135             cfg.tag = 'ul';
1136             this.glyphicon = 'caret';
1137             if (Roo.bootstrap.version == 4) {
1138                 this.fa = 'caret-down';
1139             }
1140             
1141         }
1142         
1143         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1144          
1145         //gsRoo.log(this.parentType);
1146         if (this.parentType === 'Navbar' && !this.parent().bar) {
1147             Roo.log('changing to li?');
1148             
1149             cfg.tag = 'li';
1150             
1151             cfg.cls = '';
1152             cfg.cn =  [{
1153                 tag : 'a',
1154                 cls : 'roo-button',
1155                 html : this.html,
1156                 href : this.href || '#'
1157             }];
1158             if (this.menu) {
1159                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1160                 cfg.cls += ' dropdown';
1161             }   
1162             
1163             delete cfg.html;
1164             
1165         }
1166         
1167        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1168         
1169         if (this.glyphicon) {
1170             cfg.html = ' ' + cfg.html;
1171             
1172             cfg.cn = [
1173                 {
1174                     tag: 'span',
1175                     cls: 'glyphicon glyphicon-' + this.glyphicon
1176                 }
1177             ];
1178         }
1179         if (this.fa) {
1180             cfg.html = ' ' + cfg.html;
1181             
1182             cfg.cn = [
1183                 {
1184                     tag: 'i',
1185                     cls: 'fa fas fa-' + this.fa
1186                 }
1187             ];
1188         }
1189         
1190         if (this.badge) {
1191             cfg.html += ' ';
1192             
1193             cfg.tag = 'a';
1194             
1195 //            cfg.cls='btn roo-button';
1196             
1197             cfg.href=this.href;
1198             
1199             var value = cfg.html;
1200             
1201             if(this.glyphicon){
1202                 value = {
1203                     tag: 'span',
1204                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1205                     html: this.html
1206                 };
1207             }
1208             if(this.fa){
1209                 value = {
1210                     tag: 'i',
1211                     cls: 'fa fas fa-' + this.fa,
1212                     html: this.html
1213                 };
1214             }
1215             
1216             var bw = this.badge_weight.length ? this.badge_weight :
1217                 (this.weight.length ? this.weight : 'secondary');
1218             bw = bw == 'default' ? 'secondary' : bw;
1219             
1220             cfg.cn = [
1221                 value,
1222                 {
1223                     tag: 'span',
1224                     cls: 'badge badge-' + bw,
1225                     html: this.badge
1226                 }
1227             ];
1228             
1229             cfg.html='';
1230         }
1231         
1232         if (this.menu) {
1233             cfg.cls += ' dropdown';
1234             cfg.html = typeof(cfg.html) != 'undefined' ?
1235                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1236         }
1237         
1238         if (cfg.tag !== 'a' && this.href !== '') {
1239             throw "Tag must be a to set href.";
1240         } else if (this.href.length > 0) {
1241             cfg.href = this.href;
1242         }
1243         
1244         if(this.removeClass){
1245             cfg.cls = '';
1246         }
1247         
1248         if(this.target){
1249             cfg.target = this.target;
1250         }
1251         
1252         return cfg;
1253     },
1254     initEvents: function() {
1255        // Roo.log('init events?');
1256 //        Roo.log(this.el.dom);
1257         // add the menu...
1258         
1259         if (typeof (this.menu) != 'undefined') {
1260             this.menu.parentType = this.xtype;
1261             this.menu.triggerEl = this.el;
1262             this.addxtype(Roo.apply({}, this.menu));
1263         }
1264
1265
1266        if (this.el.hasClass('roo-button')) {
1267             this.el.on('click', this.onClick, this);
1268        } else {
1269             this.el.select('.roo-button').on('click', this.onClick, this);
1270        }
1271        
1272        if(this.removeClass){
1273            this.el.on('click', this.onClick, this);
1274        }
1275        
1276        this.el.enableDisplayMode();
1277         
1278     },
1279     onClick : function(e)
1280     {
1281         if (this.disabled) {
1282             return;
1283         }
1284         
1285         Roo.log('button on click ');
1286         if(this.preventDefault){
1287             e.preventDefault();
1288         }
1289         
1290         if (this.pressed === true || this.pressed === false) {
1291             this.toggleActive(e);
1292         }
1293         
1294         
1295         this.fireEvent('click', this, e);
1296     },
1297     
1298     /**
1299      * Enables this button
1300      */
1301     enable : function()
1302     {
1303         this.disabled = false;
1304         this.el.removeClass('disabled');
1305     },
1306     
1307     /**
1308      * Disable this button
1309      */
1310     disable : function()
1311     {
1312         this.disabled = true;
1313         this.el.addClass('disabled');
1314     },
1315      /**
1316      * sets the active state on/off, 
1317      * @param {Boolean} state (optional) Force a particular state
1318      */
1319     setActive : function(v) {
1320         
1321         this.el[v ? 'addClass' : 'removeClass']('active');
1322         this.pressed = v;
1323     },
1324      /**
1325      * toggles the current active state 
1326      */
1327     toggleActive : function(e)
1328     {
1329         this.setActive(!this.pressed);
1330         this.fireEvent('toggle', this, e, !this.pressed);
1331     },
1332      /**
1333      * get the current active state
1334      * @return {boolean} true if it's active
1335      */
1336     isActive : function()
1337     {
1338         return this.el.hasClass('active');
1339     },
1340     /**
1341      * set the text of the first selected button
1342      */
1343     setText : function(str)
1344     {
1345         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1346     },
1347     /**
1348      * get the text of the first selected button
1349      */
1350     getText : function()
1351     {
1352         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1353     },
1354     
1355     setWeight : function(str)
1356     {
1357         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1358         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1359         this.weight = str;
1360         var outline = this.outline ? 'outline-' : '';
1361         if (str == 'default') {
1362             this.el.addClass('btn-default btn-outline-secondary');        
1363             return;
1364         }
1365         this.el.addClass('btn-' + outline + str);        
1366     }
1367     
1368     
1369 });
1370 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1371
1372 Roo.bootstrap.Button.weights = [
1373     'default',
1374     'secondary' ,
1375     'primary',
1376     'success',
1377     'info',
1378     'warning',
1379     'danger',
1380     'link',
1381     'light',
1382     'dark'              
1383    
1384 ];/*
1385  * - LGPL
1386  *
1387  * column
1388  * 
1389  */
1390
1391 /**
1392  * @class Roo.bootstrap.Column
1393  * @extends Roo.bootstrap.Component
1394  * Bootstrap Column class
1395  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1396  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1397  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1398  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1399  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1400  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1401  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1402  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1403  *
1404  * 
1405  * @cfg {Boolean} hidden (true|false) hide the element
1406  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1407  * @cfg {String} fa (ban|check|...) font awesome icon
1408  * @cfg {Number} fasize (1|2|....) font awsome size
1409
1410  * @cfg {String} icon (info-sign|check|...) glyphicon name
1411
1412  * @cfg {String} html content of column.
1413  * 
1414  * @constructor
1415  * Create a new Column
1416  * @param {Object} config The config object
1417  */
1418
1419 Roo.bootstrap.Column = function(config){
1420     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1421 };
1422
1423 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1424     
1425     xs: false,
1426     sm: false,
1427     md: false,
1428     lg: false,
1429     xsoff: false,
1430     smoff: false,
1431     mdoff: false,
1432     lgoff: false,
1433     html: '',
1434     offset: 0,
1435     alert: false,
1436     fa: false,
1437     icon : false,
1438     hidden : false,
1439     fasize : 1,
1440     
1441     getAutoCreate : function(){
1442         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1443         
1444         cfg = {
1445             tag: 'div',
1446             cls: 'column'
1447         };
1448         
1449         var settings=this;
1450         var sizes =   ['xs','sm','md','lg'];
1451         sizes.map(function(size ,ix){
1452             //Roo.log( size + ':' + settings[size]);
1453             
1454             if (settings[size+'off'] !== false) {
1455                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1456             }
1457             
1458             if (settings[size] === false) {
1459                 return;
1460             }
1461             
1462             if (!settings[size]) { // 0 = hidden
1463                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1464                 // bootsrap4
1465                 for (var i = ix; i > -1; i--) {
1466                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1467                 }
1468                 
1469                 
1470                 return;
1471             }
1472             cfg.cls += ' col-' + size + '-' + settings[size] + (
1473                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1474             );
1475             
1476         });
1477         
1478         if (this.hidden) {
1479             cfg.cls += ' hidden';
1480         }
1481         
1482         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1483             cfg.cls +=' alert alert-' + this.alert;
1484         }
1485         
1486         
1487         if (this.html.length) {
1488             cfg.html = this.html;
1489         }
1490         if (this.fa) {
1491             var fasize = '';
1492             if (this.fasize > 1) {
1493                 fasize = ' fa-' + this.fasize + 'x';
1494             }
1495             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1496             
1497             
1498         }
1499         if (this.icon) {
1500             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1501         }
1502         
1503         return cfg;
1504     }
1505    
1506 });
1507
1508  
1509
1510  /*
1511  * - LGPL
1512  *
1513  * page container.
1514  * 
1515  */
1516
1517
1518 /**
1519  * @class Roo.bootstrap.Container
1520  * @extends Roo.bootstrap.Component
1521  * Bootstrap Container class
1522  * @cfg {Boolean} jumbotron is it a jumbotron element
1523  * @cfg {String} html content of element
1524  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1525  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1526  * @cfg {String} header content of header (for panel)
1527  * @cfg {String} footer content of footer (for panel)
1528  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1529  * @cfg {String} tag (header|aside|section) type of HTML tag.
1530  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1531  * @cfg {String} fa font awesome icon
1532  * @cfg {String} icon (info-sign|check|...) glyphicon name
1533  * @cfg {Boolean} hidden (true|false) hide the element
1534  * @cfg {Boolean} expandable (true|false) default false
1535  * @cfg {Boolean} expanded (true|false) default true
1536  * @cfg {String} rheader contet on the right of header
1537  * @cfg {Boolean} clickable (true|false) default false
1538
1539  *     
1540  * @constructor
1541  * Create a new Container
1542  * @param {Object} config The config object
1543  */
1544
1545 Roo.bootstrap.Container = function(config){
1546     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1547     
1548     this.addEvents({
1549         // raw events
1550          /**
1551          * @event expand
1552          * After the panel has been expand
1553          * 
1554          * @param {Roo.bootstrap.Container} this
1555          */
1556         "expand" : true,
1557         /**
1558          * @event collapse
1559          * After the panel has been collapsed
1560          * 
1561          * @param {Roo.bootstrap.Container} this
1562          */
1563         "collapse" : true,
1564         /**
1565          * @event click
1566          * When a element is chick
1567          * @param {Roo.bootstrap.Container} this
1568          * @param {Roo.EventObject} e
1569          */
1570         "click" : true
1571     });
1572 };
1573
1574 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1575     
1576     jumbotron : false,
1577     well: '',
1578     panel : '',
1579     header: '',
1580     footer : '',
1581     sticky: '',
1582     tag : false,
1583     alert : false,
1584     fa: false,
1585     icon : false,
1586     expandable : false,
1587     rheader : '',
1588     expanded : true,
1589     clickable: false,
1590   
1591      
1592     getChildContainer : function() {
1593         
1594         if(!this.el){
1595             return false;
1596         }
1597         
1598         if (this.panel.length) {
1599             return this.el.select('.panel-body',true).first();
1600         }
1601         
1602         return this.el;
1603     },
1604     
1605     
1606     getAutoCreate : function(){
1607         
1608         var cfg = {
1609             tag : this.tag || 'div',
1610             html : '',
1611             cls : ''
1612         };
1613         if (this.jumbotron) {
1614             cfg.cls = 'jumbotron';
1615         }
1616         
1617         
1618         
1619         // - this is applied by the parent..
1620         //if (this.cls) {
1621         //    cfg.cls = this.cls + '';
1622         //}
1623         
1624         if (this.sticky.length) {
1625             
1626             var bd = Roo.get(document.body);
1627             if (!bd.hasClass('bootstrap-sticky')) {
1628                 bd.addClass('bootstrap-sticky');
1629                 Roo.select('html',true).setStyle('height', '100%');
1630             }
1631              
1632             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1633         }
1634         
1635         
1636         if (this.well.length) {
1637             switch (this.well) {
1638                 case 'lg':
1639                 case 'sm':
1640                     cfg.cls +=' well well-' +this.well;
1641                     break;
1642                 default:
1643                     cfg.cls +=' well';
1644                     break;
1645             }
1646         }
1647         
1648         if (this.hidden) {
1649             cfg.cls += ' hidden';
1650         }
1651         
1652         
1653         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1654             cfg.cls +=' alert alert-' + this.alert;
1655         }
1656         
1657         var body = cfg;
1658         
1659         if (this.panel.length) {
1660             cfg.cls += ' panel panel-' + this.panel;
1661             cfg.cn = [];
1662             if (this.header.length) {
1663                 
1664                 var h = [];
1665                 
1666                 if(this.expandable){
1667                     
1668                     cfg.cls = cfg.cls + ' expandable';
1669                     
1670                     h.push({
1671                         tag: 'i',
1672                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1673                     });
1674                     
1675                 }
1676                 
1677                 h.push(
1678                     {
1679                         tag: 'span',
1680                         cls : 'panel-title',
1681                         html : (this.expandable ? '&nbsp;' : '') + this.header
1682                     },
1683                     {
1684                         tag: 'span',
1685                         cls: 'panel-header-right',
1686                         html: this.rheader
1687                     }
1688                 );
1689                 
1690                 cfg.cn.push({
1691                     cls : 'panel-heading',
1692                     style : this.expandable ? 'cursor: pointer' : '',
1693                     cn : h
1694                 });
1695                 
1696             }
1697             
1698             body = false;
1699             cfg.cn.push({
1700                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1701                 html : this.html
1702             });
1703             
1704             
1705             if (this.footer.length) {
1706                 cfg.cn.push({
1707                     cls : 'panel-footer',
1708                     html : this.footer
1709                     
1710                 });
1711             }
1712             
1713         }
1714         
1715         if (body) {
1716             body.html = this.html || cfg.html;
1717             // prefix with the icons..
1718             if (this.fa) {
1719                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1720             }
1721             if (this.icon) {
1722                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1723             }
1724             
1725             
1726         }
1727         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1728             cfg.cls =  'container';
1729         }
1730         
1731         return cfg;
1732     },
1733     
1734     initEvents: function() 
1735     {
1736         if(this.expandable){
1737             var headerEl = this.headerEl();
1738         
1739             if(headerEl){
1740                 headerEl.on('click', this.onToggleClick, this);
1741             }
1742         }
1743         
1744         if(this.clickable){
1745             this.el.on('click', this.onClick, this);
1746         }
1747         
1748     },
1749     
1750     onToggleClick : function()
1751     {
1752         var headerEl = this.headerEl();
1753         
1754         if(!headerEl){
1755             return;
1756         }
1757         
1758         if(this.expanded){
1759             this.collapse();
1760             return;
1761         }
1762         
1763         this.expand();
1764     },
1765     
1766     expand : function()
1767     {
1768         if(this.fireEvent('expand', this)) {
1769             
1770             this.expanded = true;
1771             
1772             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1773             
1774             this.el.select('.panel-body',true).first().removeClass('hide');
1775             
1776             var toggleEl = this.toggleEl();
1777
1778             if(!toggleEl){
1779                 return;
1780             }
1781
1782             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1783         }
1784         
1785     },
1786     
1787     collapse : function()
1788     {
1789         if(this.fireEvent('collapse', this)) {
1790             
1791             this.expanded = false;
1792             
1793             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1794             this.el.select('.panel-body',true).first().addClass('hide');
1795         
1796             var toggleEl = this.toggleEl();
1797
1798             if(!toggleEl){
1799                 return;
1800             }
1801
1802             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1803         }
1804     },
1805     
1806     toggleEl : function()
1807     {
1808         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1809             return;
1810         }
1811         
1812         return this.el.select('.panel-heading .fa',true).first();
1813     },
1814     
1815     headerEl : function()
1816     {
1817         if(!this.el || !this.panel.length || !this.header.length){
1818             return;
1819         }
1820         
1821         return this.el.select('.panel-heading',true).first()
1822     },
1823     
1824     bodyEl : function()
1825     {
1826         if(!this.el || !this.panel.length){
1827             return;
1828         }
1829         
1830         return this.el.select('.panel-body',true).first()
1831     },
1832     
1833     titleEl : function()
1834     {
1835         if(!this.el || !this.panel.length || !this.header.length){
1836             return;
1837         }
1838         
1839         return this.el.select('.panel-title',true).first();
1840     },
1841     
1842     setTitle : function(v)
1843     {
1844         var titleEl = this.titleEl();
1845         
1846         if(!titleEl){
1847             return;
1848         }
1849         
1850         titleEl.dom.innerHTML = v;
1851     },
1852     
1853     getTitle : function()
1854     {
1855         
1856         var titleEl = this.titleEl();
1857         
1858         if(!titleEl){
1859             return '';
1860         }
1861         
1862         return titleEl.dom.innerHTML;
1863     },
1864     
1865     setRightTitle : function(v)
1866     {
1867         var t = this.el.select('.panel-header-right',true).first();
1868         
1869         if(!t){
1870             return;
1871         }
1872         
1873         t.dom.innerHTML = v;
1874     },
1875     
1876     onClick : function(e)
1877     {
1878         e.preventDefault();
1879         
1880         this.fireEvent('click', this, e);
1881     }
1882 });
1883
1884  /*
1885  *  - LGPL
1886  *
1887  *  This is BS4's Card element.. - similar to our containers probably..
1888  * 
1889  */
1890 /**
1891  * @class Roo.bootstrap.Card
1892  * @extends Roo.bootstrap.Component
1893  * Bootstrap Card class
1894  *
1895  *
1896  * possible... may not be implemented..
1897  * @cfg {String} header_image  src url of image.
1898  * @cfg {String|Object} header
1899  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1900  * 
1901  * @cfg {String} title
1902  * @cfg {String} subtitle
1903  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1904  * @cfg {String} footer
1905  
1906  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1907  * 
1908  * @cfg {String} margin (0|1|2|3|4|5|auto)
1909  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1910  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1911  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1912  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1913  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1914  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1915  *
1916  * @cfg {String} padding (0|1|2|3|4|5)
1917  * @cfg {String} padding_top (0|1|2|3|4|5)
1918  * @cfg {String} padding_bottom (0|1|2|3|4|5)
1919  * @cfg {String} padding_left (0|1|2|3|4|5)
1920  * @cfg {String} padding_right (0|1|2|3|4|5)
1921  * @cfg {String} padding_x (0|1|2|3|4|5)
1922  * @cfg {String} padding_y (0|1|2|3|4|5)
1923  *
1924  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1925  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1926  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1927  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1928  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1929  
1930  * @config {Boolean} dragable  if this card can be dragged.
1931  * @config {String} drag_group  group for drag
1932  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
1933  * @config {String} drop_group  group for drag
1934  * 
1935  * @config {Boolean} collapsable can the body be collapsed.
1936  * @config {Boolean} collapsed is the body collapsed when rendered...
1937  * @config {Boolean} rotateable can the body be rotated by clicking on it..
1938  * @config {Boolean} rotated is the body rotated when rendered...
1939  * 
1940  * @constructor
1941  * Create a new Container
1942  * @param {Object} config The config object
1943  */
1944
1945 Roo.bootstrap.Card = function(config){
1946     Roo.bootstrap.Card.superclass.constructor.call(this, config);
1947     
1948     this.addEvents({
1949          // raw events
1950         /**
1951          * @event drop
1952          * When a element a card is dropped
1953          * @param {Roo.bootstrap.Element} this
1954          * @param {Roo.Element} n the node being dropped?
1955          * @param {Object} dd Drag and drop data
1956          * @param {Roo.EventObject} e
1957          * @param {Roo.EventObject} data  the data passed via getDragData
1958          */
1959         'drop' : true,
1960          /**
1961          * @event rotate
1962          * When a element a card is rotate
1963          * @param {Roo.bootstrap.Element} this
1964          * @param {Roo.Element} n the node being dropped?
1965          * @param {Boolean} rotate status
1966          */
1967         'rotate' : true
1968         
1969     });
1970 };
1971
1972
1973 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
1974     
1975     
1976     weight : '',
1977     
1978     margin: '', /// may be better in component?
1979     margin_top: '', 
1980     margin_bottom: '', 
1981     margin_left: '',
1982     margin_right: '',
1983     margin_x: '',
1984     margin_y: '',
1985     
1986     padding : '',
1987     padding_top: '', 
1988     padding_bottom: '', 
1989     padding_left: '',
1990     padding_right: '',
1991     padding_x: '',
1992     padding_y: '',
1993     
1994     display: '', 
1995     display_xs: '', 
1996     display_sm: '', 
1997     display_lg: '',
1998     display_xl: '',
1999  
2000     header_image  : '',
2001     header : '',
2002     header_size : 0,
2003     title : '',
2004     subtitle : '',
2005     html : '',
2006     footer: '',
2007
2008     collapsable : false,
2009     collapsed : false,
2010     rotateable : false,
2011     rotated : false,
2012     
2013     dragable : false,
2014     drag_group : false,
2015     dropable : false,
2016     drop_group : false,
2017     childContainer : false,
2018     dropEl : false, /// the dom placeholde element that indicates drop location.
2019     
2020     layoutCls : function()
2021     {
2022         var cls = '';
2023         var t = this;
2024         Roo.log(this.margin_bottom.length);
2025         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2026             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2027             
2028             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2029                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2030             }
2031             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2032                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2033             }
2034         });
2035         
2036         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2037             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2038                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['margin' + (v.length ? '_' : '') + v]
2039             }
2040         });
2041         
2042         // more generic support?
2043         if (this.hidden) {
2044             cls += ' d-none';
2045         }
2046         
2047         return cls;
2048     },
2049  
2050        // Roo.log("Call onRender: " + this.xtype);
2051         /*  We are looking at something like this.
2052 <div class="card">
2053     <img src="..." class="card-img-top" alt="...">
2054     <div class="card-body">
2055         <h5 class="card-title">Card title</h5>
2056          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2057
2058         >> this bit is really the body...
2059         <div> << we will ad dthis in hopefully it will not break shit.
2060         
2061         ** card text does not actually have any styling...
2062         
2063             <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>
2064         
2065         </div> <<
2066           <a href="#" class="card-link">Card link</a>
2067           
2068     </div>
2069     <div class="card-footer">
2070         <small class="text-muted">Last updated 3 mins ago</small>
2071     </div>
2072 </div>
2073          */
2074     getAutoCreate : function(){
2075         
2076         var cfg = {
2077             tag : 'div',
2078             cls : 'card',
2079             cn : [ ]
2080         };
2081         
2082         if (this.weight.length && this.weight != 'light') {
2083             cfg.cls += ' text-white';
2084         } else {
2085             cfg.cls += ' text-dark'; // need as it's nested..
2086         }
2087         if (this.weight.length) {
2088             cfg.cls += ' bg-' + this.weight;
2089         }
2090         
2091         cfg.cls += this.layoutCls(); 
2092         
2093         var hdr = false;
2094         if (this.header.length) {
2095             hdr = {
2096                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2097                 cls : 'card-header',
2098                 cn : []
2099             };
2100             cfg.cn.push(hdr);
2101             hdr_ctr = hdr;
2102         } else {
2103             hdr = {
2104                 tag : 'div',
2105                 cls : 'card-header d-none',
2106                 cn : []
2107             };
2108             cfg.cn.push(hdr);
2109         }
2110         if (this.collapsable) {
2111             hdr_ctr = {
2112             tag : 'a',
2113             cls : 'd-block user-select-none',
2114             cn: [
2115                     {
2116                         tag: 'i',
2117                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2118                     }
2119                    
2120                 ]
2121             };
2122             hdr.cn.push(hdr_ctr);
2123         }
2124         
2125         hdr_ctr.cn.push(        {
2126             tag: 'span',
2127             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2128             html : this.header
2129         });
2130         
2131         
2132         if (this.header_image.length) {
2133             cfg.cn.push({
2134                 tag : 'img',
2135                 cls : 'card-img-top',
2136                 src: this.header_image // escape?
2137             });
2138         } else {
2139             cfg.cn.push({
2140                     tag : 'div',
2141                     cls : 'card-img-top d-none' 
2142                 });
2143         }
2144             
2145         var body = {
2146             tag : 'div',
2147             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2148             cn : []
2149         };
2150         var obody = body;
2151         if (this.collapsable || this.rotateable) {
2152             obody = {
2153                 tag: 'div',
2154                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2155                 cn : [  body ]
2156             };
2157         }
2158         
2159         cfg.cn.push(obody);
2160         
2161         if (this.title.length) {
2162             body.cn.push({
2163                 tag : 'div',
2164                 cls : 'card-title',
2165                 src: this.title // escape?
2166             });
2167         }  
2168         
2169         if (this.subtitle.length) {
2170             body.cn.push({
2171                 tag : 'div',
2172                 cls : 'card-title',
2173                 src: this.subtitle // escape?
2174             });
2175         }
2176         
2177         body.cn.push({
2178             tag : 'div',
2179             cls : 'roo-card-body-ctr'
2180         });
2181         
2182         if (this.html.length) {
2183             body.cn.push({
2184                 tag: 'div',
2185                 html : this.html
2186             });
2187         }
2188         // fixme ? handle objects?
2189         
2190         if (this.footer.length) {
2191            
2192             cfg.cn.push({
2193                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2194                 html : this.footer
2195             });
2196             
2197         } else {
2198             cfg.cn.push({cls : 'card-footer d-none'});
2199         }
2200         
2201         // footer...
2202         
2203         return cfg;
2204     },
2205     
2206     
2207     getCardHeader : function()
2208     {
2209         var  ret = this.el.select('.card-header',true).first();
2210         if (ret.hasClass('d-none')) {
2211             ret.removeClass('d-none');
2212         }
2213         
2214         return ret;
2215     },
2216     getCardFooter : function()
2217     {
2218         var  ret = this.el.select('.card-footer',true).first();
2219         if (ret.hasClass('d-none')) {
2220             ret.removeClass('d-none');
2221         }
2222         
2223         return ret;
2224     },
2225     getCardImageTop : function()
2226     {
2227         var  ret = this.el.select('.card-img-top',true).first();
2228         if (ret.hasClass('d-none')) {
2229             ret.removeClass('d-none');
2230         }
2231             
2232         return ret;
2233     },
2234     
2235     getChildContainer : function()
2236     {
2237         
2238         if(!this.el){
2239             return false;
2240         }
2241         return this.el.select('.roo-card-body-ctr',true).first();    
2242     },
2243     
2244     initEvents: function() 
2245     {
2246         
2247         this.bodyEl = this.getChildContainer();
2248         if(this.dragable){
2249             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2250                     containerScroll: true,
2251                     ddGroup: this.drag_group || 'default_card_drag_group'
2252             });
2253             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2254         }
2255         if (this.dropable) {
2256             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2257                 containerScroll: true,
2258                 ddGroup: this.drop_group || 'default_card_drag_group'
2259             });
2260             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2261             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2262             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2263             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2264             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2265         }
2266         
2267         if (this.collapsable) {
2268             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2269         }
2270         if (this.rotateable) {
2271             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2272         }
2273         this.collapsableEl = this.el.select('.roo-collapsable').first();
2274          
2275         this.footerEl = this.el.select('.card-footer').first();
2276         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle');
2277         this.headerEl = this.el.select('.roo-card-header-ctr').first();
2278         
2279         if (this.rotated) {
2280             this.el.addClass('roo-card-rotated');
2281             this.fireEvent('rotate', this, true);
2282         }
2283         
2284     },
2285     getDragData : function(e)
2286     {
2287         var target = this.getEl();
2288         if (target) {
2289             //this.handleSelection(e);
2290             
2291             var dragData = {
2292                 source: this,
2293                 copy: false,
2294                 nodes: this.getEl(),
2295                 records: []
2296             };
2297             
2298             
2299             dragData.ddel = target.dom ;    // the div element
2300             Roo.log(target.getWidth( ));
2301             dragData.ddel.style.width = target.getWidth() + 'px';
2302             
2303             return dragData;
2304         }
2305         return false;
2306     },
2307     /**
2308     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2309     *    whole Element becomes the target, and this causes the drop gesture to append.
2310     */
2311     getTargetFromEvent : function(e, dragged_card_el)
2312     {
2313         var target = e.getTarget();
2314         while ((target !== null) && (target.parentNode != this.bodyEl.dom)) {
2315             target = target.parentNode;
2316         }
2317         
2318         var ret = {
2319             position: '',
2320             cards : [],
2321             card_n : -1,
2322             items_n : -1,
2323             card : false 
2324         };
2325         
2326         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2327         // see if target is one of the 'cards'...
2328         
2329         
2330         //Roo.log(this.items.length);
2331         var pos = false;
2332         
2333         var last_card_n = 0;
2334         var cards_len  = 0;
2335         for (var i = 0;i< this.items.length;i++) {
2336             
2337             if (!this.items[i].el.hasClass('card')) {
2338                  continue;
2339             }
2340             pos = this.getDropPoint(e, this.items[i].el.dom);
2341             
2342             cards_len = ret.cards.length;
2343             //Roo.log(this.items[i].el.dom.id);
2344             ret.cards.push(this.items[i]);
2345             last_card_n  = i;
2346             if (ret.card_n < 0 && pos == 'above') {
2347                 ret.position = cards_len > 0 ? 'below' : pos;
2348                 ret.items_n = i > 0 ? i - 1 : 0;
2349                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2350                 ret.card = ret.cards[ret.card_n];
2351             }
2352         }
2353         if (!ret.cards.length) {
2354             ret.card = true;
2355             ret.position = 'below';
2356             ret.items_n;
2357             return ret;
2358         }
2359         // could not find a card.. stick it at the end..
2360         if (ret.card_n < 0) {
2361             ret.card_n = last_card_n;
2362             ret.card = ret.cards[last_card_n];
2363             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2364             ret.position = 'below';
2365         }
2366         
2367         if (this.items[ret.items_n].el == dragged_card_el) {
2368             return false;
2369         }
2370         
2371         if (ret.position == 'below') {
2372             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2373             
2374             if (card_after  && card_after.el == dragged_card_el) {
2375                 return false;
2376             }
2377             return ret;
2378         }
2379         
2380         // its's after ..
2381         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2382         
2383         if (card_before  && card_before.el == dragged_card_el) {
2384             return false;
2385         }
2386         
2387         return ret;
2388     },
2389     
2390     onNodeEnter : function(n, dd, e, data){
2391         return false;
2392     },
2393     onNodeOver : function(n, dd, e, data)
2394     {
2395        
2396         var target_info = this.getTargetFromEvent(e,data.source.el);
2397         if (target_info === false) {
2398             this.dropPlaceHolder('hide');
2399             return false;
2400         }
2401         Roo.log(['getTargetFromEvent', target_info ]);
2402         
2403          
2404         this.dropPlaceHolder('show', target_info,data);
2405         
2406         return false; 
2407     },
2408     onNodeOut : function(n, dd, e, data){
2409         this.dropPlaceHolder('hide');
2410      
2411     },
2412     onNodeDrop : function(n, dd, e, data)
2413     {
2414         
2415         // call drop - return false if
2416         
2417         // this could actually fail - if the Network drops..
2418         // we will ignore this at present..- client should probably reload
2419         // the whole set of cards if stuff like that fails.
2420         
2421         
2422         var info = this.getTargetFromEvent(e,data.source.el);
2423         if (info === false) {
2424             return false;
2425         }
2426         
2427         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
2428             return false;
2429         }
2430          
2431         this.dropPlaceHolder('hide');
2432         
2433         // do the dom manipulation first..
2434         var dom = data.source.el.dom;
2435         dom.parentNode.removeChild(dom);
2436         
2437         
2438         if (info.card !== true) {
2439             var cardel = info.card.el.dom;
2440             
2441             if (info.position == 'above') {
2442                 cardel.parentNode.insertBefore(dom, cardel);
2443             } else if (cardel.nextSibling) {
2444                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2445             } else {
2446                 cardel.parentNode.append(dom);
2447             }
2448         } else {
2449             // card container???
2450             this.bodyEl.dom.append(dom);
2451         }
2452         
2453         //FIXME HANDLE card = true 
2454         
2455         // add this to the correct place in items.
2456         
2457         
2458         
2459         // remove Card from items.
2460         
2461         var old_parent = data.source.parent();
2462         
2463         old_parent.items = old_parent.items.filter(function(e) { return e != data.source });
2464         
2465         if (this.items.length) {
2466             var nitems = [];
2467             //Roo.log([info.items_n, info.position, this.items.length]);
2468             for (var i =0; i < this.items.length; i++) {
2469                 if (i == info.items_n && info.position == 'above') {
2470                     nitems.push(data.source);
2471                 }
2472                 nitems.push(this.items[i]);
2473                 if (i == info.items_n && info.position == 'below') {
2474                     nitems.push(data.source);
2475                 }
2476             }
2477             this.items = nitems;
2478             Roo.log(this.items);
2479         } else {
2480             this.items.push(data.source);
2481         }
2482         
2483         data.source.parentId = this.id;
2484         
2485         return true;
2486     },
2487     
2488     /**    Decide whether to drop above or below a View node. */
2489     getDropPoint : function(e, n, dd)
2490     {
2491         if (dd) {
2492              return false;
2493         }
2494         if (n == this.bodyEl.dom) {
2495             return "above";
2496         }
2497         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2498         var c = t + (b - t) / 2;
2499         var y = Roo.lib.Event.getPageY(e);
2500         if(y <= c) {
2501             return "above";
2502         }else{
2503             return "below";
2504         }
2505     },
2506     onToggleCollapse : function(e)
2507         {
2508         if (this.collapsed) {
2509             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2510             this.collapsableEl.addClass('show');
2511             this.collapsed = false;
2512             return;
2513         }
2514         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2515         this.collapsableEl.removeClass('show');
2516         this.collapsed = true;
2517         
2518     
2519     },
2520     
2521     onToggleRotate : function(e)
2522     {
2523         this.collapsableEl.removeClass('show');
2524         this.footerEl.removeClass('d-none');
2525         this.el.removeClass('roo-card-rotated');
2526         this.el.removeClass('d-none');
2527         if (this.rotated) {
2528             
2529             this.collapsableEl.addClass('show');
2530             this.rotated = false;
2531             this.fireEvent('rotate', this, this.rotated);
2532             return;
2533         }
2534         this.el.addClass('roo-card-rotated');
2535         this.footerEl.addClass('d-none');
2536         this.el.select('.roo-collapsable').removeClass('show');
2537         
2538         this.rotated = true;
2539         this.fireEvent('rotate', this, this.rotated);
2540     
2541     },
2542     
2543     dropPlaceHolder: function (action, info, data)
2544     {
2545         if (this.dropEl === false) {
2546             this.dropEl = Roo.DomHelper.append(this.bodyEl, {
2547             cls : 'd-none'
2548             },true);
2549         }
2550         this.dropEl.removeClass(['d-none', 'd-block']);        
2551         if (action == 'hide') {
2552             
2553             this.dropEl.addClass('d-none');
2554             return;
2555         }
2556         // FIXME - info.card == true!!!
2557         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2558         
2559         if (info.card !== true) {
2560             var cardel = info.card.el.dom;
2561             
2562             if (info.position == 'above') {
2563                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2564             } else if (cardel.nextSibling) {
2565                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2566             } else {
2567                 cardel.parentNode.append(this.dropEl.dom);
2568             }
2569         } else {
2570             // card container???
2571             this.bodyEl.dom.append(this.dropEl.dom);
2572         }
2573         
2574         this.dropEl.addClass('d-block roo-card-dropzone');
2575         
2576         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2577         
2578         
2579     
2580     
2581     
2582     },
2583     setHeaderText: function(html)
2584     {
2585         this.headerEl.dom.innerHTML = html;
2586     }
2587
2588     
2589 });
2590
2591 /*
2592  * - LGPL
2593  *
2594  * Card header - holder for the card header elements.
2595  * 
2596  */
2597
2598 /**
2599  * @class Roo.bootstrap.CardHeader
2600  * @extends Roo.bootstrap.Element
2601  * Bootstrap CardHeader class
2602  * @constructor
2603  * Create a new Card Header - that you can embed children into
2604  * @param {Object} config The config object
2605  */
2606
2607 Roo.bootstrap.CardHeader = function(config){
2608     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2609 };
2610
2611 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2612     
2613     
2614     container_method : 'getCardHeader' 
2615     
2616      
2617     
2618     
2619    
2620 });
2621
2622  
2623
2624  /*
2625  * - LGPL
2626  *
2627  * Card footer - holder for the card footer elements.
2628  * 
2629  */
2630
2631 /**
2632  * @class Roo.bootstrap.CardFooter
2633  * @extends Roo.bootstrap.Element
2634  * Bootstrap CardFooter class
2635  * @constructor
2636  * Create a new Card Footer - that you can embed children into
2637  * @param {Object} config The config object
2638  */
2639
2640 Roo.bootstrap.CardFooter = function(config){
2641     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2642 };
2643
2644 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2645     
2646     
2647     container_method : 'getCardFooter' 
2648     
2649      
2650     
2651     
2652    
2653 });
2654
2655  
2656
2657  /*
2658  * - LGPL
2659  *
2660  * Card header - holder for the card header elements.
2661  * 
2662  */
2663
2664 /**
2665  * @class Roo.bootstrap.CardImageTop
2666  * @extends Roo.bootstrap.Element
2667  * Bootstrap CardImageTop class
2668  * @constructor
2669  * Create a new Card Image Top container
2670  * @param {Object} config The config object
2671  */
2672
2673 Roo.bootstrap.CardImageTop = function(config){
2674     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2675 };
2676
2677 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2678     
2679    
2680     container_method : 'getCardImageTop' 
2681     
2682      
2683     
2684    
2685 });
2686
2687  
2688
2689  /*
2690  * - LGPL
2691  *
2692  * image
2693  * 
2694  */
2695
2696
2697 /**
2698  * @class Roo.bootstrap.Img
2699  * @extends Roo.bootstrap.Component
2700  * Bootstrap Img class
2701  * @cfg {Boolean} imgResponsive false | true
2702  * @cfg {String} border rounded | circle | thumbnail
2703  * @cfg {String} src image source
2704  * @cfg {String} alt image alternative text
2705  * @cfg {String} href a tag href
2706  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2707  * @cfg {String} xsUrl xs image source
2708  * @cfg {String} smUrl sm image source
2709  * @cfg {String} mdUrl md image source
2710  * @cfg {String} lgUrl lg image source
2711  * 
2712  * @constructor
2713  * Create a new Input
2714  * @param {Object} config The config object
2715  */
2716
2717 Roo.bootstrap.Img = function(config){
2718     Roo.bootstrap.Img.superclass.constructor.call(this, config);
2719     
2720     this.addEvents({
2721         // img events
2722         /**
2723          * @event click
2724          * The img click event for the img.
2725          * @param {Roo.EventObject} e
2726          */
2727         "click" : true
2728     });
2729 };
2730
2731 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
2732     
2733     imgResponsive: true,
2734     border: '',
2735     src: 'about:blank',
2736     href: false,
2737     target: false,
2738     xsUrl: '',
2739     smUrl: '',
2740     mdUrl: '',
2741     lgUrl: '',
2742
2743     getAutoCreate : function()
2744     {   
2745         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2746             return this.createSingleImg();
2747         }
2748         
2749         var cfg = {
2750             tag: 'div',
2751             cls: 'roo-image-responsive-group',
2752             cn: []
2753         };
2754         var _this = this;
2755         
2756         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2757             
2758             if(!_this[size + 'Url']){
2759                 return;
2760             }
2761             
2762             var img = {
2763                 tag: 'img',
2764                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2765                 html: _this.html || cfg.html,
2766                 src: _this[size + 'Url']
2767             };
2768             
2769             img.cls += ' roo-image-responsive-' + size;
2770             
2771             var s = ['xs', 'sm', 'md', 'lg'];
2772             
2773             s.splice(s.indexOf(size), 1);
2774             
2775             Roo.each(s, function(ss){
2776                 img.cls += ' hidden-' + ss;
2777             });
2778             
2779             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2780                 cfg.cls += ' img-' + _this.border;
2781             }
2782             
2783             if(_this.alt){
2784                 cfg.alt = _this.alt;
2785             }
2786             
2787             if(_this.href){
2788                 var a = {
2789                     tag: 'a',
2790                     href: _this.href,
2791                     cn: [
2792                         img
2793                     ]
2794                 };
2795
2796                 if(this.target){
2797                     a.target = _this.target;
2798                 }
2799             }
2800             
2801             cfg.cn.push((_this.href) ? a : img);
2802             
2803         });
2804         
2805         return cfg;
2806     },
2807     
2808     createSingleImg : function()
2809     {
2810         var cfg = {
2811             tag: 'img',
2812             cls: (this.imgResponsive) ? 'img-responsive' : '',
2813             html : null,
2814             src : 'about:blank'  // just incase src get's set to undefined?!?
2815         };
2816         
2817         cfg.html = this.html || cfg.html;
2818         
2819         cfg.src = this.src || cfg.src;
2820         
2821         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2822             cfg.cls += ' img-' + this.border;
2823         }
2824         
2825         if(this.alt){
2826             cfg.alt = this.alt;
2827         }
2828         
2829         if(this.href){
2830             var a = {
2831                 tag: 'a',
2832                 href: this.href,
2833                 cn: [
2834                     cfg
2835                 ]
2836             };
2837             
2838             if(this.target){
2839                 a.target = this.target;
2840             }
2841             
2842         }
2843         
2844         return (this.href) ? a : cfg;
2845     },
2846     
2847     initEvents: function() 
2848     {
2849         if(!this.href){
2850             this.el.on('click', this.onClick, this);
2851         }
2852         
2853     },
2854     
2855     onClick : function(e)
2856     {
2857         Roo.log('img onclick');
2858         this.fireEvent('click', this, e);
2859     },
2860     /**
2861      * Sets the url of the image - used to update it
2862      * @param {String} url the url of the image
2863      */
2864     
2865     setSrc : function(url)
2866     {
2867         this.src =  url;
2868         
2869         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2870             this.el.dom.src =  url;
2871             return;
2872         }
2873         
2874         this.el.select('img', true).first().dom.src =  url;
2875     }
2876     
2877     
2878    
2879 });
2880
2881  /*
2882  * - LGPL
2883  *
2884  * image
2885  * 
2886  */
2887
2888
2889 /**
2890  * @class Roo.bootstrap.Link
2891  * @extends Roo.bootstrap.Component
2892  * Bootstrap Link Class
2893  * @cfg {String} alt image alternative text
2894  * @cfg {String} href a tag href
2895  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
2896  * @cfg {String} html the content of the link.
2897  * @cfg {String} anchor name for the anchor link
2898  * @cfg {String} fa - favicon
2899
2900  * @cfg {Boolean} preventDefault (true | false) default false
2901
2902  * 
2903  * @constructor
2904  * Create a new Input
2905  * @param {Object} config The config object
2906  */
2907
2908 Roo.bootstrap.Link = function(config){
2909     Roo.bootstrap.Link.superclass.constructor.call(this, config);
2910     
2911     this.addEvents({
2912         // img events
2913         /**
2914          * @event click
2915          * The img click event for the img.
2916          * @param {Roo.EventObject} e
2917          */
2918         "click" : true
2919     });
2920 };
2921
2922 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
2923     
2924     href: false,
2925     target: false,
2926     preventDefault: false,
2927     anchor : false,
2928     alt : false,
2929     fa: false,
2930
2931
2932     getAutoCreate : function()
2933     {
2934         var html = this.html || '';
2935         
2936         if (this.fa !== false) {
2937             html = '<i class="fa fa-' + this.fa + '"></i>';
2938         }
2939         var cfg = {
2940             tag: 'a'
2941         };
2942         // anchor's do not require html/href...
2943         if (this.anchor === false) {
2944             cfg.html = html;
2945             cfg.href = this.href || '#';
2946         } else {
2947             cfg.name = this.anchor;
2948             if (this.html !== false || this.fa !== false) {
2949                 cfg.html = html;
2950             }
2951             if (this.href !== false) {
2952                 cfg.href = this.href;
2953             }
2954         }
2955         
2956         if(this.alt !== false){
2957             cfg.alt = this.alt;
2958         }
2959         
2960         
2961         if(this.target !== false) {
2962             cfg.target = this.target;
2963         }
2964         
2965         return cfg;
2966     },
2967     
2968     initEvents: function() {
2969         
2970         if(!this.href || this.preventDefault){
2971             this.el.on('click', this.onClick, this);
2972         }
2973     },
2974     
2975     onClick : function(e)
2976     {
2977         if(this.preventDefault){
2978             e.preventDefault();
2979         }
2980         //Roo.log('img onclick');
2981         this.fireEvent('click', this, e);
2982     }
2983    
2984 });
2985
2986  /*
2987  * - LGPL
2988  *
2989  * header
2990  * 
2991  */
2992
2993 /**
2994  * @class Roo.bootstrap.Header
2995  * @extends Roo.bootstrap.Component
2996  * Bootstrap Header class
2997  * @cfg {String} html content of header
2998  * @cfg {Number} level (1|2|3|4|5|6) default 1
2999  * 
3000  * @constructor
3001  * Create a new Header
3002  * @param {Object} config The config object
3003  */
3004
3005
3006 Roo.bootstrap.Header  = function(config){
3007     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3008 };
3009
3010 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3011     
3012     //href : false,
3013     html : false,
3014     level : 1,
3015     
3016     
3017     
3018     getAutoCreate : function(){
3019         
3020         
3021         
3022         var cfg = {
3023             tag: 'h' + (1 *this.level),
3024             html: this.html || ''
3025         } ;
3026         
3027         return cfg;
3028     }
3029    
3030 });
3031
3032  
3033
3034  /*
3035  * Based on:
3036  * Ext JS Library 1.1.1
3037  * Copyright(c) 2006-2007, Ext JS, LLC.
3038  *
3039  * Originally Released Under LGPL - original licence link has changed is not relivant.
3040  *
3041  * Fork - LGPL
3042  * <script type="text/javascript">
3043  */
3044  
3045 /**
3046  * @class Roo.bootstrap.MenuMgr
3047  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3048  * @singleton
3049  */
3050 Roo.bootstrap.MenuMgr = function(){
3051    var menus, active, groups = {}, attached = false, lastShow = new Date();
3052
3053    // private - called when first menu is created
3054    function init(){
3055        menus = {};
3056        active = new Roo.util.MixedCollection();
3057        Roo.get(document).addKeyListener(27, function(){
3058            if(active.length > 0){
3059                hideAll();
3060            }
3061        });
3062    }
3063
3064    // private
3065    function hideAll(){
3066        if(active && active.length > 0){
3067            var c = active.clone();
3068            c.each(function(m){
3069                m.hide();
3070            });
3071        }
3072    }
3073
3074    // private
3075    function onHide(m){
3076        active.remove(m);
3077        if(active.length < 1){
3078            Roo.get(document).un("mouseup", onMouseDown);
3079             
3080            attached = false;
3081        }
3082    }
3083
3084    // private
3085    function onShow(m){
3086        var last = active.last();
3087        lastShow = new Date();
3088        active.add(m);
3089        if(!attached){
3090           Roo.get(document).on("mouseup", onMouseDown);
3091            
3092            attached = true;
3093        }
3094        if(m.parentMenu){
3095           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3096           m.parentMenu.activeChild = m;
3097        }else if(last && last.isVisible()){
3098           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3099        }
3100    }
3101
3102    // private
3103    function onBeforeHide(m){
3104        if(m.activeChild){
3105            m.activeChild.hide();
3106        }
3107        if(m.autoHideTimer){
3108            clearTimeout(m.autoHideTimer);
3109            delete m.autoHideTimer;
3110        }
3111    }
3112
3113    // private
3114    function onBeforeShow(m){
3115        var pm = m.parentMenu;
3116        if(!pm && !m.allowOtherMenus){
3117            hideAll();
3118        }else if(pm && pm.activeChild && active != m){
3119            pm.activeChild.hide();
3120        }
3121    }
3122
3123    // private this should really trigger on mouseup..
3124    function onMouseDown(e){
3125         Roo.log("on Mouse Up");
3126         
3127         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3128             Roo.log("MenuManager hideAll");
3129             hideAll();
3130             e.stopEvent();
3131         }
3132         
3133         
3134    }
3135
3136    // private
3137    function onBeforeCheck(mi, state){
3138        if(state){
3139            var g = groups[mi.group];
3140            for(var i = 0, l = g.length; i < l; i++){
3141                if(g[i] != mi){
3142                    g[i].setChecked(false);
3143                }
3144            }
3145        }
3146    }
3147
3148    return {
3149
3150        /**
3151         * Hides all menus that are currently visible
3152         */
3153        hideAll : function(){
3154             hideAll();  
3155        },
3156
3157        // private
3158        register : function(menu){
3159            if(!menus){
3160                init();
3161            }
3162            menus[menu.id] = menu;
3163            menu.on("beforehide", onBeforeHide);
3164            menu.on("hide", onHide);
3165            menu.on("beforeshow", onBeforeShow);
3166            menu.on("show", onShow);
3167            var g = menu.group;
3168            if(g && menu.events["checkchange"]){
3169                if(!groups[g]){
3170                    groups[g] = [];
3171                }
3172                groups[g].push(menu);
3173                menu.on("checkchange", onCheck);
3174            }
3175        },
3176
3177         /**
3178          * Returns a {@link Roo.menu.Menu} object
3179          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3180          * be used to generate and return a new Menu instance.
3181          */
3182        get : function(menu){
3183            if(typeof menu == "string"){ // menu id
3184                return menus[menu];
3185            }else if(menu.events){  // menu instance
3186                return menu;
3187            }
3188            /*else if(typeof menu.length == 'number'){ // array of menu items?
3189                return new Roo.bootstrap.Menu({items:menu});
3190            }else{ // otherwise, must be a config
3191                return new Roo.bootstrap.Menu(menu);
3192            }
3193            */
3194            return false;
3195        },
3196
3197        // private
3198        unregister : function(menu){
3199            delete menus[menu.id];
3200            menu.un("beforehide", onBeforeHide);
3201            menu.un("hide", onHide);
3202            menu.un("beforeshow", onBeforeShow);
3203            menu.un("show", onShow);
3204            var g = menu.group;
3205            if(g && menu.events["checkchange"]){
3206                groups[g].remove(menu);
3207                menu.un("checkchange", onCheck);
3208            }
3209        },
3210
3211        // private
3212        registerCheckable : function(menuItem){
3213            var g = menuItem.group;
3214            if(g){
3215                if(!groups[g]){
3216                    groups[g] = [];
3217                }
3218                groups[g].push(menuItem);
3219                menuItem.on("beforecheckchange", onBeforeCheck);
3220            }
3221        },
3222
3223        // private
3224        unregisterCheckable : function(menuItem){
3225            var g = menuItem.group;
3226            if(g){
3227                groups[g].remove(menuItem);
3228                menuItem.un("beforecheckchange", onBeforeCheck);
3229            }
3230        }
3231    };
3232 }();/*
3233  * - LGPL
3234  *
3235  * menu
3236  * 
3237  */
3238
3239 /**
3240  * @class Roo.bootstrap.Menu
3241  * @extends Roo.bootstrap.Component
3242  * Bootstrap Menu class - container for MenuItems
3243  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3244  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3245  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3246  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3247  * 
3248  * @constructor
3249  * Create a new Menu
3250  * @param {Object} config The config object
3251  */
3252
3253
3254 Roo.bootstrap.Menu = function(config){
3255     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3256     if (this.registerMenu && this.type != 'treeview')  {
3257         Roo.bootstrap.MenuMgr.register(this);
3258     }
3259     
3260     
3261     this.addEvents({
3262         /**
3263          * @event beforeshow
3264          * Fires before this menu is displayed (return false to block)
3265          * @param {Roo.menu.Menu} this
3266          */
3267         beforeshow : true,
3268         /**
3269          * @event beforehide
3270          * Fires before this menu is hidden (return false to block)
3271          * @param {Roo.menu.Menu} this
3272          */
3273         beforehide : true,
3274         /**
3275          * @event show
3276          * Fires after this menu is displayed
3277          * @param {Roo.menu.Menu} this
3278          */
3279         show : true,
3280         /**
3281          * @event hide
3282          * Fires after this menu is hidden
3283          * @param {Roo.menu.Menu} this
3284          */
3285         hide : true,
3286         /**
3287          * @event click
3288          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3289          * @param {Roo.menu.Menu} this
3290          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3291          * @param {Roo.EventObject} e
3292          */
3293         click : true,
3294         /**
3295          * @event mouseover
3296          * Fires when the mouse is hovering over this menu
3297          * @param {Roo.menu.Menu} this
3298          * @param {Roo.EventObject} e
3299          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3300          */
3301         mouseover : true,
3302         /**
3303          * @event mouseout
3304          * Fires when the mouse exits this menu
3305          * @param {Roo.menu.Menu} this
3306          * @param {Roo.EventObject} e
3307          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3308          */
3309         mouseout : true,
3310         /**
3311          * @event itemclick
3312          * Fires when a menu item contained in this menu is clicked
3313          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3314          * @param {Roo.EventObject} e
3315          */
3316         itemclick: true
3317     });
3318     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3319 };
3320
3321 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3322     
3323    /// html : false,
3324     //align : '',
3325     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3326     type: false,
3327     /**
3328      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3329      */
3330     registerMenu : true,
3331     
3332     menuItems :false, // stores the menu items..
3333     
3334     hidden:true,
3335         
3336     parentMenu : false,
3337     
3338     stopEvent : true,
3339     
3340     isLink : false,
3341     
3342     getChildContainer : function() {
3343         return this.el;  
3344     },
3345     
3346     getAutoCreate : function(){
3347          
3348         //if (['right'].indexOf(this.align)!==-1) {
3349         //    cfg.cn[1].cls += ' pull-right'
3350         //}
3351         
3352         
3353         var cfg = {
3354             tag : 'ul',
3355             cls : 'dropdown-menu' ,
3356             style : 'z-index:1000'
3357             
3358         };
3359         
3360         if (this.type === 'submenu') {
3361             cfg.cls = 'submenu active';
3362         }
3363         if (this.type === 'treeview') {
3364             cfg.cls = 'treeview-menu';
3365         }
3366         
3367         return cfg;
3368     },
3369     initEvents : function() {
3370         
3371        // Roo.log("ADD event");
3372        // Roo.log(this.triggerEl.dom);
3373         
3374         this.triggerEl.on('click', this.onTriggerClick, this);
3375         
3376         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3377         
3378         
3379         if (this.triggerEl.hasClass('nav-item')) {
3380             // dropdown toggle on the 'a' in BS4?
3381             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3382         } else {
3383             this.triggerEl.addClass('dropdown-toggle');
3384         }
3385         if (Roo.isTouch) {
3386             this.el.on('touchstart'  , this.onTouch, this);
3387         }
3388         this.el.on('click' , this.onClick, this);
3389
3390         this.el.on("mouseover", this.onMouseOver, this);
3391         this.el.on("mouseout", this.onMouseOut, this);
3392         
3393     },
3394     
3395     findTargetItem : function(e)
3396     {
3397         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3398         if(!t){
3399             return false;
3400         }
3401         //Roo.log(t);         Roo.log(t.id);
3402         if(t && t.id){
3403             //Roo.log(this.menuitems);
3404             return this.menuitems.get(t.id);
3405             
3406             //return this.items.get(t.menuItemId);
3407         }
3408         
3409         return false;
3410     },
3411     
3412     onTouch : function(e) 
3413     {
3414         Roo.log("menu.onTouch");
3415         //e.stopEvent(); this make the user popdown broken
3416         this.onClick(e);
3417     },
3418     
3419     onClick : function(e)
3420     {
3421         Roo.log("menu.onClick");
3422         
3423         var t = this.findTargetItem(e);
3424         if(!t || t.isContainer){
3425             return;
3426         }
3427         Roo.log(e);
3428         /*
3429         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3430             if(t == this.activeItem && t.shouldDeactivate(e)){
3431                 this.activeItem.deactivate();
3432                 delete this.activeItem;
3433                 return;
3434             }
3435             if(t.canActivate){
3436                 this.setActiveItem(t, true);
3437             }
3438             return;
3439             
3440             
3441         }
3442         */
3443        
3444         Roo.log('pass click event');
3445         
3446         t.onClick(e);
3447         
3448         this.fireEvent("click", this, t, e);
3449         
3450         var _this = this;
3451         
3452         if(!t.href.length || t.href == '#'){
3453             (function() { _this.hide(); }).defer(100);
3454         }
3455         
3456     },
3457     
3458     onMouseOver : function(e){
3459         var t  = this.findTargetItem(e);
3460         //Roo.log(t);
3461         //if(t){
3462         //    if(t.canActivate && !t.disabled){
3463         //        this.setActiveItem(t, true);
3464         //    }
3465         //}
3466         
3467         this.fireEvent("mouseover", this, e, t);
3468     },
3469     isVisible : function(){
3470         return !this.hidden;
3471     },
3472     onMouseOut : function(e){
3473         var t  = this.findTargetItem(e);
3474         
3475         //if(t ){
3476         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3477         //        this.activeItem.deactivate();
3478         //        delete this.activeItem;
3479         //    }
3480         //}
3481         this.fireEvent("mouseout", this, e, t);
3482     },
3483     
3484     
3485     /**
3486      * Displays this menu relative to another element
3487      * @param {String/HTMLElement/Roo.Element} element The element to align to
3488      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3489      * the element (defaults to this.defaultAlign)
3490      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3491      */
3492     show : function(el, pos, parentMenu)
3493     {
3494         if (false === this.fireEvent("beforeshow", this)) {
3495             Roo.log("show canceled");
3496             return;
3497         }
3498         this.parentMenu = parentMenu;
3499         if(!this.el){
3500             this.render();
3501         }
3502         
3503         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3504     },
3505      /**
3506      * Displays this menu at a specific xy position
3507      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3508      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3509      */
3510     showAt : function(xy, parentMenu, /* private: */_e){
3511         this.parentMenu = parentMenu;
3512         if(!this.el){
3513             this.render();
3514         }
3515         if(_e !== false){
3516             this.fireEvent("beforeshow", this);
3517             //xy = this.el.adjustForConstraints(xy);
3518         }
3519         
3520         //this.el.show();
3521         this.hideMenuItems();
3522         this.hidden = false;
3523         this.triggerEl.addClass('open');
3524         this.el.addClass('show');
3525         
3526         // reassign x when hitting right
3527         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3528             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3529         }
3530         
3531         // reassign y when hitting bottom
3532         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3533             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3534         }
3535         
3536         // but the list may align on trigger left or trigger top... should it be a properity?
3537         
3538         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3539             this.el.setXY(xy);
3540         }
3541         
3542         this.focus();
3543         this.fireEvent("show", this);
3544     },
3545     
3546     focus : function(){
3547         return;
3548         if(!this.hidden){
3549             this.doFocus.defer(50, this);
3550         }
3551     },
3552
3553     doFocus : function(){
3554         if(!this.hidden){
3555             this.focusEl.focus();
3556         }
3557     },
3558
3559     /**
3560      * Hides this menu and optionally all parent menus
3561      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3562      */
3563     hide : function(deep)
3564     {
3565         if (false === this.fireEvent("beforehide", this)) {
3566             Roo.log("hide canceled");
3567             return;
3568         }
3569         this.hideMenuItems();
3570         if(this.el && this.isVisible()){
3571            
3572             if(this.activeItem){
3573                 this.activeItem.deactivate();
3574                 this.activeItem = null;
3575             }
3576             this.triggerEl.removeClass('open');;
3577             this.el.removeClass('show');
3578             this.hidden = true;
3579             this.fireEvent("hide", this);
3580         }
3581         if(deep === true && this.parentMenu){
3582             this.parentMenu.hide(true);
3583         }
3584     },
3585     
3586     onTriggerClick : function(e)
3587     {
3588         Roo.log('trigger click');
3589         
3590         var target = e.getTarget();
3591         
3592         Roo.log(target.nodeName.toLowerCase());
3593         
3594         if(target.nodeName.toLowerCase() === 'i'){
3595             e.preventDefault();
3596         }
3597         
3598     },
3599     
3600     onTriggerPress  : function(e)
3601     {
3602         Roo.log('trigger press');
3603         //Roo.log(e.getTarget());
3604        // Roo.log(this.triggerEl.dom);
3605        
3606         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3607         var pel = Roo.get(e.getTarget());
3608         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3609             Roo.log('is treeview or dropdown?');
3610             return;
3611         }
3612         
3613         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3614             return;
3615         }
3616         
3617         if (this.isVisible()) {
3618             Roo.log('hide');
3619             this.hide();
3620         } else {
3621             Roo.log('show');
3622             this.show(this.triggerEl, '?', false);
3623         }
3624         
3625         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3626             e.stopEvent();
3627         }
3628         
3629     },
3630        
3631     
3632     hideMenuItems : function()
3633     {
3634         Roo.log("hide Menu Items");
3635         if (!this.el) { 
3636             return;
3637         }
3638         
3639         this.el.select('.open',true).each(function(aa) {
3640             
3641             aa.removeClass('open');
3642          
3643         });
3644     },
3645     addxtypeChild : function (tree, cntr) {
3646         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3647           
3648         this.menuitems.add(comp);
3649         return comp;
3650
3651     },
3652     getEl : function()
3653     {
3654         Roo.log(this.el);
3655         return this.el;
3656     },
3657     
3658     clear : function()
3659     {
3660         this.getEl().dom.innerHTML = '';
3661         this.menuitems.clear();
3662     }
3663 });
3664
3665  
3666  /*
3667  * - LGPL
3668  *
3669  * menu item
3670  * 
3671  */
3672
3673
3674 /**
3675  * @class Roo.bootstrap.MenuItem
3676  * @extends Roo.bootstrap.Component
3677  * Bootstrap MenuItem class
3678  * @cfg {String} html the menu label
3679  * @cfg {String} href the link
3680  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3681  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3682  * @cfg {Boolean} active  used on sidebars to highlight active itesm
3683  * @cfg {String} fa favicon to show on left of menu item.
3684  * @cfg {Roo.bootsrap.Menu} menu the child menu.
3685  * 
3686  * 
3687  * @constructor
3688  * Create a new MenuItem
3689  * @param {Object} config The config object
3690  */
3691
3692
3693 Roo.bootstrap.MenuItem = function(config){
3694     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3695     this.addEvents({
3696         // raw events
3697         /**
3698          * @event click
3699          * The raw click event for the entire grid.
3700          * @param {Roo.bootstrap.MenuItem} this
3701          * @param {Roo.EventObject} e
3702          */
3703         "click" : true
3704     });
3705 };
3706
3707 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
3708     
3709     href : false,
3710     html : false,
3711     preventDefault: false,
3712     isContainer : false,
3713     active : false,
3714     fa: false,
3715     
3716     getAutoCreate : function(){
3717         
3718         if(this.isContainer){
3719             return {
3720                 tag: 'li',
3721                 cls: 'dropdown-menu-item '
3722             };
3723         }
3724         var ctag = {
3725             tag: 'span',
3726             html: 'Link'
3727         };
3728         
3729         var anc = {
3730             tag : 'a',
3731             cls : 'dropdown-item',
3732             href : '#',
3733             cn : [  ]
3734         };
3735         
3736         if (this.fa !== false) {
3737             anc.cn.push({
3738                 tag : 'i',
3739                 cls : 'fa fa-' + this.fa
3740             });
3741         }
3742         
3743         anc.cn.push(ctag);
3744         
3745         
3746         var cfg= {
3747             tag: 'li',
3748             cls: 'dropdown-menu-item',
3749             cn: [ anc ]
3750         };
3751         if (this.parent().type == 'treeview') {
3752             cfg.cls = 'treeview-menu';
3753         }
3754         if (this.active) {
3755             cfg.cls += ' active';
3756         }
3757         
3758         
3759         
3760         anc.href = this.href || cfg.cn[0].href ;
3761         ctag.html = this.html || cfg.cn[0].html ;
3762         return cfg;
3763     },
3764     
3765     initEvents: function()
3766     {
3767         if (this.parent().type == 'treeview') {
3768             this.el.select('a').on('click', this.onClick, this);
3769         }
3770         
3771         if (this.menu) {
3772             this.menu.parentType = this.xtype;
3773             this.menu.triggerEl = this.el;
3774             this.menu = this.addxtype(Roo.apply({}, this.menu));
3775         }
3776         
3777     },
3778     onClick : function(e)
3779     {
3780         Roo.log('item on click ');
3781         
3782         if(this.preventDefault){
3783             e.preventDefault();
3784         }
3785         //this.parent().hideMenuItems();
3786         
3787         this.fireEvent('click', this, e);
3788     },
3789     getEl : function()
3790     {
3791         return this.el;
3792     } 
3793 });
3794
3795  
3796
3797  /*
3798  * - LGPL
3799  *
3800  * menu separator
3801  * 
3802  */
3803
3804
3805 /**
3806  * @class Roo.bootstrap.MenuSeparator
3807  * @extends Roo.bootstrap.Component
3808  * Bootstrap MenuSeparator class
3809  * 
3810  * @constructor
3811  * Create a new MenuItem
3812  * @param {Object} config The config object
3813  */
3814
3815
3816 Roo.bootstrap.MenuSeparator = function(config){
3817     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3818 };
3819
3820 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
3821     
3822     getAutoCreate : function(){
3823         var cfg = {
3824             cls: 'divider',
3825             tag : 'li'
3826         };
3827         
3828         return cfg;
3829     }
3830    
3831 });
3832
3833  
3834
3835  
3836 /*
3837 * Licence: LGPL
3838 */
3839
3840 /**
3841  * @class Roo.bootstrap.Modal
3842  * @extends Roo.bootstrap.Component
3843  * Bootstrap Modal class
3844  * @cfg {String} title Title of dialog
3845  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3846  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
3847  * @cfg {Boolean} specificTitle default false
3848  * @cfg {Array} buttons Array of buttons or standard button set..
3849  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3850  * @cfg {Boolean} animate default true
3851  * @cfg {Boolean} allow_close default true
3852  * @cfg {Boolean} fitwindow default false
3853  * @cfg {Number} width fixed width - usefull for chrome extension only really.
3854  * @cfg {Number} height fixed height - usefull for chrome extension only really.
3855  * @cfg {String} size (sm|lg|xl) default empty
3856  * @cfg {Number} max_width set the max width of modal
3857  * @cfg {Boolean} editableTitle can the title be edited
3858
3859  *
3860  *
3861  * @constructor
3862  * Create a new Modal Dialog
3863  * @param {Object} config The config object
3864  */
3865
3866 Roo.bootstrap.Modal = function(config){
3867     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3868     this.addEvents({
3869         // raw events
3870         /**
3871          * @event btnclick
3872          * The raw btnclick event for the button
3873          * @param {Roo.EventObject} e
3874          */
3875         "btnclick" : true,
3876         /**
3877          * @event resize
3878          * Fire when dialog resize
3879          * @param {Roo.bootstrap.Modal} this
3880          * @param {Roo.EventObject} e
3881          */
3882         "resize" : true,
3883         /**
3884          * @event titlechanged
3885          * Fire when the editable title has been changed
3886          * @param {Roo.bootstrap.Modal} this
3887          * @param {Roo.EventObject} value
3888          */
3889         "titlechanged" : true 
3890         
3891     });
3892     this.buttons = this.buttons || [];
3893
3894     if (this.tmpl) {
3895         this.tmpl = Roo.factory(this.tmpl);
3896     }
3897
3898 };
3899
3900 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
3901
3902     title : 'test dialog',
3903
3904     buttons : false,
3905
3906     // set on load...
3907
3908     html: false,
3909
3910     tmp: false,
3911
3912     specificTitle: false,
3913
3914     buttonPosition: 'right',
3915
3916     allow_close : true,
3917
3918     animate : true,
3919
3920     fitwindow: false,
3921     
3922      // private
3923     dialogEl: false,
3924     bodyEl:  false,
3925     footerEl:  false,
3926     titleEl:  false,
3927     closeEl:  false,
3928
3929     size: '',
3930     
3931     max_width: 0,
3932     
3933     max_height: 0,
3934     
3935     fit_content: false,
3936     editableTitle  : false,
3937
3938     onRender : function(ct, position)
3939     {
3940         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
3941
3942         if(!this.el){
3943             var cfg = Roo.apply({},  this.getAutoCreate());
3944             cfg.id = Roo.id();
3945             //if(!cfg.name){
3946             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
3947             //}
3948             //if (!cfg.name.length) {
3949             //    delete cfg.name;
3950            // }
3951             if (this.cls) {
3952                 cfg.cls += ' ' + this.cls;
3953             }
3954             if (this.style) {
3955                 cfg.style = this.style;
3956             }
3957             this.el = Roo.get(document.body).createChild(cfg, position);
3958         }
3959         //var type = this.el.dom.type;
3960
3961
3962         if(this.tabIndex !== undefined){
3963             this.el.dom.setAttribute('tabIndex', this.tabIndex);
3964         }
3965
3966         this.dialogEl = this.el.select('.modal-dialog',true).first();
3967         this.bodyEl = this.el.select('.modal-body',true).first();
3968         this.closeEl = this.el.select('.modal-header .close', true).first();
3969         this.headerEl = this.el.select('.modal-header',true).first();
3970         this.titleEl = this.el.select('.modal-title',true).first();
3971         this.footerEl = this.el.select('.modal-footer',true).first();
3972
3973         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
3974         
3975         //this.el.addClass("x-dlg-modal");
3976
3977         if (this.buttons.length) {
3978             Roo.each(this.buttons, function(bb) {
3979                 var b = Roo.apply({}, bb);
3980                 b.xns = b.xns || Roo.bootstrap;
3981                 b.xtype = b.xtype || 'Button';
3982                 if (typeof(b.listeners) == 'undefined') {
3983                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
3984                 }
3985
3986                 var btn = Roo.factory(b);
3987
3988                 btn.render(this.getButtonContainer());
3989
3990             },this);
3991         }
3992         // render the children.
3993         var nitems = [];
3994
3995         if(typeof(this.items) != 'undefined'){
3996             var items = this.items;
3997             delete this.items;
3998
3999             for(var i =0;i < items.length;i++) {
4000                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4001             }
4002         }
4003
4004         this.items = nitems;
4005
4006         // where are these used - they used to be body/close/footer
4007
4008
4009         this.initEvents();
4010         //this.el.addClass([this.fieldClass, this.cls]);
4011
4012     },
4013
4014     getAutoCreate : function()
4015     {
4016         // we will default to modal-body-overflow - might need to remove or make optional later.
4017         var bdy = {
4018                 cls : 'modal-body enable-modal-body-overflow ', 
4019                 html : this.html || ''
4020         };
4021
4022         var title = {
4023             tag: 'h4',
4024             cls : 'modal-title',
4025             html : this.title
4026         };
4027
4028         if(this.specificTitle){ // WTF is this?
4029             title = this.title;
4030         }
4031
4032         var header = [];
4033         if (this.allow_close && Roo.bootstrap.version == 3) {
4034             header.push({
4035                 tag: 'button',
4036                 cls : 'close',
4037                 html : '&times'
4038             });
4039         }
4040
4041         header.push(title);
4042
4043         if (this.editableTitle) {
4044             header.push({
4045                 cls: 'form-control roo-editable-title d-none',
4046                 tag: 'input',
4047                 type: 'text'
4048             });
4049         }
4050         
4051         if (this.allow_close && Roo.bootstrap.version == 4) {
4052             header.push({
4053                 tag: 'button',
4054                 cls : 'close',
4055                 html : '&times'
4056             });
4057         }
4058         
4059         var size = '';
4060
4061         if(this.size.length){
4062             size = 'modal-' + this.size;
4063         }
4064         
4065         var footer = Roo.bootstrap.version == 3 ?
4066             {
4067                 cls : 'modal-footer',
4068                 cn : [
4069                     {
4070                         tag: 'div',
4071                         cls: 'btn-' + this.buttonPosition
4072                     }
4073                 ]
4074
4075             } :
4076             {  // BS4 uses mr-auto on left buttons....
4077                 cls : 'modal-footer'
4078             };
4079
4080             
4081
4082         
4083         
4084         var modal = {
4085             cls: "modal",
4086              cn : [
4087                 {
4088                     cls: "modal-dialog " + size,
4089                     cn : [
4090                         {
4091                             cls : "modal-content",
4092                             cn : [
4093                                 {
4094                                     cls : 'modal-header',
4095                                     cn : header
4096                                 },
4097                                 bdy,
4098                                 footer
4099                             ]
4100
4101                         }
4102                     ]
4103
4104                 }
4105             ]
4106         };
4107
4108         if(this.animate){
4109             modal.cls += ' fade';
4110         }
4111
4112         return modal;
4113
4114     },
4115     getChildContainer : function() {
4116
4117          return this.bodyEl;
4118
4119     },
4120     getButtonContainer : function() {
4121         
4122          return Roo.bootstrap.version == 4 ?
4123             this.el.select('.modal-footer',true).first()
4124             : this.el.select('.modal-footer div',true).first();
4125
4126     },
4127     initEvents : function()
4128     {
4129         if (this.allow_close) {
4130             this.closeEl.on('click', this.hide, this);
4131         }
4132         Roo.EventManager.onWindowResize(this.resize, this, true);
4133         if (this.editableTitle) {
4134             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4135             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4136             this.headerEditEl.on('keyup', function(e) {
4137                     if(e.isNavKeyPress()){
4138                             this.toggleHeaderInput(false)
4139                     }
4140                 }, this);
4141             this.headerEditEl.on('blur', function(e) {
4142                 this.toggleHeaderInput(false)
4143             },this);
4144         }
4145
4146     },
4147   
4148
4149     resize : function()
4150     {
4151         this.maskEl.setSize(
4152             Roo.lib.Dom.getViewWidth(true),
4153             Roo.lib.Dom.getViewHeight(true)
4154         );
4155         
4156         if (this.fitwindow) {
4157             
4158            
4159             this.setSize(
4160                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4161                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4162             );
4163             return;
4164         }
4165         
4166         if(this.max_width !== 0) {
4167             
4168             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4169             
4170             if(this.height) {
4171                 this.setSize(w, this.height);
4172                 return;
4173             }
4174             
4175             if(this.max_height) {
4176                 this.setSize(w,Math.min(
4177                     this.max_height,
4178                     Roo.lib.Dom.getViewportHeight(true) - 60
4179                 ));
4180                 
4181                 return;
4182             }
4183             
4184             if(!this.fit_content) {
4185                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4186                 return;
4187             }
4188             
4189             this.setSize(w, Math.min(
4190                 60 +
4191                 this.headerEl.getHeight() + 
4192                 this.footerEl.getHeight() + 
4193                 this.getChildHeight(this.bodyEl.dom.childNodes),
4194                 Roo.lib.Dom.getViewportHeight(true) - 60)
4195             );
4196         }
4197         
4198     },
4199
4200     setSize : function(w,h)
4201     {
4202         if (!w && !h) {
4203             return;
4204         }
4205         
4206         this.resizeTo(w,h);
4207     },
4208
4209     show : function() {
4210
4211         if (!this.rendered) {
4212             this.render();
4213         }
4214
4215         //this.el.setStyle('display', 'block');
4216         this.el.removeClass('hideing');
4217         this.el.dom.style.display='block';
4218         
4219         Roo.get(document.body).addClass('modal-open');
4220  
4221         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4222             
4223             (function(){
4224                 this.el.addClass('show');
4225                 this.el.addClass('in');
4226             }).defer(50, this);
4227         }else{
4228             this.el.addClass('show');
4229             this.el.addClass('in');
4230         }
4231
4232         // not sure how we can show data in here..
4233         //if (this.tmpl) {
4234         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4235         //}
4236
4237         Roo.get(document.body).addClass("x-body-masked");
4238         
4239         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4240         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4241         this.maskEl.dom.style.display = 'block';
4242         this.maskEl.addClass('show');
4243         
4244         
4245         this.resize();
4246         
4247         this.fireEvent('show', this);
4248
4249         // set zindex here - otherwise it appears to be ignored...
4250         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4251
4252         (function () {
4253             this.items.forEach( function(e) {
4254                 e.layout ? e.layout() : false;
4255
4256             });
4257         }).defer(100,this);
4258
4259     },
4260     hide : function()
4261     {
4262         if(this.fireEvent("beforehide", this) !== false){
4263             
4264             this.maskEl.removeClass('show');
4265             
4266             this.maskEl.dom.style.display = '';
4267             Roo.get(document.body).removeClass("x-body-masked");
4268             this.el.removeClass('in');
4269             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4270
4271             if(this.animate){ // why
4272                 this.el.addClass('hideing');
4273                 this.el.removeClass('show');
4274                 (function(){
4275                     if (!this.el.hasClass('hideing')) {
4276                         return; // it's been shown again...
4277                     }
4278                     
4279                     this.el.dom.style.display='';
4280
4281                     Roo.get(document.body).removeClass('modal-open');
4282                     this.el.removeClass('hideing');
4283                 }).defer(150,this);
4284                 
4285             }else{
4286                 this.el.removeClass('show');
4287                 this.el.dom.style.display='';
4288                 Roo.get(document.body).removeClass('modal-open');
4289
4290             }
4291             this.fireEvent('hide', this);
4292         }
4293     },
4294     isVisible : function()
4295     {
4296         
4297         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4298         
4299     },
4300
4301     addButton : function(str, cb)
4302     {
4303
4304
4305         var b = Roo.apply({}, { html : str } );
4306         b.xns = b.xns || Roo.bootstrap;
4307         b.xtype = b.xtype || 'Button';
4308         if (typeof(b.listeners) == 'undefined') {
4309             b.listeners = { click : cb.createDelegate(this)  };
4310         }
4311
4312         var btn = Roo.factory(b);
4313
4314         btn.render(this.getButtonContainer());
4315
4316         return btn;
4317
4318     },
4319
4320     setDefaultButton : function(btn)
4321     {
4322         //this.el.select('.modal-footer').()
4323     },
4324
4325     resizeTo: function(w,h)
4326     {
4327         this.dialogEl.setWidth(w);
4328         
4329         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4330
4331         this.bodyEl.setHeight(h - diff);
4332         
4333         this.fireEvent('resize', this);
4334     },
4335     
4336     setContentSize  : function(w, h)
4337     {
4338
4339     },
4340     onButtonClick: function(btn,e)
4341     {
4342         //Roo.log([a,b,c]);
4343         this.fireEvent('btnclick', btn.name, e);
4344     },
4345      /**
4346      * Set the title of the Dialog
4347      * @param {String} str new Title
4348      */
4349     setTitle: function(str) {
4350         this.titleEl.dom.innerHTML = str;
4351         this.title = str;
4352     },
4353     /**
4354      * Set the body of the Dialog
4355      * @param {String} str new Title
4356      */
4357     setBody: function(str) {
4358         this.bodyEl.dom.innerHTML = str;
4359     },
4360     /**
4361      * Set the body of the Dialog using the template
4362      * @param {Obj} data - apply this data to the template and replace the body contents.
4363      */
4364     applyBody: function(obj)
4365     {
4366         if (!this.tmpl) {
4367             Roo.log("Error - using apply Body without a template");
4368             //code
4369         }
4370         this.tmpl.overwrite(this.bodyEl, obj);
4371     },
4372     
4373     getChildHeight : function(child_nodes)
4374     {
4375         if(
4376             !child_nodes ||
4377             child_nodes.length == 0
4378         ) {
4379             return 0;
4380         }
4381         
4382         var child_height = 0;
4383         
4384         for(var i = 0; i < child_nodes.length; i++) {
4385             
4386             /*
4387             * for modal with tabs...
4388             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4389                 
4390                 var layout_childs = child_nodes[i].childNodes;
4391                 
4392                 for(var j = 0; j < layout_childs.length; j++) {
4393                     
4394                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4395                         
4396                         var layout_body_childs = layout_childs[j].childNodes;
4397                         
4398                         for(var k = 0; k < layout_body_childs.length; k++) {
4399                             
4400                             if(layout_body_childs[k].classList.contains('navbar')) {
4401                                 child_height += layout_body_childs[k].offsetHeight;
4402                                 continue;
4403                             }
4404                             
4405                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4406                                 
4407                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4408                                 
4409                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4410                                     
4411                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4412                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4413                                         continue;
4414                                     }
4415                                     
4416                                 }
4417                                 
4418                             }
4419                             
4420                         }
4421                     }
4422                 }
4423                 continue;
4424             }
4425             */
4426             
4427             child_height += child_nodes[i].offsetHeight;
4428             // Roo.log(child_nodes[i].offsetHeight);
4429         }
4430         
4431         return child_height;
4432     },
4433     toggleHeaderInput : function(is_edit)
4434     {
4435         
4436         if (is_edit && this.is_header_editing) {
4437             return; // already editing..
4438         }
4439         if (is_edit) {
4440     
4441             this.headerEditEl.dom.value = this.title;
4442             this.headerEditEl.removeClass('d-none');
4443             this.headerEditEl.dom.focus();
4444             this.titleEl.addClass('d-none');
4445             
4446             this.is_header_editing = true;
4447             return
4448         }
4449         // flip back to not editing.
4450         this.title = this.headerEditEl.dom.value;
4451         this.headerEditEl.addClass('d-none');
4452         this.titleEl.removeClass('d-none');
4453         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4454         this.is_header_editing = false;
4455         this.fireEvent('titlechanged', this, this.title);
4456     
4457             
4458         
4459     }
4460
4461 });
4462
4463
4464 Roo.apply(Roo.bootstrap.Modal,  {
4465     /**
4466          * Button config that displays a single OK button
4467          * @type Object
4468          */
4469         OK :  [{
4470             name : 'ok',
4471             weight : 'primary',
4472             html : 'OK'
4473         }],
4474         /**
4475          * Button config that displays Yes and No buttons
4476          * @type Object
4477          */
4478         YESNO : [
4479             {
4480                 name  : 'no',
4481                 html : 'No'
4482             },
4483             {
4484                 name  :'yes',
4485                 weight : 'primary',
4486                 html : 'Yes'
4487             }
4488         ],
4489
4490         /**
4491          * Button config that displays OK and Cancel buttons
4492          * @type Object
4493          */
4494         OKCANCEL : [
4495             {
4496                name : 'cancel',
4497                 html : 'Cancel'
4498             },
4499             {
4500                 name : 'ok',
4501                 weight : 'primary',
4502                 html : 'OK'
4503             }
4504         ],
4505         /**
4506          * Button config that displays Yes, No and Cancel buttons
4507          * @type Object
4508          */
4509         YESNOCANCEL : [
4510             {
4511                 name : 'yes',
4512                 weight : 'primary',
4513                 html : 'Yes'
4514             },
4515             {
4516                 name : 'no',
4517                 html : 'No'
4518             },
4519             {
4520                 name : 'cancel',
4521                 html : 'Cancel'
4522             }
4523         ],
4524         
4525         zIndex : 10001
4526 });
4527
4528 /*
4529  * - LGPL
4530  *
4531  * messagebox - can be used as a replace
4532  * 
4533  */
4534 /**
4535  * @class Roo.MessageBox
4536  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4537  * Example usage:
4538  *<pre><code>
4539 // Basic alert:
4540 Roo.Msg.alert('Status', 'Changes saved successfully.');
4541
4542 // Prompt for user data:
4543 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4544     if (btn == 'ok'){
4545         // process text value...
4546     }
4547 });
4548
4549 // Show a dialog using config options:
4550 Roo.Msg.show({
4551    title:'Save Changes?',
4552    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4553    buttons: Roo.Msg.YESNOCANCEL,
4554    fn: processResult,
4555    animEl: 'elId'
4556 });
4557 </code></pre>
4558  * @singleton
4559  */
4560 Roo.bootstrap.MessageBox = function(){
4561     var dlg, opt, mask, waitTimer;
4562     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4563     var buttons, activeTextEl, bwidth;
4564
4565     
4566     // private
4567     var handleButton = function(button){
4568         dlg.hide();
4569         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4570     };
4571
4572     // private
4573     var handleHide = function(){
4574         if(opt && opt.cls){
4575             dlg.el.removeClass(opt.cls);
4576         }
4577         //if(waitTimer){
4578         //    Roo.TaskMgr.stop(waitTimer);
4579         //    waitTimer = null;
4580         //}
4581     };
4582
4583     // private
4584     var updateButtons = function(b){
4585         var width = 0;
4586         if(!b){
4587             buttons["ok"].hide();
4588             buttons["cancel"].hide();
4589             buttons["yes"].hide();
4590             buttons["no"].hide();
4591             dlg.footerEl.hide();
4592             
4593             return width;
4594         }
4595         dlg.footerEl.show();
4596         for(var k in buttons){
4597             if(typeof buttons[k] != "function"){
4598                 if(b[k]){
4599                     buttons[k].show();
4600                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4601                     width += buttons[k].el.getWidth()+15;
4602                 }else{
4603                     buttons[k].hide();
4604                 }
4605             }
4606         }
4607         return width;
4608     };
4609
4610     // private
4611     var handleEsc = function(d, k, e){
4612         if(opt && opt.closable !== false){
4613             dlg.hide();
4614         }
4615         if(e){
4616             e.stopEvent();
4617         }
4618     };
4619
4620     return {
4621         /**
4622          * Returns a reference to the underlying {@link Roo.BasicDialog} element
4623          * @return {Roo.BasicDialog} The BasicDialog element
4624          */
4625         getDialog : function(){
4626            if(!dlg){
4627                 dlg = new Roo.bootstrap.Modal( {
4628                     //draggable: true,
4629                     //resizable:false,
4630                     //constraintoviewport:false,
4631                     //fixedcenter:true,
4632                     //collapsible : false,
4633                     //shim:true,
4634                     //modal: true,
4635                 //    width: 'auto',
4636                   //  height:100,
4637                     //buttonAlign:"center",
4638                     closeClick : function(){
4639                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4640                             handleButton("no");
4641                         }else{
4642                             handleButton("cancel");
4643                         }
4644                     }
4645                 });
4646                 dlg.render();
4647                 dlg.on("hide", handleHide);
4648                 mask = dlg.mask;
4649                 //dlg.addKeyListener(27, handleEsc);
4650                 buttons = {};
4651                 this.buttons = buttons;
4652                 var bt = this.buttonText;
4653                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4654                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4655                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4656                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4657                 //Roo.log(buttons);
4658                 bodyEl = dlg.bodyEl.createChild({
4659
4660                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4661                         '<textarea class="roo-mb-textarea"></textarea>' +
4662                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
4663                 });
4664                 msgEl = bodyEl.dom.firstChild;
4665                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4666                 textboxEl.enableDisplayMode();
4667                 textboxEl.addKeyListener([10,13], function(){
4668                     if(dlg.isVisible() && opt && opt.buttons){
4669                         if(opt.buttons.ok){
4670                             handleButton("ok");
4671                         }else if(opt.buttons.yes){
4672                             handleButton("yes");
4673                         }
4674                     }
4675                 });
4676                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4677                 textareaEl.enableDisplayMode();
4678                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4679                 progressEl.enableDisplayMode();
4680                 
4681                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4682                 var pf = progressEl.dom.firstChild;
4683                 if (pf) {
4684                     pp = Roo.get(pf.firstChild);
4685                     pp.setHeight(pf.offsetHeight);
4686                 }
4687                 
4688             }
4689             return dlg;
4690         },
4691
4692         /**
4693          * Updates the message box body text
4694          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4695          * the XHTML-compliant non-breaking space character '&amp;#160;')
4696          * @return {Roo.MessageBox} This message box
4697          */
4698         updateText : function(text)
4699         {
4700             if(!dlg.isVisible() && !opt.width){
4701                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4702                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4703             }
4704             msgEl.innerHTML = text || '&#160;';
4705       
4706             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4707             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4708             var w = Math.max(
4709                     Math.min(opt.width || cw , this.maxWidth), 
4710                     Math.max(opt.minWidth || this.minWidth, bwidth)
4711             );
4712             if(opt.prompt){
4713                 activeTextEl.setWidth(w);
4714             }
4715             if(dlg.isVisible()){
4716                 dlg.fixedcenter = false;
4717             }
4718             // to big, make it scroll. = But as usual stupid IE does not support
4719             // !important..
4720             
4721             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4722                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4723                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4724             } else {
4725                 bodyEl.dom.style.height = '';
4726                 bodyEl.dom.style.overflowY = '';
4727             }
4728             if (cw > w) {
4729                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4730             } else {
4731                 bodyEl.dom.style.overflowX = '';
4732             }
4733             
4734             dlg.setContentSize(w, bodyEl.getHeight());
4735             if(dlg.isVisible()){
4736                 dlg.fixedcenter = true;
4737             }
4738             return this;
4739         },
4740
4741         /**
4742          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
4743          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4744          * @param {Number} value Any number between 0 and 1 (e.g., .5)
4745          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4746          * @return {Roo.MessageBox} This message box
4747          */
4748         updateProgress : function(value, text){
4749             if(text){
4750                 this.updateText(text);
4751             }
4752             
4753             if (pp) { // weird bug on my firefox - for some reason this is not defined
4754                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4755                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4756             }
4757             return this;
4758         },        
4759
4760         /**
4761          * Returns true if the message box is currently displayed
4762          * @return {Boolean} True if the message box is visible, else false
4763          */
4764         isVisible : function(){
4765             return dlg && dlg.isVisible();  
4766         },
4767
4768         /**
4769          * Hides the message box if it is displayed
4770          */
4771         hide : function(){
4772             if(this.isVisible()){
4773                 dlg.hide();
4774             }  
4775         },
4776
4777         /**
4778          * Displays a new message box, or reinitializes an existing message box, based on the config options
4779          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4780          * The following config object properties are supported:
4781          * <pre>
4782 Property    Type             Description
4783 ----------  ---------------  ------------------------------------------------------------------------------------
4784 animEl            String/Element   An id or Element from which the message box should animate as it opens and
4785                                    closes (defaults to undefined)
4786 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4787                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
4788 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
4789                                    progress and wait dialogs will ignore this property and always hide the
4790                                    close button as they can only be closed programmatically.
4791 cls               String           A custom CSS class to apply to the message box element
4792 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
4793                                    displayed (defaults to 75)
4794 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
4795                                    function will be btn (the name of the button that was clicked, if applicable,
4796                                    e.g. "ok"), and text (the value of the active text field, if applicable).
4797                                    Progress and wait dialogs will ignore this option since they do not respond to
4798                                    user actions and can only be closed programmatically, so any required function
4799                                    should be called by the same code after it closes the dialog.
4800 icon              String           A CSS class that provides a background image to be used as an icon for
4801                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4802 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
4803 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
4804 modal             Boolean          False to allow user interaction with the page while the message box is
4805                                    displayed (defaults to true)
4806 msg               String           A string that will replace the existing message box body text (defaults
4807                                    to the XHTML-compliant non-breaking space character '&#160;')
4808 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
4809 progress          Boolean          True to display a progress bar (defaults to false)
4810 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
4811 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
4812 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
4813 title             String           The title text
4814 value             String           The string value to set into the active textbox element if displayed
4815 wait              Boolean          True to display a progress bar (defaults to false)
4816 width             Number           The width of the dialog in pixels
4817 </pre>
4818          *
4819          * Example usage:
4820          * <pre><code>
4821 Roo.Msg.show({
4822    title: 'Address',
4823    msg: 'Please enter your address:',
4824    width: 300,
4825    buttons: Roo.MessageBox.OKCANCEL,
4826    multiline: true,
4827    fn: saveAddress,
4828    animEl: 'addAddressBtn'
4829 });
4830 </code></pre>
4831          * @param {Object} config Configuration options
4832          * @return {Roo.MessageBox} This message box
4833          */
4834         show : function(options)
4835         {
4836             
4837             // this causes nightmares if you show one dialog after another
4838             // especially on callbacks..
4839              
4840             if(this.isVisible()){
4841                 
4842                 this.hide();
4843                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4844                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
4845                 Roo.log("New Dialog Message:" +  options.msg )
4846                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4847                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4848                 
4849             }
4850             var d = this.getDialog();
4851             opt = options;
4852             d.setTitle(opt.title || "&#160;");
4853             d.closeEl.setDisplayed(opt.closable !== false);
4854             activeTextEl = textboxEl;
4855             opt.prompt = opt.prompt || (opt.multiline ? true : false);
4856             if(opt.prompt){
4857                 if(opt.multiline){
4858                     textboxEl.hide();
4859                     textareaEl.show();
4860                     textareaEl.setHeight(typeof opt.multiline == "number" ?
4861                         opt.multiline : this.defaultTextHeight);
4862                     activeTextEl = textareaEl;
4863                 }else{
4864                     textboxEl.show();
4865                     textareaEl.hide();
4866                 }
4867             }else{
4868                 textboxEl.hide();
4869                 textareaEl.hide();
4870             }
4871             progressEl.setDisplayed(opt.progress === true);
4872             if (opt.progress) {
4873                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4874             }
4875             this.updateProgress(0);
4876             activeTextEl.dom.value = opt.value || "";
4877             if(opt.prompt){
4878                 dlg.setDefaultButton(activeTextEl);
4879             }else{
4880                 var bs = opt.buttons;
4881                 var db = null;
4882                 if(bs && bs.ok){
4883                     db = buttons["ok"];
4884                 }else if(bs && bs.yes){
4885                     db = buttons["yes"];
4886                 }
4887                 dlg.setDefaultButton(db);
4888             }
4889             bwidth = updateButtons(opt.buttons);
4890             this.updateText(opt.msg);
4891             if(opt.cls){
4892                 d.el.addClass(opt.cls);
4893             }
4894             d.proxyDrag = opt.proxyDrag === true;
4895             d.modal = opt.modal !== false;
4896             d.mask = opt.modal !== false ? mask : false;
4897             if(!d.isVisible()){
4898                 // force it to the end of the z-index stack so it gets a cursor in FF
4899                 document.body.appendChild(dlg.el.dom);
4900                 d.animateTarget = null;
4901                 d.show(options.animEl);
4902             }
4903             return this;
4904         },
4905
4906         /**
4907          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
4908          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
4909          * and closing the message box when the process is complete.
4910          * @param {String} title The title bar text
4911          * @param {String} msg The message box body text
4912          * @return {Roo.MessageBox} This message box
4913          */
4914         progress : function(title, msg){
4915             this.show({
4916                 title : title,
4917                 msg : msg,
4918                 buttons: false,
4919                 progress:true,
4920                 closable:false,
4921                 minWidth: this.minProgressWidth,
4922                 modal : true
4923             });
4924             return this;
4925         },
4926
4927         /**
4928          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
4929          * If a callback function is passed it will be called after the user clicks the button, and the
4930          * id of the button that was clicked will be passed as the only parameter to the callback
4931          * (could also be the top-right close button).
4932          * @param {String} title The title bar text
4933          * @param {String} msg The message box body text
4934          * @param {Function} fn (optional) The callback function invoked after the message box is closed
4935          * @param {Object} scope (optional) The scope of the callback function
4936          * @return {Roo.MessageBox} This message box
4937          */
4938         alert : function(title, msg, fn, scope)
4939         {
4940             this.show({
4941                 title : title,
4942                 msg : msg,
4943                 buttons: this.OK,
4944                 fn: fn,
4945                 closable : false,
4946                 scope : scope,
4947                 modal : true
4948             });
4949             return this;
4950         },
4951
4952         /**
4953          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
4954          * interaction while waiting for a long-running process to complete that does not have defined intervals.
4955          * You are responsible for closing the message box when the process is complete.
4956          * @param {String} msg The message box body text
4957          * @param {String} title (optional) The title bar text
4958          * @return {Roo.MessageBox} This message box
4959          */
4960         wait : function(msg, title){
4961             this.show({
4962                 title : title,
4963                 msg : msg,
4964                 buttons: false,
4965                 closable:false,
4966                 progress:true,
4967                 modal:true,
4968                 width:300,
4969                 wait:true
4970             });
4971             waitTimer = Roo.TaskMgr.start({
4972                 run: function(i){
4973                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
4974                 },
4975                 interval: 1000
4976             });
4977             return this;
4978         },
4979
4980         /**
4981          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
4982          * If a callback function is passed it will be called after the user clicks either button, and the id of the
4983          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
4984          * @param {String} title The title bar text
4985          * @param {String} msg The message box body text
4986          * @param {Function} fn (optional) The callback function invoked after the message box is closed
4987          * @param {Object} scope (optional) The scope of the callback function
4988          * @return {Roo.MessageBox} This message box
4989          */
4990         confirm : function(title, msg, fn, scope){
4991             this.show({
4992                 title : title,
4993                 msg : msg,
4994                 buttons: this.YESNO,
4995                 fn: fn,
4996                 scope : scope,
4997                 modal : true
4998             });
4999             return this;
5000         },
5001
5002         /**
5003          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5004          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5005          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5006          * (could also be the top-right close button) and the text that was entered will be passed as the two
5007          * parameters to the callback.
5008          * @param {String} title The title bar text
5009          * @param {String} msg The message box body text
5010          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5011          * @param {Object} scope (optional) The scope of the callback function
5012          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5013          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5014          * @return {Roo.MessageBox} This message box
5015          */
5016         prompt : function(title, msg, fn, scope, multiline){
5017             this.show({
5018                 title : title,
5019                 msg : msg,
5020                 buttons: this.OKCANCEL,
5021                 fn: fn,
5022                 minWidth:250,
5023                 scope : scope,
5024                 prompt:true,
5025                 multiline: multiline,
5026                 modal : true
5027             });
5028             return this;
5029         },
5030
5031         /**
5032          * Button config that displays a single OK button
5033          * @type Object
5034          */
5035         OK : {ok:true},
5036         /**
5037          * Button config that displays Yes and No buttons
5038          * @type Object
5039          */
5040         YESNO : {yes:true, no:true},
5041         /**
5042          * Button config that displays OK and Cancel buttons
5043          * @type Object
5044          */
5045         OKCANCEL : {ok:true, cancel:true},
5046         /**
5047          * Button config that displays Yes, No and Cancel buttons
5048          * @type Object
5049          */
5050         YESNOCANCEL : {yes:true, no:true, cancel:true},
5051
5052         /**
5053          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5054          * @type Number
5055          */
5056         defaultTextHeight : 75,
5057         /**
5058          * The maximum width in pixels of the message box (defaults to 600)
5059          * @type Number
5060          */
5061         maxWidth : 600,
5062         /**
5063          * The minimum width in pixels of the message box (defaults to 100)
5064          * @type Number
5065          */
5066         minWidth : 100,
5067         /**
5068          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5069          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5070          * @type Number
5071          */
5072         minProgressWidth : 250,
5073         /**
5074          * An object containing the default button text strings that can be overriden for localized language support.
5075          * Supported properties are: ok, cancel, yes and no.
5076          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5077          * @type Object
5078          */
5079         buttonText : {
5080             ok : "OK",
5081             cancel : "Cancel",
5082             yes : "Yes",
5083             no : "No"
5084         }
5085     };
5086 }();
5087
5088 /**
5089  * Shorthand for {@link Roo.MessageBox}
5090  */
5091 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5092 Roo.Msg = Roo.Msg || Roo.MessageBox;
5093 /*
5094  * - LGPL
5095  *
5096  * navbar
5097  * 
5098  */
5099
5100 /**
5101  * @class Roo.bootstrap.Navbar
5102  * @extends Roo.bootstrap.Component
5103  * Bootstrap Navbar class
5104
5105  * @constructor
5106  * Create a new Navbar
5107  * @param {Object} config The config object
5108  */
5109
5110
5111 Roo.bootstrap.Navbar = function(config){
5112     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5113     this.addEvents({
5114         // raw events
5115         /**
5116          * @event beforetoggle
5117          * Fire before toggle the menu
5118          * @param {Roo.EventObject} e
5119          */
5120         "beforetoggle" : true
5121     });
5122 };
5123
5124 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5125     
5126     
5127    
5128     // private
5129     navItems : false,
5130     loadMask : false,
5131     
5132     
5133     getAutoCreate : function(){
5134         
5135         
5136         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5137         
5138     },
5139     
5140     initEvents :function ()
5141     {
5142         //Roo.log(this.el.select('.navbar-toggle',true));
5143         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5144         
5145         var mark = {
5146             tag: "div",
5147             cls:"x-dlg-mask"
5148         };
5149         
5150         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5151         
5152         var size = this.el.getSize();
5153         this.maskEl.setSize(size.width, size.height);
5154         this.maskEl.enableDisplayMode("block");
5155         this.maskEl.hide();
5156         
5157         if(this.loadMask){
5158             this.maskEl.show();
5159         }
5160     },
5161     
5162     
5163     getChildContainer : function()
5164     {
5165         if (this.el && this.el.select('.collapse').getCount()) {
5166             return this.el.select('.collapse',true).first();
5167         }
5168         
5169         return this.el;
5170     },
5171     
5172     mask : function()
5173     {
5174         this.maskEl.show();
5175     },
5176     
5177     unmask : function()
5178     {
5179         this.maskEl.hide();
5180     },
5181     onToggle : function()
5182     {
5183         
5184         if(this.fireEvent('beforetoggle', this) === false){
5185             return;
5186         }
5187         var ce = this.el.select('.navbar-collapse',true).first();
5188       
5189         if (!ce.hasClass('show')) {
5190            this.expand();
5191         } else {
5192             this.collapse();
5193         }
5194         
5195         
5196     
5197     },
5198     /**
5199      * Expand the navbar pulldown 
5200      */
5201     expand : function ()
5202     {
5203        
5204         var ce = this.el.select('.navbar-collapse',true).first();
5205         if (ce.hasClass('collapsing')) {
5206             return;
5207         }
5208         ce.dom.style.height = '';
5209                // show it...
5210         ce.addClass('in'); // old...
5211         ce.removeClass('collapse');
5212         ce.addClass('show');
5213         var h = ce.getHeight();
5214         Roo.log(h);
5215         ce.removeClass('show');
5216         // at this point we should be able to see it..
5217         ce.addClass('collapsing');
5218         
5219         ce.setHeight(0); // resize it ...
5220         ce.on('transitionend', function() {
5221             //Roo.log('done transition');
5222             ce.removeClass('collapsing');
5223             ce.addClass('show');
5224             ce.removeClass('collapse');
5225
5226             ce.dom.style.height = '';
5227         }, this, { single: true} );
5228         ce.setHeight(h);
5229         ce.dom.scrollTop = 0;
5230     },
5231     /**
5232      * Collapse the navbar pulldown 
5233      */
5234     collapse : function()
5235     {
5236          var ce = this.el.select('.navbar-collapse',true).first();
5237        
5238         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5239             // it's collapsed or collapsing..
5240             return;
5241         }
5242         ce.removeClass('in'); // old...
5243         ce.setHeight(ce.getHeight());
5244         ce.removeClass('show');
5245         ce.addClass('collapsing');
5246         
5247         ce.on('transitionend', function() {
5248             ce.dom.style.height = '';
5249             ce.removeClass('collapsing');
5250             ce.addClass('collapse');
5251         }, this, { single: true} );
5252         ce.setHeight(0);
5253     }
5254     
5255     
5256     
5257 });
5258
5259
5260
5261  
5262
5263  /*
5264  * - LGPL
5265  *
5266  * navbar
5267  * 
5268  */
5269
5270 /**
5271  * @class Roo.bootstrap.NavSimplebar
5272  * @extends Roo.bootstrap.Navbar
5273  * Bootstrap Sidebar class
5274  *
5275  * @cfg {Boolean} inverse is inverted color
5276  * 
5277  * @cfg {String} type (nav | pills | tabs)
5278  * @cfg {Boolean} arrangement stacked | justified
5279  * @cfg {String} align (left | right) alignment
5280  * 
5281  * @cfg {Boolean} main (true|false) main nav bar? default false
5282  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5283  * 
5284  * @cfg {String} tag (header|footer|nav|div) default is nav 
5285
5286  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5287  * 
5288  * 
5289  * @constructor
5290  * Create a new Sidebar
5291  * @param {Object} config The config object
5292  */
5293
5294
5295 Roo.bootstrap.NavSimplebar = function(config){
5296     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5297 };
5298
5299 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5300     
5301     inverse: false,
5302     
5303     type: false,
5304     arrangement: '',
5305     align : false,
5306     
5307     weight : 'light',
5308     
5309     main : false,
5310     
5311     
5312     tag : false,
5313     
5314     
5315     getAutoCreate : function(){
5316         
5317         
5318         var cfg = {
5319             tag : this.tag || 'div',
5320             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5321         };
5322         if (['light','white'].indexOf(this.weight) > -1) {
5323             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5324         }
5325         cfg.cls += ' bg-' + this.weight;
5326         
5327         if (this.inverse) {
5328             cfg.cls += ' navbar-inverse';
5329             
5330         }
5331         
5332         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5333         
5334         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5335             return cfg;
5336         }
5337         
5338         
5339     
5340         
5341         cfg.cn = [
5342             {
5343                 cls: 'nav nav-' + this.xtype,
5344                 tag : 'ul'
5345             }
5346         ];
5347         
5348          
5349         this.type = this.type || 'nav';
5350         if (['tabs','pills'].indexOf(this.type) != -1) {
5351             cfg.cn[0].cls += ' nav-' + this.type
5352         
5353         
5354         } else {
5355             if (this.type!=='nav') {
5356                 Roo.log('nav type must be nav/tabs/pills')
5357             }
5358             cfg.cn[0].cls += ' navbar-nav'
5359         }
5360         
5361         
5362         
5363         
5364         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5365             cfg.cn[0].cls += ' nav-' + this.arrangement;
5366         }
5367         
5368         
5369         if (this.align === 'right') {
5370             cfg.cn[0].cls += ' navbar-right';
5371         }
5372         
5373         
5374         
5375         
5376         return cfg;
5377     
5378         
5379     }
5380     
5381     
5382     
5383 });
5384
5385
5386
5387  
5388
5389  
5390        /*
5391  * - LGPL
5392  *
5393  * navbar
5394  * navbar-fixed-top
5395  * navbar-expand-md  fixed-top 
5396  */
5397
5398 /**
5399  * @class Roo.bootstrap.NavHeaderbar
5400  * @extends Roo.bootstrap.NavSimplebar
5401  * Bootstrap Sidebar class
5402  *
5403  * @cfg {String} brand what is brand
5404  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5405  * @cfg {String} brand_href href of the brand
5406  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5407  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5408  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5409  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5410  * 
5411  * @constructor
5412  * Create a new Sidebar
5413  * @param {Object} config The config object
5414  */
5415
5416
5417 Roo.bootstrap.NavHeaderbar = function(config){
5418     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5419       
5420 };
5421
5422 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5423     
5424     position: '',
5425     brand: '',
5426     brand_href: false,
5427     srButton : true,
5428     autohide : false,
5429     desktopCenter : false,
5430    
5431     
5432     getAutoCreate : function(){
5433         
5434         var   cfg = {
5435             tag: this.nav || 'nav',
5436             cls: 'navbar navbar-expand-md',
5437             role: 'navigation',
5438             cn: []
5439         };
5440         
5441         var cn = cfg.cn;
5442         if (this.desktopCenter) {
5443             cn.push({cls : 'container', cn : []});
5444             cn = cn[0].cn;
5445         }
5446         
5447         if(this.srButton){
5448             var btn = {
5449                 tag: 'button',
5450                 type: 'button',
5451                 cls: 'navbar-toggle navbar-toggler',
5452                 'data-toggle': 'collapse',
5453                 cn: [
5454                     {
5455                         tag: 'span',
5456                         cls: 'sr-only',
5457                         html: 'Toggle navigation'
5458                     },
5459                     {
5460                         tag: 'span',
5461                         cls: 'icon-bar navbar-toggler-icon'
5462                     },
5463                     {
5464                         tag: 'span',
5465                         cls: 'icon-bar'
5466                     },
5467                     {
5468                         tag: 'span',
5469                         cls: 'icon-bar'
5470                     }
5471                 ]
5472             };
5473             
5474             cn.push( Roo.bootstrap.version == 4 ? btn : {
5475                 tag: 'div',
5476                 cls: 'navbar-header',
5477                 cn: [
5478                     btn
5479                 ]
5480             });
5481         }
5482         
5483         cn.push({
5484             tag: 'div',
5485             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5486             cn : []
5487         });
5488         
5489         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5490         
5491         if (['light','white'].indexOf(this.weight) > -1) {
5492             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5493         }
5494         cfg.cls += ' bg-' + this.weight;
5495         
5496         
5497         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5498             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5499             
5500             // tag can override this..
5501             
5502             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5503         }
5504         
5505         if (this.brand !== '') {
5506             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5507             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5508                 tag: 'a',
5509                 href: this.brand_href ? this.brand_href : '#',
5510                 cls: 'navbar-brand',
5511                 cn: [
5512                 this.brand
5513                 ]
5514             });
5515         }
5516         
5517         if(this.main){
5518             cfg.cls += ' main-nav';
5519         }
5520         
5521         
5522         return cfg;
5523
5524         
5525     },
5526     getHeaderChildContainer : function()
5527     {
5528         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5529             return this.el.select('.navbar-header',true).first();
5530         }
5531         
5532         return this.getChildContainer();
5533     },
5534     
5535     getChildContainer : function()
5536     {
5537          
5538         return this.el.select('.roo-navbar-collapse',true).first();
5539          
5540         
5541     },
5542     
5543     initEvents : function()
5544     {
5545         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5546         
5547         if (this.autohide) {
5548             
5549             var prevScroll = 0;
5550             var ft = this.el;
5551             
5552             Roo.get(document).on('scroll',function(e) {
5553                 var ns = Roo.get(document).getScroll().top;
5554                 var os = prevScroll;
5555                 prevScroll = ns;
5556                 
5557                 if(ns > os){
5558                     ft.removeClass('slideDown');
5559                     ft.addClass('slideUp');
5560                     return;
5561                 }
5562                 ft.removeClass('slideUp');
5563                 ft.addClass('slideDown');
5564                  
5565               
5566           },this);
5567         }
5568     }    
5569     
5570 });
5571
5572
5573
5574  
5575
5576  /*
5577  * - LGPL
5578  *
5579  * navbar
5580  * 
5581  */
5582
5583 /**
5584  * @class Roo.bootstrap.NavSidebar
5585  * @extends Roo.bootstrap.Navbar
5586  * Bootstrap Sidebar class
5587  * 
5588  * @constructor
5589  * Create a new Sidebar
5590  * @param {Object} config The config object
5591  */
5592
5593
5594 Roo.bootstrap.NavSidebar = function(config){
5595     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5596 };
5597
5598 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
5599     
5600     sidebar : true, // used by Navbar Item and NavbarGroup at present...
5601     
5602     getAutoCreate : function(){
5603         
5604         
5605         return  {
5606             tag: 'div',
5607             cls: 'sidebar sidebar-nav'
5608         };
5609     
5610         
5611     }
5612     
5613     
5614     
5615 });
5616
5617
5618
5619  
5620
5621  /*
5622  * - LGPL
5623  *
5624  * nav group
5625  * 
5626  */
5627
5628 /**
5629  * @class Roo.bootstrap.NavGroup
5630  * @extends Roo.bootstrap.Component
5631  * Bootstrap NavGroup class
5632  * @cfg {String} align (left|right)
5633  * @cfg {Boolean} inverse
5634  * @cfg {String} type (nav|pills|tab) default nav
5635  * @cfg {String} navId - reference Id for navbar.
5636
5637  * 
5638  * @constructor
5639  * Create a new nav group
5640  * @param {Object} config The config object
5641  */
5642
5643 Roo.bootstrap.NavGroup = function(config){
5644     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5645     this.navItems = [];
5646    
5647     Roo.bootstrap.NavGroup.register(this);
5648      this.addEvents({
5649         /**
5650              * @event changed
5651              * Fires when the active item changes
5652              * @param {Roo.bootstrap.NavGroup} this
5653              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5654              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
5655          */
5656         'changed': true
5657      });
5658     
5659 };
5660
5661 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
5662     
5663     align: '',
5664     inverse: false,
5665     form: false,
5666     type: 'nav',
5667     navId : '',
5668     // private
5669     
5670     navItems : false, 
5671     
5672     getAutoCreate : function()
5673     {
5674         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5675         
5676         cfg = {
5677             tag : 'ul',
5678             cls: 'nav' 
5679         };
5680         if (Roo.bootstrap.version == 4) {
5681             if (['tabs','pills'].indexOf(this.type) != -1) {
5682                 cfg.cls += ' nav-' + this.type; 
5683             } else {
5684                 // trying to remove so header bar can right align top?
5685                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5686                     // do not use on header bar... 
5687                     cfg.cls += ' navbar-nav';
5688                 }
5689             }
5690             
5691         } else {
5692             if (['tabs','pills'].indexOf(this.type) != -1) {
5693                 cfg.cls += ' nav-' + this.type
5694             } else {
5695                 if (this.type !== 'nav') {
5696                     Roo.log('nav type must be nav/tabs/pills')
5697                 }
5698                 cfg.cls += ' navbar-nav'
5699             }
5700         }
5701         
5702         if (this.parent() && this.parent().sidebar) {
5703             cfg = {
5704                 tag: 'ul',
5705                 cls: 'dashboard-menu sidebar-menu'
5706             };
5707             
5708             return cfg;
5709         }
5710         
5711         if (this.form === true) {
5712             cfg = {
5713                 tag: 'form',
5714                 cls: 'navbar-form form-inline'
5715             };
5716             //nav navbar-right ml-md-auto
5717             if (this.align === 'right') {
5718                 cfg.cls += ' navbar-right ml-md-auto';
5719             } else {
5720                 cfg.cls += ' navbar-left';
5721             }
5722         }
5723         
5724         if (this.align === 'right') {
5725             cfg.cls += ' navbar-right ml-md-auto';
5726         } else {
5727             cfg.cls += ' mr-auto';
5728         }
5729         
5730         if (this.inverse) {
5731             cfg.cls += ' navbar-inverse';
5732             
5733         }
5734         
5735         
5736         return cfg;
5737     },
5738     /**
5739     * sets the active Navigation item
5740     * @param {Roo.bootstrap.NavItem} the new current navitem
5741     */
5742     setActiveItem : function(item)
5743     {
5744         var prev = false;
5745         Roo.each(this.navItems, function(v){
5746             if (v == item) {
5747                 return ;
5748             }
5749             if (v.isActive()) {
5750                 v.setActive(false, true);
5751                 prev = v;
5752                 
5753             }
5754             
5755         });
5756
5757         item.setActive(true, true);
5758         this.fireEvent('changed', this, item, prev);
5759         
5760         
5761     },
5762     /**
5763     * gets the active Navigation item
5764     * @return {Roo.bootstrap.NavItem} the current navitem
5765     */
5766     getActive : function()
5767     {
5768         
5769         var prev = false;
5770         Roo.each(this.navItems, function(v){
5771             
5772             if (v.isActive()) {
5773                 prev = v;
5774                 
5775             }
5776             
5777         });
5778         return prev;
5779     },
5780     
5781     indexOfNav : function()
5782     {
5783         
5784         var prev = false;
5785         Roo.each(this.navItems, function(v,i){
5786             
5787             if (v.isActive()) {
5788                 prev = i;
5789                 
5790             }
5791             
5792         });
5793         return prev;
5794     },
5795     /**
5796     * adds a Navigation item
5797     * @param {Roo.bootstrap.NavItem} the navitem to add
5798     */
5799     addItem : function(cfg)
5800     {
5801         if (this.form && Roo.bootstrap.version == 4) {
5802             cfg.tag = 'div';
5803         }
5804         var cn = new Roo.bootstrap.NavItem(cfg);
5805         this.register(cn);
5806         cn.parentId = this.id;
5807         cn.onRender(this.el, null);
5808         return cn;
5809     },
5810     /**
5811     * register a Navigation item
5812     * @param {Roo.bootstrap.NavItem} the navitem to add
5813     */
5814     register : function(item)
5815     {
5816         this.navItems.push( item);
5817         item.navId = this.navId;
5818     
5819     },
5820     
5821     /**
5822     * clear all the Navigation item
5823     */
5824    
5825     clearAll : function()
5826     {
5827         this.navItems = [];
5828         this.el.dom.innerHTML = '';
5829     },
5830     
5831     getNavItem: function(tabId)
5832     {
5833         var ret = false;
5834         Roo.each(this.navItems, function(e) {
5835             if (e.tabId == tabId) {
5836                ret =  e;
5837                return false;
5838             }
5839             return true;
5840             
5841         });
5842         return ret;
5843     },
5844     
5845     setActiveNext : function()
5846     {
5847         var i = this.indexOfNav(this.getActive());
5848         if (i > this.navItems.length) {
5849             return;
5850         }
5851         this.setActiveItem(this.navItems[i+1]);
5852     },
5853     setActivePrev : function()
5854     {
5855         var i = this.indexOfNav(this.getActive());
5856         if (i  < 1) {
5857             return;
5858         }
5859         this.setActiveItem(this.navItems[i-1]);
5860     },
5861     clearWasActive : function(except) {
5862         Roo.each(this.navItems, function(e) {
5863             if (e.tabId != except.tabId && e.was_active) {
5864                e.was_active = false;
5865                return false;
5866             }
5867             return true;
5868             
5869         });
5870     },
5871     getWasActive : function ()
5872     {
5873         var r = false;
5874         Roo.each(this.navItems, function(e) {
5875             if (e.was_active) {
5876                r = e;
5877                return false;
5878             }
5879             return true;
5880             
5881         });
5882         return r;
5883     }
5884     
5885     
5886 });
5887
5888  
5889 Roo.apply(Roo.bootstrap.NavGroup, {
5890     
5891     groups: {},
5892      /**
5893     * register a Navigation Group
5894     * @param {Roo.bootstrap.NavGroup} the navgroup to add
5895     */
5896     register : function(navgrp)
5897     {
5898         this.groups[navgrp.navId] = navgrp;
5899         
5900     },
5901     /**
5902     * fetch a Navigation Group based on the navigation ID
5903     * @param {string} the navgroup to add
5904     * @returns {Roo.bootstrap.NavGroup} the navgroup 
5905     */
5906     get: function(navId) {
5907         if (typeof(this.groups[navId]) == 'undefined') {
5908             return false;
5909             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
5910         }
5911         return this.groups[navId] ;
5912     }
5913     
5914     
5915     
5916 });
5917
5918  /*
5919  * - LGPL
5920  *
5921  * row
5922  * 
5923  */
5924
5925 /**
5926  * @class Roo.bootstrap.NavItem
5927  * @extends Roo.bootstrap.Component
5928  * Bootstrap Navbar.NavItem class
5929  * @cfg {String} href  link to
5930  * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
5931
5932  * @cfg {String} html content of button
5933  * @cfg {String} badge text inside badge
5934  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
5935  * @cfg {String} glyphicon DEPRICATED - use fa
5936  * @cfg {String} icon DEPRICATED - use fa
5937  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
5938  * @cfg {Boolean} active Is item active
5939  * @cfg {Boolean} disabled Is item disabled
5940  
5941  * @cfg {Boolean} preventDefault (true | false) default false
5942  * @cfg {String} tabId the tab that this item activates.
5943  * @cfg {String} tagtype (a|span) render as a href or span?
5944  * @cfg {Boolean} animateRef (true|false) link to element default false  
5945   
5946  * @constructor
5947  * Create a new Navbar Item
5948  * @param {Object} config The config object
5949  */
5950 Roo.bootstrap.NavItem = function(config){
5951     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
5952     this.addEvents({
5953         // raw events
5954         /**
5955          * @event click
5956          * The raw click event for the entire grid.
5957          * @param {Roo.EventObject} e
5958          */
5959         "click" : true,
5960          /**
5961             * @event changed
5962             * Fires when the active item active state changes
5963             * @param {Roo.bootstrap.NavItem} this
5964             * @param {boolean} state the new state
5965              
5966          */
5967         'changed': true,
5968         /**
5969             * @event scrollto
5970             * Fires when scroll to element
5971             * @param {Roo.bootstrap.NavItem} this
5972             * @param {Object} options
5973             * @param {Roo.EventObject} e
5974              
5975          */
5976         'scrollto': true
5977     });
5978    
5979 };
5980
5981 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
5982     
5983     href: false,
5984     html: '',
5985     badge: '',
5986     icon: false,
5987     fa : false,
5988     glyphicon: false,
5989     active: false,
5990     preventDefault : false,
5991     tabId : false,
5992     tagtype : 'a',
5993     tag: 'li',
5994     disabled : false,
5995     animateRef : false,
5996     was_active : false,
5997     button_weight : '',
5998     button_outline : false,
5999     
6000     navLink: false,
6001     
6002     getAutoCreate : function(){
6003          
6004         var cfg = {
6005             tag: this.tag,
6006             cls: 'nav-item'
6007         };
6008         
6009         if (this.active) {
6010             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
6011         }
6012         if (this.disabled) {
6013             cfg.cls += ' disabled';
6014         }
6015         
6016         // BS4 only?
6017         if (this.button_weight.length) {
6018             cfg.tag = this.href ? 'a' : 'button';
6019             cfg.html = this.html || '';
6020             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6021             if (this.href) {
6022                 cfg.href = this.href;
6023             }
6024             if (this.fa) {
6025                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6026             }
6027             
6028             // menu .. should add dropdown-menu class - so no need for carat..
6029             
6030             if (this.badge !== '') {
6031                  
6032                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6033             }
6034             return cfg;
6035         }
6036         
6037         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6038             cfg.cn = [
6039                 {
6040                     tag: this.tagtype,
6041                     href : this.href || "#",
6042                     html: this.html || ''
6043                 }
6044             ];
6045             if (this.tagtype == 'a') {
6046                 cfg.cn[0].cls = 'nav-link';
6047             }
6048             if (this.icon) {
6049                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6050             }
6051             if (this.fa) {
6052                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6053             }
6054             if(this.glyphicon) {
6055                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6056             }
6057             
6058             if (this.menu) {
6059                 
6060                 cfg.cn[0].html += " <span class='caret'></span>";
6061              
6062             }
6063             
6064             if (this.badge !== '') {
6065                  
6066                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6067             }
6068         }
6069         
6070         
6071         
6072         return cfg;
6073     },
6074     onRender : function(ct, position)
6075     {
6076        // Roo.log("Call onRender: " + this.xtype);
6077         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6078             this.tag = 'div';
6079         }
6080         
6081         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6082         this.navLink = this.el.select('.nav-link',true).first();
6083         return ret;
6084     },
6085       
6086     
6087     initEvents: function() 
6088     {
6089         if (typeof (this.menu) != 'undefined') {
6090             this.menu.parentType = this.xtype;
6091             this.menu.triggerEl = this.el;
6092             this.menu = this.addxtype(Roo.apply({}, this.menu));
6093         }
6094         
6095         this.el.select('a',true).on('click', this.onClick, this);
6096         
6097         if(this.tagtype == 'span'){
6098             this.el.select('span',true).on('click', this.onClick, this);
6099         }
6100        
6101         // at this point parent should be available..
6102         this.parent().register(this);
6103     },
6104     
6105     onClick : function(e)
6106     {
6107         if (e.getTarget('.dropdown-menu-item')) {
6108             // did you click on a menu itemm.... - then don't trigger onclick..
6109             return;
6110         }
6111         
6112         if(
6113                 this.preventDefault || 
6114                 this.href == '#' 
6115         ){
6116             Roo.log("NavItem - prevent Default?");
6117             e.preventDefault();
6118         }
6119         
6120         if (this.disabled) {
6121             return;
6122         }
6123         
6124         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6125         if (tg && tg.transition) {
6126             Roo.log("waiting for the transitionend");
6127             return;
6128         }
6129         
6130         
6131         
6132         //Roo.log("fire event clicked");
6133         if(this.fireEvent('click', this, e) === false){
6134             return;
6135         };
6136         
6137         if(this.tagtype == 'span'){
6138             return;
6139         }
6140         
6141         //Roo.log(this.href);
6142         var ael = this.el.select('a',true).first();
6143         //Roo.log(ael);
6144         
6145         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6146             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6147             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6148                 return; // ignore... - it's a 'hash' to another page.
6149             }
6150             Roo.log("NavItem - prevent Default?");
6151             e.preventDefault();
6152             this.scrollToElement(e);
6153         }
6154         
6155         
6156         var p =  this.parent();
6157    
6158         if (['tabs','pills'].indexOf(p.type)!==-1) {
6159             if (typeof(p.setActiveItem) !== 'undefined') {
6160                 p.setActiveItem(this);
6161             }
6162         }
6163         
6164         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6165         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6166             // remove the collapsed menu expand...
6167             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6168         }
6169     },
6170     
6171     isActive: function () {
6172         return this.active
6173     },
6174     setActive : function(state, fire, is_was_active)
6175     {
6176         if (this.active && !state && this.navId) {
6177             this.was_active = true;
6178             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6179             if (nv) {
6180                 nv.clearWasActive(this);
6181             }
6182             
6183         }
6184         this.active = state;
6185         
6186         if (!state ) {
6187             this.el.removeClass('active');
6188             this.navLink ? this.navLink.removeClass('active') : false;
6189         } else if (!this.el.hasClass('active')) {
6190             
6191             this.el.addClass('active');
6192             if (Roo.bootstrap.version == 4 && this.navLink ) {
6193                 this.navLink.addClass('active');
6194             }
6195             
6196         }
6197         if (fire) {
6198             this.fireEvent('changed', this, state);
6199         }
6200         
6201         // show a panel if it's registered and related..
6202         
6203         if (!this.navId || !this.tabId || !state || is_was_active) {
6204             return;
6205         }
6206         
6207         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6208         if (!tg) {
6209             return;
6210         }
6211         var pan = tg.getPanelByName(this.tabId);
6212         if (!pan) {
6213             return;
6214         }
6215         // if we can not flip to new panel - go back to old nav highlight..
6216         if (false == tg.showPanel(pan)) {
6217             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6218             if (nv) {
6219                 var onav = nv.getWasActive();
6220                 if (onav) {
6221                     onav.setActive(true, false, true);
6222                 }
6223             }
6224             
6225         }
6226         
6227         
6228         
6229     },
6230      // this should not be here...
6231     setDisabled : function(state)
6232     {
6233         this.disabled = state;
6234         if (!state ) {
6235             this.el.removeClass('disabled');
6236         } else if (!this.el.hasClass('disabled')) {
6237             this.el.addClass('disabled');
6238         }
6239         
6240     },
6241     
6242     /**
6243      * Fetch the element to display the tooltip on.
6244      * @return {Roo.Element} defaults to this.el
6245      */
6246     tooltipEl : function()
6247     {
6248         return this.el.select('' + this.tagtype + '', true).first();
6249     },
6250     
6251     scrollToElement : function(e)
6252     {
6253         var c = document.body;
6254         
6255         /*
6256          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6257          */
6258         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6259             c = document.documentElement;
6260         }
6261         
6262         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6263         
6264         if(!target){
6265             return;
6266         }
6267
6268         var o = target.calcOffsetsTo(c);
6269         
6270         var options = {
6271             target : target,
6272             value : o[1]
6273         };
6274         
6275         this.fireEvent('scrollto', this, options, e);
6276         
6277         Roo.get(c).scrollTo('top', options.value, true);
6278         
6279         return;
6280     }
6281 });
6282  
6283
6284  /*
6285  * - LGPL
6286  *
6287  * sidebar item
6288  *
6289  *  li
6290  *    <span> icon </span>
6291  *    <span> text </span>
6292  *    <span>badge </span>
6293  */
6294
6295 /**
6296  * @class Roo.bootstrap.NavSidebarItem
6297  * @extends Roo.bootstrap.NavItem
6298  * Bootstrap Navbar.NavSidebarItem class
6299  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6300  * {Boolean} open is the menu open
6301  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6302  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6303  * {String} buttonSize (sm|md|lg)the extra classes for the button
6304  * {Boolean} showArrow show arrow next to the text (default true)
6305  * @constructor
6306  * Create a new Navbar Button
6307  * @param {Object} config The config object
6308  */
6309 Roo.bootstrap.NavSidebarItem = function(config){
6310     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6311     this.addEvents({
6312         // raw events
6313         /**
6314          * @event click
6315          * The raw click event for the entire grid.
6316          * @param {Roo.EventObject} e
6317          */
6318         "click" : true,
6319          /**
6320             * @event changed
6321             * Fires when the active item active state changes
6322             * @param {Roo.bootstrap.NavSidebarItem} this
6323             * @param {boolean} state the new state
6324              
6325          */
6326         'changed': true
6327     });
6328    
6329 };
6330
6331 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6332     
6333     badgeWeight : 'default',
6334     
6335     open: false,
6336     
6337     buttonView : false,
6338     
6339     buttonWeight : 'default',
6340     
6341     buttonSize : 'md',
6342     
6343     showArrow : true,
6344     
6345     getAutoCreate : function(){
6346         
6347         
6348         var a = {
6349                 tag: 'a',
6350                 href : this.href || '#',
6351                 cls: '',
6352                 html : '',
6353                 cn : []
6354         };
6355         
6356         if(this.buttonView){
6357             a = {
6358                 tag: 'button',
6359                 href : this.href || '#',
6360                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6361                 html : this.html,
6362                 cn : []
6363             };
6364         }
6365         
6366         var cfg = {
6367             tag: 'li',
6368             cls: '',
6369             cn: [ a ]
6370         };
6371         
6372         if (this.active) {
6373             cfg.cls += ' active';
6374         }
6375         
6376         if (this.disabled) {
6377             cfg.cls += ' disabled';
6378         }
6379         if (this.open) {
6380             cfg.cls += ' open x-open';
6381         }
6382         // left icon..
6383         if (this.glyphicon || this.icon) {
6384             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6385             a.cn.push({ tag : 'i', cls : c }) ;
6386         }
6387         
6388         if(!this.buttonView){
6389             var span = {
6390                 tag: 'span',
6391                 html : this.html || ''
6392             };
6393
6394             a.cn.push(span);
6395             
6396         }
6397         
6398         if (this.badge !== '') {
6399             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6400         }
6401         
6402         if (this.menu) {
6403             
6404             if(this.showArrow){
6405                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6406             }
6407             
6408             a.cls += ' dropdown-toggle treeview' ;
6409         }
6410         
6411         return cfg;
6412     },
6413     
6414     initEvents : function()
6415     { 
6416         if (typeof (this.menu) != 'undefined') {
6417             this.menu.parentType = this.xtype;
6418             this.menu.triggerEl = this.el;
6419             this.menu = this.addxtype(Roo.apply({}, this.menu));
6420         }
6421         
6422         this.el.on('click', this.onClick, this);
6423         
6424         if(this.badge !== ''){
6425             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6426         }
6427         
6428     },
6429     
6430     onClick : function(e)
6431     {
6432         if(this.disabled){
6433             e.preventDefault();
6434             return;
6435         }
6436         
6437         if(this.preventDefault){
6438             e.preventDefault();
6439         }
6440         
6441         this.fireEvent('click', this, e);
6442     },
6443     
6444     disable : function()
6445     {
6446         this.setDisabled(true);
6447     },
6448     
6449     enable : function()
6450     {
6451         this.setDisabled(false);
6452     },
6453     
6454     setDisabled : function(state)
6455     {
6456         if(this.disabled == state){
6457             return;
6458         }
6459         
6460         this.disabled = state;
6461         
6462         if (state) {
6463             this.el.addClass('disabled');
6464             return;
6465         }
6466         
6467         this.el.removeClass('disabled');
6468         
6469         return;
6470     },
6471     
6472     setActive : function(state)
6473     {
6474         if(this.active == state){
6475             return;
6476         }
6477         
6478         this.active = state;
6479         
6480         if (state) {
6481             this.el.addClass('active');
6482             return;
6483         }
6484         
6485         this.el.removeClass('active');
6486         
6487         return;
6488     },
6489     
6490     isActive: function () 
6491     {
6492         return this.active;
6493     },
6494     
6495     setBadge : function(str)
6496     {
6497         if(!this.badgeEl){
6498             return;
6499         }
6500         
6501         this.badgeEl.dom.innerHTML = str;
6502     }
6503     
6504    
6505      
6506  
6507 });
6508  
6509
6510  /*
6511  * - LGPL
6512  *
6513  * row
6514  * 
6515  */
6516
6517 /**
6518  * @class Roo.bootstrap.Row
6519  * @extends Roo.bootstrap.Component
6520  * Bootstrap Row class (contains columns...)
6521  * 
6522  * @constructor
6523  * Create a new Row
6524  * @param {Object} config The config object
6525  */
6526
6527 Roo.bootstrap.Row = function(config){
6528     Roo.bootstrap.Row.superclass.constructor.call(this, config);
6529 };
6530
6531 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
6532     
6533     getAutoCreate : function(){
6534        return {
6535             cls: 'row clearfix'
6536        };
6537     }
6538     
6539     
6540 });
6541
6542  
6543
6544  /*
6545  * - LGPL
6546  *
6547  * pagination
6548  * 
6549  */
6550
6551 /**
6552  * @class Roo.bootstrap.Pagination
6553  * @extends Roo.bootstrap.Component
6554  * Bootstrap Pagination class
6555  * @cfg {String} size xs | sm | md | lg
6556  * @cfg {Boolean} inverse false | true
6557  * 
6558  * @constructor
6559  * Create a new Pagination
6560  * @param {Object} config The config object
6561  */
6562
6563 Roo.bootstrap.Pagination = function(config){
6564     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6565 };
6566
6567 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
6568     
6569     cls: false,
6570     size: false,
6571     inverse: false,
6572     
6573     getAutoCreate : function(){
6574         var cfg = {
6575             tag: 'ul',
6576                 cls: 'pagination'
6577         };
6578         if (this.inverse) {
6579             cfg.cls += ' inverse';
6580         }
6581         if (this.html) {
6582             cfg.html=this.html;
6583         }
6584         if (this.cls) {
6585             cfg.cls += " " + this.cls;
6586         }
6587         return cfg;
6588     }
6589    
6590 });
6591
6592  
6593
6594  /*
6595  * - LGPL
6596  *
6597  * Pagination item
6598  * 
6599  */
6600
6601
6602 /**
6603  * @class Roo.bootstrap.PaginationItem
6604  * @extends Roo.bootstrap.Component
6605  * Bootstrap PaginationItem class
6606  * @cfg {String} html text
6607  * @cfg {String} href the link
6608  * @cfg {Boolean} preventDefault (true | false) default true
6609  * @cfg {Boolean} active (true | false) default false
6610  * @cfg {Boolean} disabled default false
6611  * 
6612  * 
6613  * @constructor
6614  * Create a new PaginationItem
6615  * @param {Object} config The config object
6616  */
6617
6618
6619 Roo.bootstrap.PaginationItem = function(config){
6620     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6621     this.addEvents({
6622         // raw events
6623         /**
6624          * @event click
6625          * The raw click event for the entire grid.
6626          * @param {Roo.EventObject} e
6627          */
6628         "click" : true
6629     });
6630 };
6631
6632 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
6633     
6634     href : false,
6635     html : false,
6636     preventDefault: true,
6637     active : false,
6638     cls : false,
6639     disabled: false,
6640     
6641     getAutoCreate : function(){
6642         var cfg= {
6643             tag: 'li',
6644             cn: [
6645                 {
6646                     tag : 'a',
6647                     href : this.href ? this.href : '#',
6648                     html : this.html ? this.html : ''
6649                 }
6650             ]
6651         };
6652         
6653         if(this.cls){
6654             cfg.cls = this.cls;
6655         }
6656         
6657         if(this.disabled){
6658             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6659         }
6660         
6661         if(this.active){
6662             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6663         }
6664         
6665         return cfg;
6666     },
6667     
6668     initEvents: function() {
6669         
6670         this.el.on('click', this.onClick, this);
6671         
6672     },
6673     onClick : function(e)
6674     {
6675         Roo.log('PaginationItem on click ');
6676         if(this.preventDefault){
6677             e.preventDefault();
6678         }
6679         
6680         if(this.disabled){
6681             return;
6682         }
6683         
6684         this.fireEvent('click', this, e);
6685     }
6686    
6687 });
6688
6689  
6690
6691  /*
6692  * - LGPL
6693  *
6694  * slider
6695  * 
6696  */
6697
6698
6699 /**
6700  * @class Roo.bootstrap.Slider
6701  * @extends Roo.bootstrap.Component
6702  * Bootstrap Slider class
6703  *    
6704  * @constructor
6705  * Create a new Slider
6706  * @param {Object} config The config object
6707  */
6708
6709 Roo.bootstrap.Slider = function(config){
6710     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6711 };
6712
6713 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
6714     
6715     getAutoCreate : function(){
6716         
6717         var cfg = {
6718             tag: 'div',
6719             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6720             cn: [
6721                 {
6722                     tag: 'a',
6723                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
6724                 }
6725             ]
6726         };
6727         
6728         return cfg;
6729     }
6730    
6731 });
6732
6733  /*
6734  * Based on:
6735  * Ext JS Library 1.1.1
6736  * Copyright(c) 2006-2007, Ext JS, LLC.
6737  *
6738  * Originally Released Under LGPL - original licence link has changed is not relivant.
6739  *
6740  * Fork - LGPL
6741  * <script type="text/javascript">
6742  */
6743  
6744
6745 /**
6746  * @class Roo.grid.ColumnModel
6747  * @extends Roo.util.Observable
6748  * This is the default implementation of a ColumnModel used by the Grid. It defines
6749  * the columns in the grid.
6750  * <br>Usage:<br>
6751  <pre><code>
6752  var colModel = new Roo.grid.ColumnModel([
6753         {header: "Ticker", width: 60, sortable: true, locked: true},
6754         {header: "Company Name", width: 150, sortable: true},
6755         {header: "Market Cap.", width: 100, sortable: true},
6756         {header: "$ Sales", width: 100, sortable: true, renderer: money},
6757         {header: "Employees", width: 100, sortable: true, resizable: false}
6758  ]);
6759  </code></pre>
6760  * <p>
6761  
6762  * The config options listed for this class are options which may appear in each
6763  * individual column definition.
6764  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
6765  * @constructor
6766  * @param {Object} config An Array of column config objects. See this class's
6767  * config objects for details.
6768 */
6769 Roo.grid.ColumnModel = function(config){
6770         /**
6771      * The config passed into the constructor
6772      */
6773     this.config = config;
6774     this.lookup = {};
6775
6776     // if no id, create one
6777     // if the column does not have a dataIndex mapping,
6778     // map it to the order it is in the config
6779     for(var i = 0, len = config.length; i < len; i++){
6780         var c = config[i];
6781         if(typeof c.dataIndex == "undefined"){
6782             c.dataIndex = i;
6783         }
6784         if(typeof c.renderer == "string"){
6785             c.renderer = Roo.util.Format[c.renderer];
6786         }
6787         if(typeof c.id == "undefined"){
6788             c.id = Roo.id();
6789         }
6790         if(c.editor && c.editor.xtype){
6791             c.editor  = Roo.factory(c.editor, Roo.grid);
6792         }
6793         if(c.editor && c.editor.isFormField){
6794             c.editor = new Roo.grid.GridEditor(c.editor);
6795         }
6796         this.lookup[c.id] = c;
6797     }
6798
6799     /**
6800      * The width of columns which have no width specified (defaults to 100)
6801      * @type Number
6802      */
6803     this.defaultWidth = 100;
6804
6805     /**
6806      * Default sortable of columns which have no sortable specified (defaults to false)
6807      * @type Boolean
6808      */
6809     this.defaultSortable = false;
6810
6811     this.addEvents({
6812         /**
6813              * @event widthchange
6814              * Fires when the width of a column changes.
6815              * @param {ColumnModel} this
6816              * @param {Number} columnIndex The column index
6817              * @param {Number} newWidth The new width
6818              */
6819             "widthchange": true,
6820         /**
6821              * @event headerchange
6822              * Fires when the text of a header changes.
6823              * @param {ColumnModel} this
6824              * @param {Number} columnIndex The column index
6825              * @param {Number} newText The new header text
6826              */
6827             "headerchange": true,
6828         /**
6829              * @event hiddenchange
6830              * Fires when a column is hidden or "unhidden".
6831              * @param {ColumnModel} this
6832              * @param {Number} columnIndex The column index
6833              * @param {Boolean} hidden true if hidden, false otherwise
6834              */
6835             "hiddenchange": true,
6836             /**
6837          * @event columnmoved
6838          * Fires when a column is moved.
6839          * @param {ColumnModel} this
6840          * @param {Number} oldIndex
6841          * @param {Number} newIndex
6842          */
6843         "columnmoved" : true,
6844         /**
6845          * @event columlockchange
6846          * Fires when a column's locked state is changed
6847          * @param {ColumnModel} this
6848          * @param {Number} colIndex
6849          * @param {Boolean} locked true if locked
6850          */
6851         "columnlockchange" : true
6852     });
6853     Roo.grid.ColumnModel.superclass.constructor.call(this);
6854 };
6855 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
6856     /**
6857      * @cfg {String} header The header text to display in the Grid view.
6858      */
6859     /**
6860      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
6861      * {@link Roo.data.Record} definition from which to draw the column's value. If not
6862      * specified, the column's index is used as an index into the Record's data Array.
6863      */
6864     /**
6865      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
6866      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
6867      */
6868     /**
6869      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
6870      * Defaults to the value of the {@link #defaultSortable} property.
6871      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
6872      */
6873     /**
6874      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
6875      */
6876     /**
6877      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
6878      */
6879     /**
6880      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
6881      */
6882     /**
6883      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
6884      */
6885     /**
6886      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
6887      * given the cell's data value. See {@link #setRenderer}. If not specified, the
6888      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
6889      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
6890      */
6891        /**
6892      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
6893      */
6894     /**
6895      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
6896      */
6897     /**
6898      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
6899      */
6900     /**
6901      * @cfg {String} cursor (Optional)
6902      */
6903     /**
6904      * @cfg {String} tooltip (Optional)
6905      */
6906     /**
6907      * @cfg {Number} xs (Optional)
6908      */
6909     /**
6910      * @cfg {Number} sm (Optional)
6911      */
6912     /**
6913      * @cfg {Number} md (Optional)
6914      */
6915     /**
6916      * @cfg {Number} lg (Optional)
6917      */
6918     /**
6919      * Returns the id of the column at the specified index.
6920      * @param {Number} index The column index
6921      * @return {String} the id
6922      */
6923     getColumnId : function(index){
6924         return this.config[index].id;
6925     },
6926
6927     /**
6928      * Returns the column for a specified id.
6929      * @param {String} id The column id
6930      * @return {Object} the column
6931      */
6932     getColumnById : function(id){
6933         return this.lookup[id];
6934     },
6935
6936     
6937     /**
6938      * Returns the column for a specified dataIndex.
6939      * @param {String} dataIndex The column dataIndex
6940      * @return {Object|Boolean} the column or false if not found
6941      */
6942     getColumnByDataIndex: function(dataIndex){
6943         var index = this.findColumnIndex(dataIndex);
6944         return index > -1 ? this.config[index] : false;
6945     },
6946     
6947     /**
6948      * Returns the index for a specified column id.
6949      * @param {String} id The column id
6950      * @return {Number} the index, or -1 if not found
6951      */
6952     getIndexById : function(id){
6953         for(var i = 0, len = this.config.length; i < len; i++){
6954             if(this.config[i].id == id){
6955                 return i;
6956             }
6957         }
6958         return -1;
6959     },
6960     
6961     /**
6962      * Returns the index for a specified column dataIndex.
6963      * @param {String} dataIndex The column dataIndex
6964      * @return {Number} the index, or -1 if not found
6965      */
6966     
6967     findColumnIndex : function(dataIndex){
6968         for(var i = 0, len = this.config.length; i < len; i++){
6969             if(this.config[i].dataIndex == dataIndex){
6970                 return i;
6971             }
6972         }
6973         return -1;
6974     },
6975     
6976     
6977     moveColumn : function(oldIndex, newIndex){
6978         var c = this.config[oldIndex];
6979         this.config.splice(oldIndex, 1);
6980         this.config.splice(newIndex, 0, c);
6981         this.dataMap = null;
6982         this.fireEvent("columnmoved", this, oldIndex, newIndex);
6983     },
6984
6985     isLocked : function(colIndex){
6986         return this.config[colIndex].locked === true;
6987     },
6988
6989     setLocked : function(colIndex, value, suppressEvent){
6990         if(this.isLocked(colIndex) == value){
6991             return;
6992         }
6993         this.config[colIndex].locked = value;
6994         if(!suppressEvent){
6995             this.fireEvent("columnlockchange", this, colIndex, value);
6996         }
6997     },
6998
6999     getTotalLockedWidth : function(){
7000         var totalWidth = 0;
7001         for(var i = 0; i < this.config.length; i++){
7002             if(this.isLocked(i) && !this.isHidden(i)){
7003                 this.totalWidth += this.getColumnWidth(i);
7004             }
7005         }
7006         return totalWidth;
7007     },
7008
7009     getLockedCount : function(){
7010         for(var i = 0, len = this.config.length; i < len; i++){
7011             if(!this.isLocked(i)){
7012                 return i;
7013             }
7014         }
7015         
7016         return this.config.length;
7017     },
7018
7019     /**
7020      * Returns the number of columns.
7021      * @return {Number}
7022      */
7023     getColumnCount : function(visibleOnly){
7024         if(visibleOnly === true){
7025             var c = 0;
7026             for(var i = 0, len = this.config.length; i < len; i++){
7027                 if(!this.isHidden(i)){
7028                     c++;
7029                 }
7030             }
7031             return c;
7032         }
7033         return this.config.length;
7034     },
7035
7036     /**
7037      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7038      * @param {Function} fn
7039      * @param {Object} scope (optional)
7040      * @return {Array} result
7041      */
7042     getColumnsBy : function(fn, scope){
7043         var r = [];
7044         for(var i = 0, len = this.config.length; i < len; i++){
7045             var c = this.config[i];
7046             if(fn.call(scope||this, c, i) === true){
7047                 r[r.length] = c;
7048             }
7049         }
7050         return r;
7051     },
7052
7053     /**
7054      * Returns true if the specified column is sortable.
7055      * @param {Number} col The column index
7056      * @return {Boolean}
7057      */
7058     isSortable : function(col){
7059         if(typeof this.config[col].sortable == "undefined"){
7060             return this.defaultSortable;
7061         }
7062         return this.config[col].sortable;
7063     },
7064
7065     /**
7066      * Returns the rendering (formatting) function defined for the column.
7067      * @param {Number} col The column index.
7068      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7069      */
7070     getRenderer : function(col){
7071         if(!this.config[col].renderer){
7072             return Roo.grid.ColumnModel.defaultRenderer;
7073         }
7074         return this.config[col].renderer;
7075     },
7076
7077     /**
7078      * Sets the rendering (formatting) function for a column.
7079      * @param {Number} col The column index
7080      * @param {Function} fn The function to use to process the cell's raw data
7081      * to return HTML markup for the grid view. The render function is called with
7082      * the following parameters:<ul>
7083      * <li>Data value.</li>
7084      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7085      * <li>css A CSS style string to apply to the table cell.</li>
7086      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7087      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7088      * <li>Row index</li>
7089      * <li>Column index</li>
7090      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7091      */
7092     setRenderer : function(col, fn){
7093         this.config[col].renderer = fn;
7094     },
7095
7096     /**
7097      * Returns the width for the specified column.
7098      * @param {Number} col The column index
7099      * @return {Number}
7100      */
7101     getColumnWidth : function(col){
7102         return this.config[col].width * 1 || this.defaultWidth;
7103     },
7104
7105     /**
7106      * Sets the width for a column.
7107      * @param {Number} col The column index
7108      * @param {Number} width The new width
7109      */
7110     setColumnWidth : function(col, width, suppressEvent){
7111         this.config[col].width = width;
7112         this.totalWidth = null;
7113         if(!suppressEvent){
7114              this.fireEvent("widthchange", this, col, width);
7115         }
7116     },
7117
7118     /**
7119      * Returns the total width of all columns.
7120      * @param {Boolean} includeHidden True to include hidden column widths
7121      * @return {Number}
7122      */
7123     getTotalWidth : function(includeHidden){
7124         if(!this.totalWidth){
7125             this.totalWidth = 0;
7126             for(var i = 0, len = this.config.length; i < len; i++){
7127                 if(includeHidden || !this.isHidden(i)){
7128                     this.totalWidth += this.getColumnWidth(i);
7129                 }
7130             }
7131         }
7132         return this.totalWidth;
7133     },
7134
7135     /**
7136      * Returns the header for the specified column.
7137      * @param {Number} col The column index
7138      * @return {String}
7139      */
7140     getColumnHeader : function(col){
7141         return this.config[col].header;
7142     },
7143
7144     /**
7145      * Sets the header for a column.
7146      * @param {Number} col The column index
7147      * @param {String} header The new header
7148      */
7149     setColumnHeader : function(col, header){
7150         this.config[col].header = header;
7151         this.fireEvent("headerchange", this, col, header);
7152     },
7153
7154     /**
7155      * Returns the tooltip for the specified column.
7156      * @param {Number} col The column index
7157      * @return {String}
7158      */
7159     getColumnTooltip : function(col){
7160             return this.config[col].tooltip;
7161     },
7162     /**
7163      * Sets the tooltip for a column.
7164      * @param {Number} col The column index
7165      * @param {String} tooltip The new tooltip
7166      */
7167     setColumnTooltip : function(col, tooltip){
7168             this.config[col].tooltip = tooltip;
7169     },
7170
7171     /**
7172      * Returns the dataIndex for the specified column.
7173      * @param {Number} col The column index
7174      * @return {Number}
7175      */
7176     getDataIndex : function(col){
7177         return this.config[col].dataIndex;
7178     },
7179
7180     /**
7181      * Sets the dataIndex for a column.
7182      * @param {Number} col The column index
7183      * @param {Number} dataIndex The new dataIndex
7184      */
7185     setDataIndex : function(col, dataIndex){
7186         this.config[col].dataIndex = dataIndex;
7187     },
7188
7189     
7190     
7191     /**
7192      * Returns true if the cell is editable.
7193      * @param {Number} colIndex The column index
7194      * @param {Number} rowIndex The row index - this is nto actually used..?
7195      * @return {Boolean}
7196      */
7197     isCellEditable : function(colIndex, rowIndex){
7198         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7199     },
7200
7201     /**
7202      * Returns the editor defined for the cell/column.
7203      * return false or null to disable editing.
7204      * @param {Number} colIndex The column index
7205      * @param {Number} rowIndex The row index
7206      * @return {Object}
7207      */
7208     getCellEditor : function(colIndex, rowIndex){
7209         return this.config[colIndex].editor;
7210     },
7211
7212     /**
7213      * Sets if a column is editable.
7214      * @param {Number} col The column index
7215      * @param {Boolean} editable True if the column is editable
7216      */
7217     setEditable : function(col, editable){
7218         this.config[col].editable = editable;
7219     },
7220
7221
7222     /**
7223      * Returns true if the column is hidden.
7224      * @param {Number} colIndex The column index
7225      * @return {Boolean}
7226      */
7227     isHidden : function(colIndex){
7228         return this.config[colIndex].hidden;
7229     },
7230
7231
7232     /**
7233      * Returns true if the column width cannot be changed
7234      */
7235     isFixed : function(colIndex){
7236         return this.config[colIndex].fixed;
7237     },
7238
7239     /**
7240      * Returns true if the column can be resized
7241      * @return {Boolean}
7242      */
7243     isResizable : function(colIndex){
7244         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7245     },
7246     /**
7247      * Sets if a column is hidden.
7248      * @param {Number} colIndex The column index
7249      * @param {Boolean} hidden True if the column is hidden
7250      */
7251     setHidden : function(colIndex, hidden){
7252         this.config[colIndex].hidden = hidden;
7253         this.totalWidth = null;
7254         this.fireEvent("hiddenchange", this, colIndex, hidden);
7255     },
7256
7257     /**
7258      * Sets the editor for a column.
7259      * @param {Number} col The column index
7260      * @param {Object} editor The editor object
7261      */
7262     setEditor : function(col, editor){
7263         this.config[col].editor = editor;
7264     }
7265 });
7266
7267 Roo.grid.ColumnModel.defaultRenderer = function(value)
7268 {
7269     if(typeof value == "object") {
7270         return value;
7271     }
7272         if(typeof value == "string" && value.length < 1){
7273             return "&#160;";
7274         }
7275     
7276         return String.format("{0}", value);
7277 };
7278
7279 // Alias for backwards compatibility
7280 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7281 /*
7282  * Based on:
7283  * Ext JS Library 1.1.1
7284  * Copyright(c) 2006-2007, Ext JS, LLC.
7285  *
7286  * Originally Released Under LGPL - original licence link has changed is not relivant.
7287  *
7288  * Fork - LGPL
7289  * <script type="text/javascript">
7290  */
7291  
7292 /**
7293  * @class Roo.LoadMask
7294  * A simple utility class for generically masking elements while loading data.  If the element being masked has
7295  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7296  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
7297  * element's UpdateManager load indicator and will be destroyed after the initial load.
7298  * @constructor
7299  * Create a new LoadMask
7300  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7301  * @param {Object} config The config object
7302  */
7303 Roo.LoadMask = function(el, config){
7304     this.el = Roo.get(el);
7305     Roo.apply(this, config);
7306     if(this.store){
7307         this.store.on('beforeload', this.onBeforeLoad, this);
7308         this.store.on('load', this.onLoad, this);
7309         this.store.on('loadexception', this.onLoadException, this);
7310         this.removeMask = false;
7311     }else{
7312         var um = this.el.getUpdateManager();
7313         um.showLoadIndicator = false; // disable the default indicator
7314         um.on('beforeupdate', this.onBeforeLoad, this);
7315         um.on('update', this.onLoad, this);
7316         um.on('failure', this.onLoad, this);
7317         this.removeMask = true;
7318     }
7319 };
7320
7321 Roo.LoadMask.prototype = {
7322     /**
7323      * @cfg {Boolean} removeMask
7324      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7325      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7326      */
7327     /**
7328      * @cfg {String} msg
7329      * The text to display in a centered loading message box (defaults to 'Loading...')
7330      */
7331     msg : 'Loading...',
7332     /**
7333      * @cfg {String} msgCls
7334      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7335      */
7336     msgCls : 'x-mask-loading',
7337
7338     /**
7339      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7340      * @type Boolean
7341      */
7342     disabled: false,
7343
7344     /**
7345      * Disables the mask to prevent it from being displayed
7346      */
7347     disable : function(){
7348        this.disabled = true;
7349     },
7350
7351     /**
7352      * Enables the mask so that it can be displayed
7353      */
7354     enable : function(){
7355         this.disabled = false;
7356     },
7357     
7358     onLoadException : function()
7359     {
7360         Roo.log(arguments);
7361         
7362         if (typeof(arguments[3]) != 'undefined') {
7363             Roo.MessageBox.alert("Error loading",arguments[3]);
7364         } 
7365         /*
7366         try {
7367             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7368                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7369             }   
7370         } catch(e) {
7371             
7372         }
7373         */
7374     
7375         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7376     },
7377     // private
7378     onLoad : function()
7379     {
7380         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7381     },
7382
7383     // private
7384     onBeforeLoad : function(){
7385         if(!this.disabled){
7386             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7387         }
7388     },
7389
7390     // private
7391     destroy : function(){
7392         if(this.store){
7393             this.store.un('beforeload', this.onBeforeLoad, this);
7394             this.store.un('load', this.onLoad, this);
7395             this.store.un('loadexception', this.onLoadException, this);
7396         }else{
7397             var um = this.el.getUpdateManager();
7398             um.un('beforeupdate', this.onBeforeLoad, this);
7399             um.un('update', this.onLoad, this);
7400             um.un('failure', this.onLoad, this);
7401         }
7402     }
7403 };/*
7404  * - LGPL
7405  *
7406  * table
7407  * 
7408  */
7409
7410 /**
7411  * @class Roo.bootstrap.Table
7412  * @extends Roo.bootstrap.Component
7413  * Bootstrap Table class
7414  * @cfg {String} cls table class
7415  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7416  * @cfg {String} bgcolor Specifies the background color for a table
7417  * @cfg {Number} border Specifies whether the table cells should have borders or not
7418  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7419  * @cfg {Number} cellspacing Specifies the space between cells
7420  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7421  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7422  * @cfg {String} sortable Specifies that the table should be sortable
7423  * @cfg {String} summary Specifies a summary of the content of a table
7424  * @cfg {Number} width Specifies the width of a table
7425  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7426  * 
7427  * @cfg {boolean} striped Should the rows be alternative striped
7428  * @cfg {boolean} bordered Add borders to the table
7429  * @cfg {boolean} hover Add hover highlighting
7430  * @cfg {boolean} condensed Format condensed
7431  * @cfg {boolean} responsive Format condensed
7432  * @cfg {Boolean} loadMask (true|false) default false
7433  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7434  * @cfg {Boolean} headerShow (true|false) generate thead, default true
7435  * @cfg {Boolean} rowSelection (true|false) default false
7436  * @cfg {Boolean} cellSelection (true|false) default false
7437  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7438  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
7439  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
7440  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
7441  
7442  * 
7443  * @constructor
7444  * Create a new Table
7445  * @param {Object} config The config object
7446  */
7447
7448 Roo.bootstrap.Table = function(config){
7449     Roo.bootstrap.Table.superclass.constructor.call(this, config);
7450     
7451   
7452     
7453     // BC...
7454     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7455     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7456     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7457     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7458     
7459     this.sm = this.sm || {xtype: 'RowSelectionModel'};
7460     if (this.sm) {
7461         this.sm.grid = this;
7462         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7463         this.sm = this.selModel;
7464         this.sm.xmodule = this.xmodule || false;
7465     }
7466     
7467     if (this.cm && typeof(this.cm.config) == 'undefined') {
7468         this.colModel = new Roo.grid.ColumnModel(this.cm);
7469         this.cm = this.colModel;
7470         this.cm.xmodule = this.xmodule || false;
7471     }
7472     if (this.store) {
7473         this.store= Roo.factory(this.store, Roo.data);
7474         this.ds = this.store;
7475         this.ds.xmodule = this.xmodule || false;
7476          
7477     }
7478     if (this.footer && this.store) {
7479         this.footer.dataSource = this.ds;
7480         this.footer = Roo.factory(this.footer);
7481     }
7482     
7483     /** @private */
7484     this.addEvents({
7485         /**
7486          * @event cellclick
7487          * Fires when a cell is clicked
7488          * @param {Roo.bootstrap.Table} this
7489          * @param {Roo.Element} el
7490          * @param {Number} rowIndex
7491          * @param {Number} columnIndex
7492          * @param {Roo.EventObject} e
7493          */
7494         "cellclick" : true,
7495         /**
7496          * @event celldblclick
7497          * Fires when a cell is double clicked
7498          * @param {Roo.bootstrap.Table} this
7499          * @param {Roo.Element} el
7500          * @param {Number} rowIndex
7501          * @param {Number} columnIndex
7502          * @param {Roo.EventObject} e
7503          */
7504         "celldblclick" : true,
7505         /**
7506          * @event rowclick
7507          * Fires when a row is clicked
7508          * @param {Roo.bootstrap.Table} this
7509          * @param {Roo.Element} el
7510          * @param {Number} rowIndex
7511          * @param {Roo.EventObject} e
7512          */
7513         "rowclick" : true,
7514         /**
7515          * @event rowdblclick
7516          * Fires when a row is double clicked
7517          * @param {Roo.bootstrap.Table} this
7518          * @param {Roo.Element} el
7519          * @param {Number} rowIndex
7520          * @param {Roo.EventObject} e
7521          */
7522         "rowdblclick" : true,
7523         /**
7524          * @event mouseover
7525          * Fires when a mouseover occur
7526          * @param {Roo.bootstrap.Table} this
7527          * @param {Roo.Element} el
7528          * @param {Number} rowIndex
7529          * @param {Number} columnIndex
7530          * @param {Roo.EventObject} e
7531          */
7532         "mouseover" : true,
7533         /**
7534          * @event mouseout
7535          * Fires when a mouseout occur
7536          * @param {Roo.bootstrap.Table} this
7537          * @param {Roo.Element} el
7538          * @param {Number} rowIndex
7539          * @param {Number} columnIndex
7540          * @param {Roo.EventObject} e
7541          */
7542         "mouseout" : true,
7543         /**
7544          * @event rowclass
7545          * Fires when a row is rendered, so you can change add a style to it.
7546          * @param {Roo.bootstrap.Table} this
7547          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
7548          */
7549         'rowclass' : true,
7550           /**
7551          * @event rowsrendered
7552          * Fires when all the  rows have been rendered
7553          * @param {Roo.bootstrap.Table} this
7554          */
7555         'rowsrendered' : true,
7556         /**
7557          * @event contextmenu
7558          * The raw contextmenu event for the entire grid.
7559          * @param {Roo.EventObject} e
7560          */
7561         "contextmenu" : true,
7562         /**
7563          * @event rowcontextmenu
7564          * Fires when a row is right clicked
7565          * @param {Roo.bootstrap.Table} this
7566          * @param {Number} rowIndex
7567          * @param {Roo.EventObject} e
7568          */
7569         "rowcontextmenu" : true,
7570         /**
7571          * @event cellcontextmenu
7572          * Fires when a cell is right clicked
7573          * @param {Roo.bootstrap.Table} this
7574          * @param {Number} rowIndex
7575          * @param {Number} cellIndex
7576          * @param {Roo.EventObject} e
7577          */
7578          "cellcontextmenu" : true,
7579          /**
7580          * @event headercontextmenu
7581          * Fires when a header is right clicked
7582          * @param {Roo.bootstrap.Table} this
7583          * @param {Number} columnIndex
7584          * @param {Roo.EventObject} e
7585          */
7586         "headercontextmenu" : true
7587     });
7588 };
7589
7590 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
7591     
7592     cls: false,
7593     align: false,
7594     bgcolor: false,
7595     border: false,
7596     cellpadding: false,
7597     cellspacing: false,
7598     frame: false,
7599     rules: false,
7600     sortable: false,
7601     summary: false,
7602     width: false,
7603     striped : false,
7604     scrollBody : false,
7605     bordered: false,
7606     hover:  false,
7607     condensed : false,
7608     responsive : false,
7609     sm : false,
7610     cm : false,
7611     store : false,
7612     loadMask : false,
7613     footerShow : true,
7614     headerShow : true,
7615   
7616     rowSelection : false,
7617     cellSelection : false,
7618     layout : false,
7619     
7620     // Roo.Element - the tbody
7621     mainBody: false,
7622     // Roo.Element - thead element
7623     mainHead: false,
7624     
7625     container: false, // used by gridpanel...
7626     
7627     lazyLoad : false,
7628     
7629     CSS : Roo.util.CSS,
7630     
7631     auto_hide_footer : false,
7632     
7633     getAutoCreate : function()
7634     {
7635         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7636         
7637         cfg = {
7638             tag: 'table',
7639             cls : 'table',
7640             cn : []
7641         };
7642         if (this.scrollBody) {
7643             cfg.cls += ' table-body-fixed';
7644         }    
7645         if (this.striped) {
7646             cfg.cls += ' table-striped';
7647         }
7648         
7649         if (this.hover) {
7650             cfg.cls += ' table-hover';
7651         }
7652         if (this.bordered) {
7653             cfg.cls += ' table-bordered';
7654         }
7655         if (this.condensed) {
7656             cfg.cls += ' table-condensed';
7657         }
7658         if (this.responsive) {
7659             cfg.cls += ' table-responsive';
7660         }
7661         
7662         if (this.cls) {
7663             cfg.cls+=  ' ' +this.cls;
7664         }
7665         
7666         // this lot should be simplifed...
7667         var _t = this;
7668         var cp = [
7669             'align',
7670             'bgcolor',
7671             'border',
7672             'cellpadding',
7673             'cellspacing',
7674             'frame',
7675             'rules',
7676             'sortable',
7677             'summary',
7678             'width'
7679         ].forEach(function(k) {
7680             if (_t[k]) {
7681                 cfg[k] = _t[k];
7682             }
7683         });
7684         
7685         
7686         if (this.layout) {
7687             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7688         }
7689         
7690         if(this.store || this.cm){
7691             if(this.headerShow){
7692                 cfg.cn.push(this.renderHeader());
7693             }
7694             
7695             cfg.cn.push(this.renderBody());
7696             
7697             if(this.footerShow){
7698                 cfg.cn.push(this.renderFooter());
7699             }
7700             // where does this come from?
7701             //cfg.cls+=  ' TableGrid';
7702         }
7703         
7704         return { cn : [ cfg ] };
7705     },
7706     
7707     initEvents : function()
7708     {   
7709         if(!this.store || !this.cm){
7710             return;
7711         }
7712         if (this.selModel) {
7713             this.selModel.initEvents();
7714         }
7715         
7716         
7717         //Roo.log('initEvents with ds!!!!');
7718         
7719         this.mainBody = this.el.select('tbody', true).first();
7720         this.mainHead = this.el.select('thead', true).first();
7721         this.mainFoot = this.el.select('tfoot', true).first();
7722         
7723         
7724         
7725         var _this = this;
7726         
7727         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7728             e.on('click', _this.sort, _this);
7729         });
7730         
7731         this.mainBody.on("click", this.onClick, this);
7732         this.mainBody.on("dblclick", this.onDblClick, this);
7733         
7734         // why is this done????? = it breaks dialogs??
7735         //this.parent().el.setStyle('position', 'relative');
7736         
7737         
7738         if (this.footer) {
7739             this.footer.parentId = this.id;
7740             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
7741             
7742             if(this.lazyLoad){
7743                 this.el.select('tfoot tr td').first().addClass('hide');
7744             }
7745         } 
7746         
7747         if(this.loadMask) {
7748             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
7749         }
7750         
7751         this.store.on('load', this.onLoad, this);
7752         this.store.on('beforeload', this.onBeforeLoad, this);
7753         this.store.on('update', this.onUpdate, this);
7754         this.store.on('add', this.onAdd, this);
7755         this.store.on("clear", this.clear, this);
7756         
7757         this.el.on("contextmenu", this.onContextMenu, this);
7758         
7759         this.mainBody.on('scroll', this.onBodyScroll, this);
7760         
7761         this.cm.on("headerchange", this.onHeaderChange, this);
7762         
7763         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
7764         
7765     },
7766     
7767     onContextMenu : function(e, t)
7768     {
7769         this.processEvent("contextmenu", e);
7770     },
7771     
7772     processEvent : function(name, e)
7773     {
7774         if (name != 'touchstart' ) {
7775             this.fireEvent(name, e);    
7776         }
7777         
7778         var t = e.getTarget();
7779         
7780         var cell = Roo.get(t);
7781         
7782         if(!cell){
7783             return;
7784         }
7785         
7786         if(cell.findParent('tfoot', false, true)){
7787             return;
7788         }
7789         
7790         if(cell.findParent('thead', false, true)){
7791             
7792             if(e.getTarget().nodeName.toLowerCase() != 'th'){
7793                 cell = Roo.get(t).findParent('th', false, true);
7794                 if (!cell) {
7795                     Roo.log("failed to find th in thead?");
7796                     Roo.log(e.getTarget());
7797                     return;
7798                 }
7799             }
7800             
7801             var cellIndex = cell.dom.cellIndex;
7802             
7803             var ename = name == 'touchstart' ? 'click' : name;
7804             this.fireEvent("header" + ename, this, cellIndex, e);
7805             
7806             return;
7807         }
7808         
7809         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7810             cell = Roo.get(t).findParent('td', false, true);
7811             if (!cell) {
7812                 Roo.log("failed to find th in tbody?");
7813                 Roo.log(e.getTarget());
7814                 return;
7815             }
7816         }
7817         
7818         var row = cell.findParent('tr', false, true);
7819         var cellIndex = cell.dom.cellIndex;
7820         var rowIndex = row.dom.rowIndex - 1;
7821         
7822         if(row !== false){
7823             
7824             this.fireEvent("row" + name, this, rowIndex, e);
7825             
7826             if(cell !== false){
7827             
7828                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
7829             }
7830         }
7831         
7832     },
7833     
7834     onMouseover : function(e, el)
7835     {
7836         var cell = Roo.get(el);
7837         
7838         if(!cell){
7839             return;
7840         }
7841         
7842         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7843             cell = cell.findParent('td', false, true);
7844         }
7845         
7846         var row = cell.findParent('tr', false, true);
7847         var cellIndex = cell.dom.cellIndex;
7848         var rowIndex = row.dom.rowIndex - 1; // start from 0
7849         
7850         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
7851         
7852     },
7853     
7854     onMouseout : function(e, el)
7855     {
7856         var cell = Roo.get(el);
7857         
7858         if(!cell){
7859             return;
7860         }
7861         
7862         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7863             cell = cell.findParent('td', false, true);
7864         }
7865         
7866         var row = cell.findParent('tr', false, true);
7867         var cellIndex = cell.dom.cellIndex;
7868         var rowIndex = row.dom.rowIndex - 1; // start from 0
7869         
7870         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
7871         
7872     },
7873     
7874     onClick : function(e, el)
7875     {
7876         var cell = Roo.get(el);
7877         
7878         if(!cell || (!this.cellSelection && !this.rowSelection)){
7879             return;
7880         }
7881         
7882         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7883             cell = cell.findParent('td', false, true);
7884         }
7885         
7886         if(!cell || typeof(cell) == 'undefined'){
7887             return;
7888         }
7889         
7890         var row = cell.findParent('tr', false, true);
7891         
7892         if(!row || typeof(row) == 'undefined'){
7893             return;
7894         }
7895         
7896         var cellIndex = cell.dom.cellIndex;
7897         var rowIndex = this.getRowIndex(row);
7898         
7899         // why??? - should these not be based on SelectionModel?
7900         if(this.cellSelection){
7901             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
7902         }
7903         
7904         if(this.rowSelection){
7905             this.fireEvent('rowclick', this, row, rowIndex, e);
7906         }
7907         
7908         
7909     },
7910         
7911     onDblClick : function(e,el)
7912     {
7913         var cell = Roo.get(el);
7914         
7915         if(!cell || (!this.cellSelection && !this.rowSelection)){
7916             return;
7917         }
7918         
7919         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7920             cell = cell.findParent('td', false, true);
7921         }
7922         
7923         if(!cell || typeof(cell) == 'undefined'){
7924             return;
7925         }
7926         
7927         var row = cell.findParent('tr', false, true);
7928         
7929         if(!row || typeof(row) == 'undefined'){
7930             return;
7931         }
7932         
7933         var cellIndex = cell.dom.cellIndex;
7934         var rowIndex = this.getRowIndex(row);
7935         
7936         if(this.cellSelection){
7937             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
7938         }
7939         
7940         if(this.rowSelection){
7941             this.fireEvent('rowdblclick', this, row, rowIndex, e);
7942         }
7943     },
7944     
7945     sort : function(e,el)
7946     {
7947         var col = Roo.get(el);
7948         
7949         if(!col.hasClass('sortable')){
7950             return;
7951         }
7952         
7953         var sort = col.attr('sort');
7954         var dir = 'ASC';
7955         
7956         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
7957             dir = 'DESC';
7958         }
7959         
7960         this.store.sortInfo = {field : sort, direction : dir};
7961         
7962         if (this.footer) {
7963             Roo.log("calling footer first");
7964             this.footer.onClick('first');
7965         } else {
7966         
7967             this.store.load({ params : { start : 0 } });
7968         }
7969     },
7970     
7971     renderHeader : function()
7972     {
7973         var header = {
7974             tag: 'thead',
7975             cn : []
7976         };
7977         
7978         var cm = this.cm;
7979         this.totalWidth = 0;
7980         
7981         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7982             
7983             var config = cm.config[i];
7984             
7985             var c = {
7986                 tag: 'th',
7987                 cls : 'x-hcol-' + i,
7988                 style : '',
7989                 html: cm.getColumnHeader(i)
7990             };
7991             
7992             var hh = '';
7993             
7994             if(typeof(config.sortable) != 'undefined' && config.sortable){
7995                 c.cls = 'sortable';
7996                 c.html = '<i class="glyphicon"></i>' + c.html;
7997             }
7998             
7999             // could use BS4 hidden-..-down 
8000             
8001             if(typeof(config.lgHeader) != 'undefined'){
8002                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8003             }
8004             
8005             if(typeof(config.mdHeader) != 'undefined'){
8006                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8007             }
8008             
8009             if(typeof(config.smHeader) != 'undefined'){
8010                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8011             }
8012             
8013             if(typeof(config.xsHeader) != 'undefined'){
8014                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8015             }
8016             
8017             if(hh.length){
8018                 c.html = hh;
8019             }
8020             
8021             if(typeof(config.tooltip) != 'undefined'){
8022                 c.tooltip = config.tooltip;
8023             }
8024             
8025             if(typeof(config.colspan) != 'undefined'){
8026                 c.colspan = config.colspan;
8027             }
8028             
8029             if(typeof(config.hidden) != 'undefined' && config.hidden){
8030                 c.style += ' display:none;';
8031             }
8032             
8033             if(typeof(config.dataIndex) != 'undefined'){
8034                 c.sort = config.dataIndex;
8035             }
8036             
8037            
8038             
8039             if(typeof(config.align) != 'undefined' && config.align.length){
8040                 c.style += ' text-align:' + config.align + ';';
8041             }
8042             
8043             if(typeof(config.width) != 'undefined'){
8044                 c.style += ' width:' + config.width + 'px;';
8045                 this.totalWidth += config.width;
8046             } else {
8047                 this.totalWidth += 100; // assume minimum of 100 per column?
8048             }
8049             
8050             if(typeof(config.cls) != 'undefined'){
8051                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8052             }
8053             
8054             ['xs','sm','md','lg'].map(function(size){
8055                 
8056                 if(typeof(config[size]) == 'undefined'){
8057                     return;
8058                 }
8059                  
8060                 if (!config[size]) { // 0 = hidden
8061                     // BS 4 '0' is treated as hide that column and below.
8062                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8063                     return;
8064                 }
8065                 
8066                 c.cls += ' col-' + size + '-' + config[size] + (
8067                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8068                 );
8069                 
8070                 
8071             });
8072             
8073             header.cn.push(c)
8074         }
8075         
8076         return header;
8077     },
8078     
8079     renderBody : function()
8080     {
8081         var body = {
8082             tag: 'tbody',
8083             cn : [
8084                 {
8085                     tag: 'tr',
8086                     cn : [
8087                         {
8088                             tag : 'td',
8089                             colspan :  this.cm.getColumnCount()
8090                         }
8091                     ]
8092                 }
8093             ]
8094         };
8095         
8096         return body;
8097     },
8098     
8099     renderFooter : function()
8100     {
8101         var footer = {
8102             tag: 'tfoot',
8103             cn : [
8104                 {
8105                     tag: 'tr',
8106                     cn : [
8107                         {
8108                             tag : 'td',
8109                             colspan :  this.cm.getColumnCount()
8110                         }
8111                     ]
8112                 }
8113             ]
8114         };
8115         
8116         return footer;
8117     },
8118     
8119     
8120     
8121     onLoad : function()
8122     {
8123 //        Roo.log('ds onload');
8124         this.clear();
8125         
8126         var _this = this;
8127         var cm = this.cm;
8128         var ds = this.store;
8129         
8130         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8131             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8132             if (_this.store.sortInfo) {
8133                     
8134                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8135                     e.select('i', true).addClass(['glyphicon-arrow-up']);
8136                 }
8137                 
8138                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8139                     e.select('i', true).addClass(['glyphicon-arrow-down']);
8140                 }
8141             }
8142         });
8143         
8144         var tbody =  this.mainBody;
8145               
8146         if(ds.getCount() > 0){
8147             ds.data.each(function(d,rowIndex){
8148                 var row =  this.renderRow(cm, ds, rowIndex);
8149                 
8150                 tbody.createChild(row);
8151                 
8152                 var _this = this;
8153                 
8154                 if(row.cellObjects.length){
8155                     Roo.each(row.cellObjects, function(r){
8156                         _this.renderCellObject(r);
8157                     })
8158                 }
8159                 
8160             }, this);
8161         }
8162         
8163         var tfoot = this.el.select('tfoot', true).first();
8164         
8165         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8166             
8167             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8168             
8169             var total = this.ds.getTotalCount();
8170             
8171             if(this.footer.pageSize < total){
8172                 this.mainFoot.show();
8173             }
8174         }
8175         
8176         Roo.each(this.el.select('tbody td', true).elements, function(e){
8177             e.on('mouseover', _this.onMouseover, _this);
8178         });
8179         
8180         Roo.each(this.el.select('tbody td', true).elements, function(e){
8181             e.on('mouseout', _this.onMouseout, _this);
8182         });
8183         this.fireEvent('rowsrendered', this);
8184         
8185         this.autoSize();
8186     },
8187     
8188     
8189     onUpdate : function(ds,record)
8190     {
8191         this.refreshRow(record);
8192         this.autoSize();
8193     },
8194     
8195     onRemove : function(ds, record, index, isUpdate){
8196         if(isUpdate !== true){
8197             this.fireEvent("beforerowremoved", this, index, record);
8198         }
8199         var bt = this.mainBody.dom;
8200         
8201         var rows = this.el.select('tbody > tr', true).elements;
8202         
8203         if(typeof(rows[index]) != 'undefined'){
8204             bt.removeChild(rows[index].dom);
8205         }
8206         
8207 //        if(bt.rows[index]){
8208 //            bt.removeChild(bt.rows[index]);
8209 //        }
8210         
8211         if(isUpdate !== true){
8212             //this.stripeRows(index);
8213             //this.syncRowHeights(index, index);
8214             //this.layout();
8215             this.fireEvent("rowremoved", this, index, record);
8216         }
8217     },
8218     
8219     onAdd : function(ds, records, rowIndex)
8220     {
8221         //Roo.log('on Add called');
8222         // - note this does not handle multiple adding very well..
8223         var bt = this.mainBody.dom;
8224         for (var i =0 ; i < records.length;i++) {
8225             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8226             //Roo.log(records[i]);
8227             //Roo.log(this.store.getAt(rowIndex+i));
8228             this.insertRow(this.store, rowIndex + i, false);
8229             return;
8230         }
8231         
8232     },
8233     
8234     
8235     refreshRow : function(record){
8236         var ds = this.store, index;
8237         if(typeof record == 'number'){
8238             index = record;
8239             record = ds.getAt(index);
8240         }else{
8241             index = ds.indexOf(record);
8242         }
8243         this.insertRow(ds, index, true);
8244         this.autoSize();
8245         this.onRemove(ds, record, index+1, true);
8246         this.autoSize();
8247         //this.syncRowHeights(index, index);
8248         //this.layout();
8249         this.fireEvent("rowupdated", this, index, record);
8250     },
8251     
8252     insertRow : function(dm, rowIndex, isUpdate){
8253         
8254         if(!isUpdate){
8255             this.fireEvent("beforerowsinserted", this, rowIndex);
8256         }
8257             //var s = this.getScrollState();
8258         var row = this.renderRow(this.cm, this.store, rowIndex);
8259         // insert before rowIndex..
8260         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8261         
8262         var _this = this;
8263                 
8264         if(row.cellObjects.length){
8265             Roo.each(row.cellObjects, function(r){
8266                 _this.renderCellObject(r);
8267             })
8268         }
8269             
8270         if(!isUpdate){
8271             this.fireEvent("rowsinserted", this, rowIndex);
8272             //this.syncRowHeights(firstRow, lastRow);
8273             //this.stripeRows(firstRow);
8274             //this.layout();
8275         }
8276         
8277     },
8278     
8279     
8280     getRowDom : function(rowIndex)
8281     {
8282         var rows = this.el.select('tbody > tr', true).elements;
8283         
8284         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8285         
8286     },
8287     // returns the object tree for a tr..
8288   
8289     
8290     renderRow : function(cm, ds, rowIndex) 
8291     {
8292         var d = ds.getAt(rowIndex);
8293         
8294         var row = {
8295             tag : 'tr',
8296             cls : 'x-row-' + rowIndex,
8297             cn : []
8298         };
8299             
8300         var cellObjects = [];
8301         
8302         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8303             var config = cm.config[i];
8304             
8305             var renderer = cm.getRenderer(i);
8306             var value = '';
8307             var id = false;
8308             
8309             if(typeof(renderer) !== 'undefined'){
8310                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8311             }
8312             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8313             // and are rendered into the cells after the row is rendered - using the id for the element.
8314             
8315             if(typeof(value) === 'object'){
8316                 id = Roo.id();
8317                 cellObjects.push({
8318                     container : id,
8319                     cfg : value 
8320                 })
8321             }
8322             
8323             var rowcfg = {
8324                 record: d,
8325                 rowIndex : rowIndex,
8326                 colIndex : i,
8327                 rowClass : ''
8328             };
8329
8330             this.fireEvent('rowclass', this, rowcfg);
8331             
8332             var td = {
8333                 tag: 'td',
8334                 cls : rowcfg.rowClass + ' x-col-' + i,
8335                 style: '',
8336                 html: (typeof(value) === 'object') ? '' : value
8337             };
8338             
8339             if (id) {
8340                 td.id = id;
8341             }
8342             
8343             if(typeof(config.colspan) != 'undefined'){
8344                 td.colspan = config.colspan;
8345             }
8346             
8347             if(typeof(config.hidden) != 'undefined' && config.hidden){
8348                 td.style += ' display:none;';
8349             }
8350             
8351             if(typeof(config.align) != 'undefined' && config.align.length){
8352                 td.style += ' text-align:' + config.align + ';';
8353             }
8354             if(typeof(config.valign) != 'undefined' && config.valign.length){
8355                 td.style += ' vertical-align:' + config.valign + ';';
8356             }
8357             
8358             if(typeof(config.width) != 'undefined'){
8359                 td.style += ' width:' +  config.width + 'px;';
8360             }
8361             
8362             if(typeof(config.cursor) != 'undefined'){
8363                 td.style += ' cursor:' +  config.cursor + ';';
8364             }
8365             
8366             if(typeof(config.cls) != 'undefined'){
8367                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8368             }
8369             
8370             ['xs','sm','md','lg'].map(function(size){
8371                 
8372                 if(typeof(config[size]) == 'undefined'){
8373                     return;
8374                 }
8375                 
8376                 
8377                   
8378                 if (!config[size]) { // 0 = hidden
8379                     // BS 4 '0' is treated as hide that column and below.
8380                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8381                     return;
8382                 }
8383                 
8384                 td.cls += ' col-' + size + '-' + config[size] + (
8385                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8386                 );
8387                  
8388
8389             });
8390             
8391             row.cn.push(td);
8392            
8393         }
8394         
8395         row.cellObjects = cellObjects;
8396         
8397         return row;
8398           
8399     },
8400     
8401     
8402     
8403     onBeforeLoad : function()
8404     {
8405         
8406     },
8407      /**
8408      * Remove all rows
8409      */
8410     clear : function()
8411     {
8412         this.el.select('tbody', true).first().dom.innerHTML = '';
8413     },
8414     /**
8415      * Show or hide a row.
8416      * @param {Number} rowIndex to show or hide
8417      * @param {Boolean} state hide
8418      */
8419     setRowVisibility : function(rowIndex, state)
8420     {
8421         var bt = this.mainBody.dom;
8422         
8423         var rows = this.el.select('tbody > tr', true).elements;
8424         
8425         if(typeof(rows[rowIndex]) == 'undefined'){
8426             return;
8427         }
8428         rows[rowIndex].dom.style.display = state ? '' : 'none';
8429     },
8430     
8431     
8432     getSelectionModel : function(){
8433         if(!this.selModel){
8434             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8435         }
8436         return this.selModel;
8437     },
8438     /*
8439      * Render the Roo.bootstrap object from renderder
8440      */
8441     renderCellObject : function(r)
8442     {
8443         var _this = this;
8444         
8445         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8446         
8447         var t = r.cfg.render(r.container);
8448         
8449         if(r.cfg.cn){
8450             Roo.each(r.cfg.cn, function(c){
8451                 var child = {
8452                     container: t.getChildContainer(),
8453                     cfg: c
8454                 };
8455                 _this.renderCellObject(child);
8456             })
8457         }
8458     },
8459     
8460     getRowIndex : function(row)
8461     {
8462         var rowIndex = -1;
8463         
8464         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8465             if(el != row){
8466                 return;
8467             }
8468             
8469             rowIndex = index;
8470         });
8471         
8472         return rowIndex;
8473     },
8474      /**
8475      * Returns the grid's underlying element = used by panel.Grid
8476      * @return {Element} The element
8477      */
8478     getGridEl : function(){
8479         return this.el;
8480     },
8481      /**
8482      * Forces a resize - used by panel.Grid
8483      * @return {Element} The element
8484      */
8485     autoSize : function()
8486     {
8487         //var ctr = Roo.get(this.container.dom.parentElement);
8488         var ctr = Roo.get(this.el.dom);
8489         
8490         var thd = this.getGridEl().select('thead',true).first();
8491         var tbd = this.getGridEl().select('tbody', true).first();
8492         var tfd = this.getGridEl().select('tfoot', true).first();
8493         
8494         var cw = ctr.getWidth();
8495         
8496         if (tbd) {
8497             
8498             tbd.setWidth(ctr.getWidth());
8499             // if the body has a max height - and then scrolls - we should perhaps set up the height here
8500             // this needs fixing for various usage - currently only hydra job advers I think..
8501             //tdb.setHeight(
8502             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8503             //); 
8504             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8505             cw -= barsize;
8506         }
8507         cw = Math.max(cw, this.totalWidth);
8508         this.getGridEl().select('tr',true).setWidth(cw);
8509         // resize 'expandable coloumn?
8510         
8511         return; // we doe not have a view in this design..
8512         
8513     },
8514     onBodyScroll: function()
8515     {
8516         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8517         if(this.mainHead){
8518             this.mainHead.setStyle({
8519                 'position' : 'relative',
8520                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8521             });
8522         }
8523         
8524         if(this.lazyLoad){
8525             
8526             var scrollHeight = this.mainBody.dom.scrollHeight;
8527             
8528             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8529             
8530             var height = this.mainBody.getHeight();
8531             
8532             if(scrollHeight - height == scrollTop) {
8533                 
8534                 var total = this.ds.getTotalCount();
8535                 
8536                 if(this.footer.cursor + this.footer.pageSize < total){
8537                     
8538                     this.footer.ds.load({
8539                         params : {
8540                             start : this.footer.cursor + this.footer.pageSize,
8541                             limit : this.footer.pageSize
8542                         },
8543                         add : true
8544                     });
8545                 }
8546             }
8547             
8548         }
8549     },
8550     
8551     onHeaderChange : function()
8552     {
8553         var header = this.renderHeader();
8554         var table = this.el.select('table', true).first();
8555         
8556         this.mainHead.remove();
8557         this.mainHead = table.createChild(header, this.mainBody, false);
8558     },
8559     
8560     onHiddenChange : function(colModel, colIndex, hidden)
8561     {
8562         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8563         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8564         
8565         this.CSS.updateRule(thSelector, "display", "");
8566         this.CSS.updateRule(tdSelector, "display", "");
8567         
8568         if(hidden){
8569             this.CSS.updateRule(thSelector, "display", "none");
8570             this.CSS.updateRule(tdSelector, "display", "none");
8571         }
8572         
8573         this.onHeaderChange();
8574         this.onLoad();
8575     },
8576     
8577     setColumnWidth: function(col_index, width)
8578     {
8579         // width = "md-2 xs-2..."
8580         if(!this.colModel.config[col_index]) {
8581             return;
8582         }
8583         
8584         var w = width.split(" ");
8585         
8586         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8587         
8588         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8589         
8590         
8591         for(var j = 0; j < w.length; j++) {
8592             
8593             if(!w[j]) {
8594                 continue;
8595             }
8596             
8597             var size_cls = w[j].split("-");
8598             
8599             if(!Number.isInteger(size_cls[1] * 1)) {
8600                 continue;
8601             }
8602             
8603             if(!this.colModel.config[col_index][size_cls[0]]) {
8604                 continue;
8605             }
8606             
8607             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8608                 continue;
8609             }
8610             
8611             h_row[0].classList.replace(
8612                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8613                 "col-"+size_cls[0]+"-"+size_cls[1]
8614             );
8615             
8616             for(var i = 0; i < rows.length; i++) {
8617                 
8618                 var size_cls = w[j].split("-");
8619                 
8620                 if(!Number.isInteger(size_cls[1] * 1)) {
8621                     continue;
8622                 }
8623                 
8624                 if(!this.colModel.config[col_index][size_cls[0]]) {
8625                     continue;
8626                 }
8627                 
8628                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8629                     continue;
8630                 }
8631                 
8632                 rows[i].classList.replace(
8633                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8634                     "col-"+size_cls[0]+"-"+size_cls[1]
8635                 );
8636             }
8637             
8638             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8639         }
8640     }
8641 });
8642
8643  
8644
8645  /*
8646  * - LGPL
8647  *
8648  * table cell
8649  * 
8650  */
8651
8652 /**
8653  * @class Roo.bootstrap.TableCell
8654  * @extends Roo.bootstrap.Component
8655  * Bootstrap TableCell class
8656  * @cfg {String} html cell contain text
8657  * @cfg {String} cls cell class
8658  * @cfg {String} tag cell tag (td|th) default td
8659  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8660  * @cfg {String} align Aligns the content in a cell
8661  * @cfg {String} axis Categorizes cells
8662  * @cfg {String} bgcolor Specifies the background color of a cell
8663  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8664  * @cfg {Number} colspan Specifies the number of columns a cell should span
8665  * @cfg {String} headers Specifies one or more header cells a cell is related to
8666  * @cfg {Number} height Sets the height of a cell
8667  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8668  * @cfg {Number} rowspan Sets the number of rows a cell should span
8669  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8670  * @cfg {String} valign Vertical aligns the content in a cell
8671  * @cfg {Number} width Specifies the width of a cell
8672  * 
8673  * @constructor
8674  * Create a new TableCell
8675  * @param {Object} config The config object
8676  */
8677
8678 Roo.bootstrap.TableCell = function(config){
8679     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8680 };
8681
8682 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
8683     
8684     html: false,
8685     cls: false,
8686     tag: false,
8687     abbr: false,
8688     align: false,
8689     axis: false,
8690     bgcolor: false,
8691     charoff: false,
8692     colspan: false,
8693     headers: false,
8694     height: false,
8695     nowrap: false,
8696     rowspan: false,
8697     scope: false,
8698     valign: false,
8699     width: false,
8700     
8701     
8702     getAutoCreate : function(){
8703         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8704         
8705         cfg = {
8706             tag: 'td'
8707         };
8708         
8709         if(this.tag){
8710             cfg.tag = this.tag;
8711         }
8712         
8713         if (this.html) {
8714             cfg.html=this.html
8715         }
8716         if (this.cls) {
8717             cfg.cls=this.cls
8718         }
8719         if (this.abbr) {
8720             cfg.abbr=this.abbr
8721         }
8722         if (this.align) {
8723             cfg.align=this.align
8724         }
8725         if (this.axis) {
8726             cfg.axis=this.axis
8727         }
8728         if (this.bgcolor) {
8729             cfg.bgcolor=this.bgcolor
8730         }
8731         if (this.charoff) {
8732             cfg.charoff=this.charoff
8733         }
8734         if (this.colspan) {
8735             cfg.colspan=this.colspan
8736         }
8737         if (this.headers) {
8738             cfg.headers=this.headers
8739         }
8740         if (this.height) {
8741             cfg.height=this.height
8742         }
8743         if (this.nowrap) {
8744             cfg.nowrap=this.nowrap
8745         }
8746         if (this.rowspan) {
8747             cfg.rowspan=this.rowspan
8748         }
8749         if (this.scope) {
8750             cfg.scope=this.scope
8751         }
8752         if (this.valign) {
8753             cfg.valign=this.valign
8754         }
8755         if (this.width) {
8756             cfg.width=this.width
8757         }
8758         
8759         
8760         return cfg;
8761     }
8762    
8763 });
8764
8765  
8766
8767  /*
8768  * - LGPL
8769  *
8770  * table row
8771  * 
8772  */
8773
8774 /**
8775  * @class Roo.bootstrap.TableRow
8776  * @extends Roo.bootstrap.Component
8777  * Bootstrap TableRow class
8778  * @cfg {String} cls row class
8779  * @cfg {String} align Aligns the content in a table row
8780  * @cfg {String} bgcolor Specifies a background color for a table row
8781  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8782  * @cfg {String} valign Vertical aligns the content in a table row
8783  * 
8784  * @constructor
8785  * Create a new TableRow
8786  * @param {Object} config The config object
8787  */
8788
8789 Roo.bootstrap.TableRow = function(config){
8790     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
8791 };
8792
8793 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
8794     
8795     cls: false,
8796     align: false,
8797     bgcolor: false,
8798     charoff: false,
8799     valign: false,
8800     
8801     getAutoCreate : function(){
8802         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
8803         
8804         cfg = {
8805             tag: 'tr'
8806         };
8807             
8808         if(this.cls){
8809             cfg.cls = this.cls;
8810         }
8811         if(this.align){
8812             cfg.align = this.align;
8813         }
8814         if(this.bgcolor){
8815             cfg.bgcolor = this.bgcolor;
8816         }
8817         if(this.charoff){
8818             cfg.charoff = this.charoff;
8819         }
8820         if(this.valign){
8821             cfg.valign = this.valign;
8822         }
8823         
8824         return cfg;
8825     }
8826    
8827 });
8828
8829  
8830
8831  /*
8832  * - LGPL
8833  *
8834  * table body
8835  * 
8836  */
8837
8838 /**
8839  * @class Roo.bootstrap.TableBody
8840  * @extends Roo.bootstrap.Component
8841  * Bootstrap TableBody class
8842  * @cfg {String} cls element class
8843  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
8844  * @cfg {String} align Aligns the content inside the element
8845  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
8846  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
8847  * 
8848  * @constructor
8849  * Create a new TableBody
8850  * @param {Object} config The config object
8851  */
8852
8853 Roo.bootstrap.TableBody = function(config){
8854     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
8855 };
8856
8857 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
8858     
8859     cls: false,
8860     tag: false,
8861     align: false,
8862     charoff: false,
8863     valign: false,
8864     
8865     getAutoCreate : function(){
8866         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
8867         
8868         cfg = {
8869             tag: 'tbody'
8870         };
8871             
8872         if (this.cls) {
8873             cfg.cls=this.cls
8874         }
8875         if(this.tag){
8876             cfg.tag = this.tag;
8877         }
8878         
8879         if(this.align){
8880             cfg.align = this.align;
8881         }
8882         if(this.charoff){
8883             cfg.charoff = this.charoff;
8884         }
8885         if(this.valign){
8886             cfg.valign = this.valign;
8887         }
8888         
8889         return cfg;
8890     }
8891     
8892     
8893 //    initEvents : function()
8894 //    {
8895 //        
8896 //        if(!this.store){
8897 //            return;
8898 //        }
8899 //        
8900 //        this.store = Roo.factory(this.store, Roo.data);
8901 //        this.store.on('load', this.onLoad, this);
8902 //        
8903 //        this.store.load();
8904 //        
8905 //    },
8906 //    
8907 //    onLoad: function () 
8908 //    {   
8909 //        this.fireEvent('load', this);
8910 //    }
8911 //    
8912 //   
8913 });
8914
8915  
8916
8917  /*
8918  * Based on:
8919  * Ext JS Library 1.1.1
8920  * Copyright(c) 2006-2007, Ext JS, LLC.
8921  *
8922  * Originally Released Under LGPL - original licence link has changed is not relivant.
8923  *
8924  * Fork - LGPL
8925  * <script type="text/javascript">
8926  */
8927
8928 // as we use this in bootstrap.
8929 Roo.namespace('Roo.form');
8930  /**
8931  * @class Roo.form.Action
8932  * Internal Class used to handle form actions
8933  * @constructor
8934  * @param {Roo.form.BasicForm} el The form element or its id
8935  * @param {Object} config Configuration options
8936  */
8937
8938  
8939  
8940 // define the action interface
8941 Roo.form.Action = function(form, options){
8942     this.form = form;
8943     this.options = options || {};
8944 };
8945 /**
8946  * Client Validation Failed
8947  * @const 
8948  */
8949 Roo.form.Action.CLIENT_INVALID = 'client';
8950 /**
8951  * Server Validation Failed
8952  * @const 
8953  */
8954 Roo.form.Action.SERVER_INVALID = 'server';
8955  /**
8956  * Connect to Server Failed
8957  * @const 
8958  */
8959 Roo.form.Action.CONNECT_FAILURE = 'connect';
8960 /**
8961  * Reading Data from Server Failed
8962  * @const 
8963  */
8964 Roo.form.Action.LOAD_FAILURE = 'load';
8965
8966 Roo.form.Action.prototype = {
8967     type : 'default',
8968     failureType : undefined,
8969     response : undefined,
8970     result : undefined,
8971
8972     // interface method
8973     run : function(options){
8974
8975     },
8976
8977     // interface method
8978     success : function(response){
8979
8980     },
8981
8982     // interface method
8983     handleResponse : function(response){
8984
8985     },
8986
8987     // default connection failure
8988     failure : function(response){
8989         
8990         this.response = response;
8991         this.failureType = Roo.form.Action.CONNECT_FAILURE;
8992         this.form.afterAction(this, false);
8993     },
8994
8995     processResponse : function(response){
8996         this.response = response;
8997         if(!response.responseText){
8998             return true;
8999         }
9000         this.result = this.handleResponse(response);
9001         return this.result;
9002     },
9003
9004     // utility functions used internally
9005     getUrl : function(appendParams){
9006         var url = this.options.url || this.form.url || this.form.el.dom.action;
9007         if(appendParams){
9008             var p = this.getParams();
9009             if(p){
9010                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9011             }
9012         }
9013         return url;
9014     },
9015
9016     getMethod : function(){
9017         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9018     },
9019
9020     getParams : function(){
9021         var bp = this.form.baseParams;
9022         var p = this.options.params;
9023         if(p){
9024             if(typeof p == "object"){
9025                 p = Roo.urlEncode(Roo.applyIf(p, bp));
9026             }else if(typeof p == 'string' && bp){
9027                 p += '&' + Roo.urlEncode(bp);
9028             }
9029         }else if(bp){
9030             p = Roo.urlEncode(bp);
9031         }
9032         return p;
9033     },
9034
9035     createCallback : function(){
9036         return {
9037             success: this.success,
9038             failure: this.failure,
9039             scope: this,
9040             timeout: (this.form.timeout*1000),
9041             upload: this.form.fileUpload ? this.success : undefined
9042         };
9043     }
9044 };
9045
9046 Roo.form.Action.Submit = function(form, options){
9047     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9048 };
9049
9050 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9051     type : 'submit',
9052
9053     haveProgress : false,
9054     uploadComplete : false,
9055     
9056     // uploadProgress indicator.
9057     uploadProgress : function()
9058     {
9059         if (!this.form.progressUrl) {
9060             return;
9061         }
9062         
9063         if (!this.haveProgress) {
9064             Roo.MessageBox.progress("Uploading", "Uploading");
9065         }
9066         if (this.uploadComplete) {
9067            Roo.MessageBox.hide();
9068            return;
9069         }
9070         
9071         this.haveProgress = true;
9072    
9073         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9074         
9075         var c = new Roo.data.Connection();
9076         c.request({
9077             url : this.form.progressUrl,
9078             params: {
9079                 id : uid
9080             },
9081             method: 'GET',
9082             success : function(req){
9083                //console.log(data);
9084                 var rdata = false;
9085                 var edata;
9086                 try  {
9087                    rdata = Roo.decode(req.responseText)
9088                 } catch (e) {
9089                     Roo.log("Invalid data from server..");
9090                     Roo.log(edata);
9091                     return;
9092                 }
9093                 if (!rdata || !rdata.success) {
9094                     Roo.log(rdata);
9095                     Roo.MessageBox.alert(Roo.encode(rdata));
9096                     return;
9097                 }
9098                 var data = rdata.data;
9099                 
9100                 if (this.uploadComplete) {
9101                    Roo.MessageBox.hide();
9102                    return;
9103                 }
9104                    
9105                 if (data){
9106                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9107                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9108                     );
9109                 }
9110                 this.uploadProgress.defer(2000,this);
9111             },
9112        
9113             failure: function(data) {
9114                 Roo.log('progress url failed ');
9115                 Roo.log(data);
9116             },
9117             scope : this
9118         });
9119            
9120     },
9121     
9122     
9123     run : function()
9124     {
9125         // run get Values on the form, so it syncs any secondary forms.
9126         this.form.getValues();
9127         
9128         var o = this.options;
9129         var method = this.getMethod();
9130         var isPost = method == 'POST';
9131         if(o.clientValidation === false || this.form.isValid()){
9132             
9133             if (this.form.progressUrl) {
9134                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9135                     (new Date() * 1) + '' + Math.random());
9136                     
9137             } 
9138             
9139             
9140             Roo.Ajax.request(Roo.apply(this.createCallback(), {
9141                 form:this.form.el.dom,
9142                 url:this.getUrl(!isPost),
9143                 method: method,
9144                 params:isPost ? this.getParams() : null,
9145                 isUpload: this.form.fileUpload,
9146                 formData : this.form.formData
9147             }));
9148             
9149             this.uploadProgress();
9150
9151         }else if (o.clientValidation !== false){ // client validation failed
9152             this.failureType = Roo.form.Action.CLIENT_INVALID;
9153             this.form.afterAction(this, false);
9154         }
9155     },
9156
9157     success : function(response)
9158     {
9159         this.uploadComplete= true;
9160         if (this.haveProgress) {
9161             Roo.MessageBox.hide();
9162         }
9163         
9164         
9165         var result = this.processResponse(response);
9166         if(result === true || result.success){
9167             this.form.afterAction(this, true);
9168             return;
9169         }
9170         if(result.errors){
9171             this.form.markInvalid(result.errors);
9172             this.failureType = Roo.form.Action.SERVER_INVALID;
9173         }
9174         this.form.afterAction(this, false);
9175     },
9176     failure : function(response)
9177     {
9178         this.uploadComplete= true;
9179         if (this.haveProgress) {
9180             Roo.MessageBox.hide();
9181         }
9182         
9183         this.response = response;
9184         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9185         this.form.afterAction(this, false);
9186     },
9187     
9188     handleResponse : function(response){
9189         if(this.form.errorReader){
9190             var rs = this.form.errorReader.read(response);
9191             var errors = [];
9192             if(rs.records){
9193                 for(var i = 0, len = rs.records.length; i < len; i++) {
9194                     var r = rs.records[i];
9195                     errors[i] = r.data;
9196                 }
9197             }
9198             if(errors.length < 1){
9199                 errors = null;
9200             }
9201             return {
9202                 success : rs.success,
9203                 errors : errors
9204             };
9205         }
9206         var ret = false;
9207         try {
9208             ret = Roo.decode(response.responseText);
9209         } catch (e) {
9210             ret = {
9211                 success: false,
9212                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9213                 errors : []
9214             };
9215         }
9216         return ret;
9217         
9218     }
9219 });
9220
9221
9222 Roo.form.Action.Load = function(form, options){
9223     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9224     this.reader = this.form.reader;
9225 };
9226
9227 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9228     type : 'load',
9229
9230     run : function(){
9231         
9232         Roo.Ajax.request(Roo.apply(
9233                 this.createCallback(), {
9234                     method:this.getMethod(),
9235                     url:this.getUrl(false),
9236                     params:this.getParams()
9237         }));
9238     },
9239
9240     success : function(response){
9241         
9242         var result = this.processResponse(response);
9243         if(result === true || !result.success || !result.data){
9244             this.failureType = Roo.form.Action.LOAD_FAILURE;
9245             this.form.afterAction(this, false);
9246             return;
9247         }
9248         this.form.clearInvalid();
9249         this.form.setValues(result.data);
9250         this.form.afterAction(this, true);
9251     },
9252
9253     handleResponse : function(response){
9254         if(this.form.reader){
9255             var rs = this.form.reader.read(response);
9256             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9257             return {
9258                 success : rs.success,
9259                 data : data
9260             };
9261         }
9262         return Roo.decode(response.responseText);
9263     }
9264 });
9265
9266 Roo.form.Action.ACTION_TYPES = {
9267     'load' : Roo.form.Action.Load,
9268     'submit' : Roo.form.Action.Submit
9269 };/*
9270  * - LGPL
9271  *
9272  * form
9273  *
9274  */
9275
9276 /**
9277  * @class Roo.bootstrap.Form
9278  * @extends Roo.bootstrap.Component
9279  * Bootstrap Form class
9280  * @cfg {String} method  GET | POST (default POST)
9281  * @cfg {String} labelAlign top | left (default top)
9282  * @cfg {String} align left  | right - for navbars
9283  * @cfg {Boolean} loadMask load mask when submit (default true)
9284
9285  *
9286  * @constructor
9287  * Create a new Form
9288  * @param {Object} config The config object
9289  */
9290
9291
9292 Roo.bootstrap.Form = function(config){
9293     
9294     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9295     
9296     Roo.bootstrap.Form.popover.apply();
9297     
9298     this.addEvents({
9299         /**
9300          * @event clientvalidation
9301          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9302          * @param {Form} this
9303          * @param {Boolean} valid true if the form has passed client-side validation
9304          */
9305         clientvalidation: true,
9306         /**
9307          * @event beforeaction
9308          * Fires before any action is performed. Return false to cancel the action.
9309          * @param {Form} this
9310          * @param {Action} action The action to be performed
9311          */
9312         beforeaction: true,
9313         /**
9314          * @event actionfailed
9315          * Fires when an action fails.
9316          * @param {Form} this
9317          * @param {Action} action The action that failed
9318          */
9319         actionfailed : true,
9320         /**
9321          * @event actioncomplete
9322          * Fires when an action is completed.
9323          * @param {Form} this
9324          * @param {Action} action The action that completed
9325          */
9326         actioncomplete : true
9327     });
9328 };
9329
9330 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9331
9332      /**
9333      * @cfg {String} method
9334      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9335      */
9336     method : 'POST',
9337     /**
9338      * @cfg {String} url
9339      * The URL to use for form actions if one isn't supplied in the action options.
9340      */
9341     /**
9342      * @cfg {Boolean} fileUpload
9343      * Set to true if this form is a file upload.
9344      */
9345
9346     /**
9347      * @cfg {Object} baseParams
9348      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9349      */
9350
9351     /**
9352      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9353      */
9354     timeout: 30,
9355     /**
9356      * @cfg {Sting} align (left|right) for navbar forms
9357      */
9358     align : 'left',
9359
9360     // private
9361     activeAction : null,
9362
9363     /**
9364      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9365      * element by passing it or its id or mask the form itself by passing in true.
9366      * @type Mixed
9367      */
9368     waitMsgTarget : false,
9369
9370     loadMask : true,
9371     
9372     /**
9373      * @cfg {Boolean} errorMask (true|false) default false
9374      */
9375     errorMask : false,
9376     
9377     /**
9378      * @cfg {Number} maskOffset Default 100
9379      */
9380     maskOffset : 100,
9381     
9382     /**
9383      * @cfg {Boolean} maskBody
9384      */
9385     maskBody : false,
9386
9387     getAutoCreate : function(){
9388
9389         var cfg = {
9390             tag: 'form',
9391             method : this.method || 'POST',
9392             id : this.id || Roo.id(),
9393             cls : ''
9394         };
9395         if (this.parent().xtype.match(/^Nav/)) {
9396             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9397
9398         }
9399
9400         if (this.labelAlign == 'left' ) {
9401             cfg.cls += ' form-horizontal';
9402         }
9403
9404
9405         return cfg;
9406     },
9407     initEvents : function()
9408     {
9409         this.el.on('submit', this.onSubmit, this);
9410         // this was added as random key presses on the form where triggering form submit.
9411         this.el.on('keypress', function(e) {
9412             if (e.getCharCode() != 13) {
9413                 return true;
9414             }
9415             // we might need to allow it for textareas.. and some other items.
9416             // check e.getTarget().
9417
9418             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9419                 return true;
9420             }
9421
9422             Roo.log("keypress blocked");
9423
9424             e.preventDefault();
9425             return false;
9426         });
9427         
9428     },
9429     // private
9430     onSubmit : function(e){
9431         e.stopEvent();
9432     },
9433
9434      /**
9435      * Returns true if client-side validation on the form is successful.
9436      * @return Boolean
9437      */
9438     isValid : function(){
9439         var items = this.getItems();
9440         var valid = true;
9441         var target = false;
9442         
9443         items.each(function(f){
9444             
9445             if(f.validate()){
9446                 return;
9447             }
9448             
9449             Roo.log('invalid field: ' + f.name);
9450             
9451             valid = false;
9452
9453             if(!target && f.el.isVisible(true)){
9454                 target = f;
9455             }
9456            
9457         });
9458         
9459         if(this.errorMask && !valid){
9460             Roo.bootstrap.Form.popover.mask(this, target);
9461         }
9462         
9463         return valid;
9464     },
9465     
9466     /**
9467      * Returns true if any fields in this form have changed since their original load.
9468      * @return Boolean
9469      */
9470     isDirty : function(){
9471         var dirty = false;
9472         var items = this.getItems();
9473         items.each(function(f){
9474            if(f.isDirty()){
9475                dirty = true;
9476                return false;
9477            }
9478            return true;
9479         });
9480         return dirty;
9481     },
9482      /**
9483      * Performs a predefined action (submit or load) or custom actions you define on this form.
9484      * @param {String} actionName The name of the action type
9485      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
9486      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9487      * accept other config options):
9488      * <pre>
9489 Property          Type             Description
9490 ----------------  ---------------  ----------------------------------------------------------------------------------
9491 url               String           The url for the action (defaults to the form's url)
9492 method            String           The form method to use (defaults to the form's method, or POST if not defined)
9493 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
9494 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
9495                                    validate the form on the client (defaults to false)
9496      * </pre>
9497      * @return {BasicForm} this
9498      */
9499     doAction : function(action, options){
9500         if(typeof action == 'string'){
9501             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9502         }
9503         if(this.fireEvent('beforeaction', this, action) !== false){
9504             this.beforeAction(action);
9505             action.run.defer(100, action);
9506         }
9507         return this;
9508     },
9509
9510     // private
9511     beforeAction : function(action){
9512         var o = action.options;
9513         
9514         if(this.loadMask){
9515             
9516             if(this.maskBody){
9517                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9518             } else {
9519                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9520             }
9521         }
9522         // not really supported yet.. ??
9523
9524         //if(this.waitMsgTarget === true){
9525         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9526         //}else if(this.waitMsgTarget){
9527         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9528         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9529         //}else {
9530         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9531        // }
9532
9533     },
9534
9535     // private
9536     afterAction : function(action, success){
9537         this.activeAction = null;
9538         var o = action.options;
9539
9540         if(this.loadMask){
9541             
9542             if(this.maskBody){
9543                 Roo.get(document.body).unmask();
9544             } else {
9545                 this.el.unmask();
9546             }
9547         }
9548         
9549         //if(this.waitMsgTarget === true){
9550 //            this.el.unmask();
9551         //}else if(this.waitMsgTarget){
9552         //    this.waitMsgTarget.unmask();
9553         //}else{
9554         //    Roo.MessageBox.updateProgress(1);
9555         //    Roo.MessageBox.hide();
9556        // }
9557         //
9558         if(success){
9559             if(o.reset){
9560                 this.reset();
9561             }
9562             Roo.callback(o.success, o.scope, [this, action]);
9563             this.fireEvent('actioncomplete', this, action);
9564
9565         }else{
9566
9567             // failure condition..
9568             // we have a scenario where updates need confirming.
9569             // eg. if a locking scenario exists..
9570             // we look for { errors : { needs_confirm : true }} in the response.
9571             if (
9572                 (typeof(action.result) != 'undefined')  &&
9573                 (typeof(action.result.errors) != 'undefined')  &&
9574                 (typeof(action.result.errors.needs_confirm) != 'undefined')
9575            ){
9576                 var _t = this;
9577                 Roo.log("not supported yet");
9578                  /*
9579
9580                 Roo.MessageBox.confirm(
9581                     "Change requires confirmation",
9582                     action.result.errorMsg,
9583                     function(r) {
9584                         if (r != 'yes') {
9585                             return;
9586                         }
9587                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
9588                     }
9589
9590                 );
9591                 */
9592
9593
9594                 return;
9595             }
9596
9597             Roo.callback(o.failure, o.scope, [this, action]);
9598             // show an error message if no failed handler is set..
9599             if (!this.hasListener('actionfailed')) {
9600                 Roo.log("need to add dialog support");
9601                 /*
9602                 Roo.MessageBox.alert("Error",
9603                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9604                         action.result.errorMsg :
9605                         "Saving Failed, please check your entries or try again"
9606                 );
9607                 */
9608             }
9609
9610             this.fireEvent('actionfailed', this, action);
9611         }
9612
9613     },
9614     /**
9615      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9616      * @param {String} id The value to search for
9617      * @return Field
9618      */
9619     findField : function(id){
9620         var items = this.getItems();
9621         var field = items.get(id);
9622         if(!field){
9623              items.each(function(f){
9624                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9625                     field = f;
9626                     return false;
9627                 }
9628                 return true;
9629             });
9630         }
9631         return field || null;
9632     },
9633      /**
9634      * Mark fields in this form invalid in bulk.
9635      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9636      * @return {BasicForm} this
9637      */
9638     markInvalid : function(errors){
9639         if(errors instanceof Array){
9640             for(var i = 0, len = errors.length; i < len; i++){
9641                 var fieldError = errors[i];
9642                 var f = this.findField(fieldError.id);
9643                 if(f){
9644                     f.markInvalid(fieldError.msg);
9645                 }
9646             }
9647         }else{
9648             var field, id;
9649             for(id in errors){
9650                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9651                     field.markInvalid(errors[id]);
9652                 }
9653             }
9654         }
9655         //Roo.each(this.childForms || [], function (f) {
9656         //    f.markInvalid(errors);
9657         //});
9658
9659         return this;
9660     },
9661
9662     /**
9663      * Set values for fields in this form in bulk.
9664      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9665      * @return {BasicForm} this
9666      */
9667     setValues : function(values){
9668         if(values instanceof Array){ // array of objects
9669             for(var i = 0, len = values.length; i < len; i++){
9670                 var v = values[i];
9671                 var f = this.findField(v.id);
9672                 if(f){
9673                     f.setValue(v.value);
9674                     if(this.trackResetOnLoad){
9675                         f.originalValue = f.getValue();
9676                     }
9677                 }
9678             }
9679         }else{ // object hash
9680             var field, id;
9681             for(id in values){
9682                 if(typeof values[id] != 'function' && (field = this.findField(id))){
9683
9684                     if (field.setFromData &&
9685                         field.valueField &&
9686                         field.displayField &&
9687                         // combos' with local stores can
9688                         // be queried via setValue()
9689                         // to set their value..
9690                         (field.store && !field.store.isLocal)
9691                         ) {
9692                         // it's a combo
9693                         var sd = { };
9694                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9695                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9696                         field.setFromData(sd);
9697
9698                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9699                         
9700                         field.setFromData(values);
9701                         
9702                     } else {
9703                         field.setValue(values[id]);
9704                     }
9705
9706
9707                     if(this.trackResetOnLoad){
9708                         field.originalValue = field.getValue();
9709                     }
9710                 }
9711             }
9712         }
9713
9714         //Roo.each(this.childForms || [], function (f) {
9715         //    f.setValues(values);
9716         //});
9717
9718         return this;
9719     },
9720
9721     /**
9722      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9723      * they are returned as an array.
9724      * @param {Boolean} asString
9725      * @return {Object}
9726      */
9727     getValues : function(asString){
9728         //if (this.childForms) {
9729             // copy values from the child forms
9730         //    Roo.each(this.childForms, function (f) {
9731         //        this.setValues(f.getValues());
9732         //    }, this);
9733         //}
9734
9735
9736
9737         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
9738         if(asString === true){
9739             return fs;
9740         }
9741         return Roo.urlDecode(fs);
9742     },
9743
9744     /**
9745      * Returns the fields in this form as an object with key/value pairs.
9746      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
9747      * @return {Object}
9748      */
9749     getFieldValues : function(with_hidden)
9750     {
9751         var items = this.getItems();
9752         var ret = {};
9753         items.each(function(f){
9754             
9755             if (!f.getName()) {
9756                 return;
9757             }
9758             
9759             var v = f.getValue();
9760             
9761             if (f.inputType =='radio') {
9762                 if (typeof(ret[f.getName()]) == 'undefined') {
9763                     ret[f.getName()] = ''; // empty..
9764                 }
9765
9766                 if (!f.el.dom.checked) {
9767                     return;
9768
9769                 }
9770                 v = f.el.dom.value;
9771
9772             }
9773             
9774             if(f.xtype == 'MoneyField'){
9775                 ret[f.currencyName] = f.getCurrency();
9776             }
9777
9778             // not sure if this supported any more..
9779             if ((typeof(v) == 'object') && f.getRawValue) {
9780                 v = f.getRawValue() ; // dates..
9781             }
9782             // combo boxes where name != hiddenName...
9783             if (f.name !== false && f.name != '' && f.name != f.getName()) {
9784                 ret[f.name] = f.getRawValue();
9785             }
9786             ret[f.getName()] = v;
9787         });
9788
9789         return ret;
9790     },
9791
9792     /**
9793      * Clears all invalid messages in this form.
9794      * @return {BasicForm} this
9795      */
9796     clearInvalid : function(){
9797         var items = this.getItems();
9798
9799         items.each(function(f){
9800            f.clearInvalid();
9801         });
9802
9803         return this;
9804     },
9805
9806     /**
9807      * Resets this form.
9808      * @return {BasicForm} this
9809      */
9810     reset : function(){
9811         var items = this.getItems();
9812         items.each(function(f){
9813             f.reset();
9814         });
9815
9816         Roo.each(this.childForms || [], function (f) {
9817             f.reset();
9818         });
9819
9820
9821         return this;
9822     },
9823     
9824     getItems : function()
9825     {
9826         var r=new Roo.util.MixedCollection(false, function(o){
9827             return o.id || (o.id = Roo.id());
9828         });
9829         var iter = function(el) {
9830             if (el.inputEl) {
9831                 r.add(el);
9832             }
9833             if (!el.items) {
9834                 return;
9835             }
9836             Roo.each(el.items,function(e) {
9837                 iter(e);
9838             });
9839         };
9840
9841         iter(this);
9842         return r;
9843     },
9844     
9845     hideFields : function(items)
9846     {
9847         Roo.each(items, function(i){
9848             
9849             var f = this.findField(i);
9850             
9851             if(!f){
9852                 return;
9853             }
9854             
9855             f.hide();
9856             
9857         }, this);
9858     },
9859     
9860     showFields : function(items)
9861     {
9862         Roo.each(items, function(i){
9863             
9864             var f = this.findField(i);
9865             
9866             if(!f){
9867                 return;
9868             }
9869             
9870             f.show();
9871             
9872         }, this);
9873     }
9874
9875 });
9876
9877 Roo.apply(Roo.bootstrap.Form, {
9878     
9879     popover : {
9880         
9881         padding : 5,
9882         
9883         isApplied : false,
9884         
9885         isMasked : false,
9886         
9887         form : false,
9888         
9889         target : false,
9890         
9891         toolTip : false,
9892         
9893         intervalID : false,
9894         
9895         maskEl : false,
9896         
9897         apply : function()
9898         {
9899             if(this.isApplied){
9900                 return;
9901             }
9902             
9903             this.maskEl = {
9904                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
9905                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
9906                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
9907                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
9908             };
9909             
9910             this.maskEl.top.enableDisplayMode("block");
9911             this.maskEl.left.enableDisplayMode("block");
9912             this.maskEl.bottom.enableDisplayMode("block");
9913             this.maskEl.right.enableDisplayMode("block");
9914             
9915             this.toolTip = new Roo.bootstrap.Tooltip({
9916                 cls : 'roo-form-error-popover',
9917                 alignment : {
9918                     'left' : ['r-l', [-2,0], 'right'],
9919                     'right' : ['l-r', [2,0], 'left'],
9920                     'bottom' : ['tl-bl', [0,2], 'top'],
9921                     'top' : [ 'bl-tl', [0,-2], 'bottom']
9922                 }
9923             });
9924             
9925             this.toolTip.render(Roo.get(document.body));
9926
9927             this.toolTip.el.enableDisplayMode("block");
9928             
9929             Roo.get(document.body).on('click', function(){
9930                 this.unmask();
9931             }, this);
9932             
9933             Roo.get(document.body).on('touchstart', function(){
9934                 this.unmask();
9935             }, this);
9936             
9937             this.isApplied = true
9938         },
9939         
9940         mask : function(form, target)
9941         {
9942             this.form = form;
9943             
9944             this.target = target;
9945             
9946             if(!this.form.errorMask || !target.el){
9947                 return;
9948             }
9949             
9950             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
9951             
9952             Roo.log(scrollable);
9953             
9954             var ot = this.target.el.calcOffsetsTo(scrollable);
9955             
9956             var scrollTo = ot[1] - this.form.maskOffset;
9957             
9958             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
9959             
9960             scrollable.scrollTo('top', scrollTo);
9961             
9962             var box = this.target.el.getBox();
9963             Roo.log(box);
9964             var zIndex = Roo.bootstrap.Modal.zIndex++;
9965
9966             
9967             this.maskEl.top.setStyle('position', 'absolute');
9968             this.maskEl.top.setStyle('z-index', zIndex);
9969             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
9970             this.maskEl.top.setLeft(0);
9971             this.maskEl.top.setTop(0);
9972             this.maskEl.top.show();
9973             
9974             this.maskEl.left.setStyle('position', 'absolute');
9975             this.maskEl.left.setStyle('z-index', zIndex);
9976             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
9977             this.maskEl.left.setLeft(0);
9978             this.maskEl.left.setTop(box.y - this.padding);
9979             this.maskEl.left.show();
9980
9981             this.maskEl.bottom.setStyle('position', 'absolute');
9982             this.maskEl.bottom.setStyle('z-index', zIndex);
9983             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
9984             this.maskEl.bottom.setLeft(0);
9985             this.maskEl.bottom.setTop(box.bottom + this.padding);
9986             this.maskEl.bottom.show();
9987
9988             this.maskEl.right.setStyle('position', 'absolute');
9989             this.maskEl.right.setStyle('z-index', zIndex);
9990             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
9991             this.maskEl.right.setLeft(box.right + this.padding);
9992             this.maskEl.right.setTop(box.y - this.padding);
9993             this.maskEl.right.show();
9994
9995             this.toolTip.bindEl = this.target.el;
9996
9997             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
9998
9999             var tip = this.target.blankText;
10000
10001             if(this.target.getValue() !== '' ) {
10002                 
10003                 if (this.target.invalidText.length) {
10004                     tip = this.target.invalidText;
10005                 } else if (this.target.regexText.length){
10006                     tip = this.target.regexText;
10007                 }
10008             }
10009
10010             this.toolTip.show(tip);
10011
10012             this.intervalID = window.setInterval(function() {
10013                 Roo.bootstrap.Form.popover.unmask();
10014             }, 10000);
10015
10016             window.onwheel = function(){ return false;};
10017             
10018             (function(){ this.isMasked = true; }).defer(500, this);
10019             
10020         },
10021         
10022         unmask : function()
10023         {
10024             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10025                 return;
10026             }
10027             
10028             this.maskEl.top.setStyle('position', 'absolute');
10029             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10030             this.maskEl.top.hide();
10031
10032             this.maskEl.left.setStyle('position', 'absolute');
10033             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10034             this.maskEl.left.hide();
10035
10036             this.maskEl.bottom.setStyle('position', 'absolute');
10037             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10038             this.maskEl.bottom.hide();
10039
10040             this.maskEl.right.setStyle('position', 'absolute');
10041             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10042             this.maskEl.right.hide();
10043             
10044             this.toolTip.hide();
10045             
10046             this.toolTip.el.hide();
10047             
10048             window.onwheel = function(){ return true;};
10049             
10050             if(this.intervalID){
10051                 window.clearInterval(this.intervalID);
10052                 this.intervalID = false;
10053             }
10054             
10055             this.isMasked = false;
10056             
10057         }
10058         
10059     }
10060     
10061 });
10062
10063 /*
10064  * Based on:
10065  * Ext JS Library 1.1.1
10066  * Copyright(c) 2006-2007, Ext JS, LLC.
10067  *
10068  * Originally Released Under LGPL - original licence link has changed is not relivant.
10069  *
10070  * Fork - LGPL
10071  * <script type="text/javascript">
10072  */
10073 /**
10074  * @class Roo.form.VTypes
10075  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10076  * @singleton
10077  */
10078 Roo.form.VTypes = function(){
10079     // closure these in so they are only created once.
10080     var alpha = /^[a-zA-Z_]+$/;
10081     var alphanum = /^[a-zA-Z0-9_]+$/;
10082     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10083     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10084
10085     // All these messages and functions are configurable
10086     return {
10087         /**
10088          * The function used to validate email addresses
10089          * @param {String} value The email address
10090          */
10091         'email' : function(v){
10092             return email.test(v);
10093         },
10094         /**
10095          * The error text to display when the email validation function returns false
10096          * @type String
10097          */
10098         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10099         /**
10100          * The keystroke filter mask to be applied on email input
10101          * @type RegExp
10102          */
10103         'emailMask' : /[a-z0-9_\.\-@]/i,
10104
10105         /**
10106          * The function used to validate URLs
10107          * @param {String} value The URL
10108          */
10109         'url' : function(v){
10110             return url.test(v);
10111         },
10112         /**
10113          * The error text to display when the url validation function returns false
10114          * @type String
10115          */
10116         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10117         
10118         /**
10119          * The function used to validate alpha values
10120          * @param {String} value The value
10121          */
10122         'alpha' : function(v){
10123             return alpha.test(v);
10124         },
10125         /**
10126          * The error text to display when the alpha validation function returns false
10127          * @type String
10128          */
10129         'alphaText' : 'This field should only contain letters and _',
10130         /**
10131          * The keystroke filter mask to be applied on alpha input
10132          * @type RegExp
10133          */
10134         'alphaMask' : /[a-z_]/i,
10135
10136         /**
10137          * The function used to validate alphanumeric values
10138          * @param {String} value The value
10139          */
10140         'alphanum' : function(v){
10141             return alphanum.test(v);
10142         },
10143         /**
10144          * The error text to display when the alphanumeric validation function returns false
10145          * @type String
10146          */
10147         'alphanumText' : 'This field should only contain letters, numbers and _',
10148         /**
10149          * The keystroke filter mask to be applied on alphanumeric input
10150          * @type RegExp
10151          */
10152         'alphanumMask' : /[a-z0-9_]/i
10153     };
10154 }();/*
10155  * - LGPL
10156  *
10157  * Input
10158  * 
10159  */
10160
10161 /**
10162  * @class Roo.bootstrap.Input
10163  * @extends Roo.bootstrap.Component
10164  * Bootstrap Input class
10165  * @cfg {Boolean} disabled is it disabled
10166  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
10167  * @cfg {String} name name of the input
10168  * @cfg {string} fieldLabel - the label associated
10169  * @cfg {string} placeholder - placeholder to put in text.
10170  * @cfg {string}  before - input group add on before
10171  * @cfg {string} after - input group add on after
10172  * @cfg {string} size - (lg|sm) or leave empty..
10173  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10174  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10175  * @cfg {Number} md colspan out of 12 for computer-sized screens
10176  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10177  * @cfg {string} value default value of the input
10178  * @cfg {Number} labelWidth set the width of label 
10179  * @cfg {Number} labellg set the width of label (1-12)
10180  * @cfg {Number} labelmd set the width of label (1-12)
10181  * @cfg {Number} labelsm set the width of label (1-12)
10182  * @cfg {Number} labelxs set the width of label (1-12)
10183  * @cfg {String} labelAlign (top|left)
10184  * @cfg {Boolean} readOnly Specifies that the field should be read-only
10185  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10186  * @cfg {String} indicatorpos (left|right) default left
10187  * @cfg {String} capture (user|camera) use for file input only. (default empty)
10188  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10189  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10190
10191  * @cfg {String} align (left|center|right) Default left
10192  * @cfg {Boolean} forceFeedback (true|false) Default false
10193  * 
10194  * @constructor
10195  * Create a new Input
10196  * @param {Object} config The config object
10197  */
10198
10199 Roo.bootstrap.Input = function(config){
10200     
10201     Roo.bootstrap.Input.superclass.constructor.call(this, config);
10202     
10203     this.addEvents({
10204         /**
10205          * @event focus
10206          * Fires when this field receives input focus.
10207          * @param {Roo.form.Field} this
10208          */
10209         focus : true,
10210         /**
10211          * @event blur
10212          * Fires when this field loses input focus.
10213          * @param {Roo.form.Field} this
10214          */
10215         blur : true,
10216         /**
10217          * @event specialkey
10218          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
10219          * {@link Roo.EventObject#getKey} to determine which key was pressed.
10220          * @param {Roo.form.Field} this
10221          * @param {Roo.EventObject} e The event object
10222          */
10223         specialkey : true,
10224         /**
10225          * @event change
10226          * Fires just before the field blurs if the field value has changed.
10227          * @param {Roo.form.Field} this
10228          * @param {Mixed} newValue The new value
10229          * @param {Mixed} oldValue The original value
10230          */
10231         change : true,
10232         /**
10233          * @event invalid
10234          * Fires after the field has been marked as invalid.
10235          * @param {Roo.form.Field} this
10236          * @param {String} msg The validation message
10237          */
10238         invalid : true,
10239         /**
10240          * @event valid
10241          * Fires after the field has been validated with no errors.
10242          * @param {Roo.form.Field} this
10243          */
10244         valid : true,
10245          /**
10246          * @event keyup
10247          * Fires after the key up
10248          * @param {Roo.form.Field} this
10249          * @param {Roo.EventObject}  e The event Object
10250          */
10251         keyup : true
10252     });
10253 };
10254
10255 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10256      /**
10257      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10258       automatic validation (defaults to "keyup").
10259      */
10260     validationEvent : "keyup",
10261      /**
10262      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10263      */
10264     validateOnBlur : true,
10265     /**
10266      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10267      */
10268     validationDelay : 250,
10269      /**
10270      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10271      */
10272     focusClass : "x-form-focus",  // not needed???
10273     
10274        
10275     /**
10276      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10277      */
10278     invalidClass : "has-warning",
10279     
10280     /**
10281      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10282      */
10283     validClass : "has-success",
10284     
10285     /**
10286      * @cfg {Boolean} hasFeedback (true|false) default true
10287      */
10288     hasFeedback : true,
10289     
10290     /**
10291      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10292      */
10293     invalidFeedbackClass : "glyphicon-warning-sign",
10294     
10295     /**
10296      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10297      */
10298     validFeedbackClass : "glyphicon-ok",
10299     
10300     /**
10301      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10302      */
10303     selectOnFocus : false,
10304     
10305      /**
10306      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10307      */
10308     maskRe : null,
10309        /**
10310      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10311      */
10312     vtype : null,
10313     
10314       /**
10315      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10316      */
10317     disableKeyFilter : false,
10318     
10319        /**
10320      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10321      */
10322     disabled : false,
10323      /**
10324      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10325      */
10326     allowBlank : true,
10327     /**
10328      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10329      */
10330     blankText : "Please complete this mandatory field",
10331     
10332      /**
10333      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10334      */
10335     minLength : 0,
10336     /**
10337      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10338      */
10339     maxLength : Number.MAX_VALUE,
10340     /**
10341      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10342      */
10343     minLengthText : "The minimum length for this field is {0}",
10344     /**
10345      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10346      */
10347     maxLengthText : "The maximum length for this field is {0}",
10348   
10349     
10350     /**
10351      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10352      * If available, this function will be called only after the basic validators all return true, and will be passed the
10353      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10354      */
10355     validator : null,
10356     /**
10357      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10358      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10359      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10360      */
10361     regex : null,
10362     /**
10363      * @cfg {String} regexText -- Depricated - use Invalid Text
10364      */
10365     regexText : "",
10366     
10367     /**
10368      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10369      */
10370     invalidText : "",
10371     
10372     
10373     
10374     autocomplete: false,
10375     
10376     
10377     fieldLabel : '',
10378     inputType : 'text',
10379     
10380     name : false,
10381     placeholder: false,
10382     before : false,
10383     after : false,
10384     size : false,
10385     hasFocus : false,
10386     preventMark: false,
10387     isFormField : true,
10388     value : '',
10389     labelWidth : 2,
10390     labelAlign : false,
10391     readOnly : false,
10392     align : false,
10393     formatedValue : false,
10394     forceFeedback : false,
10395     
10396     indicatorpos : 'left',
10397     
10398     labellg : 0,
10399     labelmd : 0,
10400     labelsm : 0,
10401     labelxs : 0,
10402     
10403     capture : '',
10404     accept : '',
10405     
10406     parentLabelAlign : function()
10407     {
10408         var parent = this;
10409         while (parent.parent()) {
10410             parent = parent.parent();
10411             if (typeof(parent.labelAlign) !='undefined') {
10412                 return parent.labelAlign;
10413             }
10414         }
10415         return 'left';
10416         
10417     },
10418     
10419     getAutoCreate : function()
10420     {
10421         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10422         
10423         var id = Roo.id();
10424         
10425         var cfg = {};
10426         
10427         if(this.inputType != 'hidden'){
10428             cfg.cls = 'form-group' //input-group
10429         }
10430         
10431         var input =  {
10432             tag: 'input',
10433             id : id,
10434             type : this.inputType,
10435             value : this.value,
10436             cls : 'form-control',
10437             placeholder : this.placeholder || '',
10438             autocomplete : this.autocomplete || 'new-password'
10439         };
10440         
10441         if(this.capture.length){
10442             input.capture = this.capture;
10443         }
10444         
10445         if(this.accept.length){
10446             input.accept = this.accept + "/*";
10447         }
10448         
10449         if(this.align){
10450             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10451         }
10452         
10453         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10454             input.maxLength = this.maxLength;
10455         }
10456         
10457         if (this.disabled) {
10458             input.disabled=true;
10459         }
10460         
10461         if (this.readOnly) {
10462             input.readonly=true;
10463         }
10464         
10465         if (this.name) {
10466             input.name = this.name;
10467         }
10468         
10469         if (this.size) {
10470             input.cls += ' input-' + this.size;
10471         }
10472         
10473         var settings=this;
10474         ['xs','sm','md','lg'].map(function(size){
10475             if (settings[size]) {
10476                 cfg.cls += ' col-' + size + '-' + settings[size];
10477             }
10478         });
10479         
10480         var inputblock = input;
10481         
10482         var feedback = {
10483             tag: 'span',
10484             cls: 'glyphicon form-control-feedback'
10485         };
10486             
10487         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10488             
10489             inputblock = {
10490                 cls : 'has-feedback',
10491                 cn :  [
10492                     input,
10493                     feedback
10494                 ] 
10495             };  
10496         }
10497         
10498         if (this.before || this.after) {
10499             
10500             inputblock = {
10501                 cls : 'input-group',
10502                 cn :  [] 
10503             };
10504             
10505             if (this.before && typeof(this.before) == 'string') {
10506                 
10507                 inputblock.cn.push({
10508                     tag :'span',
10509                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10510                     html : this.before
10511                 });
10512             }
10513             if (this.before && typeof(this.before) == 'object') {
10514                 this.before = Roo.factory(this.before);
10515                 
10516                 inputblock.cn.push({
10517                     tag :'span',
10518                     cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
10519                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10520                 });
10521             }
10522             
10523             inputblock.cn.push(input);
10524             
10525             if (this.after && typeof(this.after) == 'string') {
10526                 inputblock.cn.push({
10527                     tag :'span',
10528                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10529                     html : this.after
10530                 });
10531             }
10532             if (this.after && typeof(this.after) == 'object') {
10533                 this.after = Roo.factory(this.after);
10534                 
10535                 inputblock.cn.push({
10536                     tag :'span',
10537                     cls : 'roo-input-after input-group-append input-group-text input-group-' +
10538                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10539                 });
10540             }
10541             
10542             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10543                 inputblock.cls += ' has-feedback';
10544                 inputblock.cn.push(feedback);
10545             }
10546         };
10547         var indicator = {
10548             tag : 'i',
10549             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10550             tooltip : 'This field is required'
10551         };
10552         if (Roo.bootstrap.version == 4) {
10553             indicator = {
10554                 tag : 'i',
10555                 style : 'display-none'
10556             };
10557         }
10558         if (align ==='left' && this.fieldLabel.length) {
10559             
10560             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10561             
10562             cfg.cn = [
10563                 indicator,
10564                 {
10565                     tag: 'label',
10566                     'for' :  id,
10567                     cls : 'control-label col-form-label',
10568                     html : this.fieldLabel
10569
10570                 },
10571                 {
10572                     cls : "", 
10573                     cn: [
10574                         inputblock
10575                     ]
10576                 }
10577             ];
10578             
10579             var labelCfg = cfg.cn[1];
10580             var contentCfg = cfg.cn[2];
10581             
10582             if(this.indicatorpos == 'right'){
10583                 cfg.cn = [
10584                     {
10585                         tag: 'label',
10586                         'for' :  id,
10587                         cls : 'control-label col-form-label',
10588                         cn : [
10589                             {
10590                                 tag : 'span',
10591                                 html : this.fieldLabel
10592                             },
10593                             indicator
10594                         ]
10595                     },
10596                     {
10597                         cls : "",
10598                         cn: [
10599                             inputblock
10600                         ]
10601                     }
10602
10603                 ];
10604                 
10605                 labelCfg = cfg.cn[0];
10606                 contentCfg = cfg.cn[1];
10607             
10608             }
10609             
10610             if(this.labelWidth > 12){
10611                 labelCfg.style = "width: " + this.labelWidth + 'px';
10612             }
10613             
10614             if(this.labelWidth < 13 && this.labelmd == 0){
10615                 this.labelmd = this.labelWidth;
10616             }
10617             
10618             if(this.labellg > 0){
10619                 labelCfg.cls += ' col-lg-' + this.labellg;
10620                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10621             }
10622             
10623             if(this.labelmd > 0){
10624                 labelCfg.cls += ' col-md-' + this.labelmd;
10625                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10626             }
10627             
10628             if(this.labelsm > 0){
10629                 labelCfg.cls += ' col-sm-' + this.labelsm;
10630                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10631             }
10632             
10633             if(this.labelxs > 0){
10634                 labelCfg.cls += ' col-xs-' + this.labelxs;
10635                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10636             }
10637             
10638             
10639         } else if ( this.fieldLabel.length) {
10640                 
10641             cfg.cn = [
10642                 {
10643                     tag : 'i',
10644                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10645                     tooltip : 'This field is required'
10646                 },
10647                 {
10648                     tag: 'label',
10649                    //cls : 'input-group-addon',
10650                     html : this.fieldLabel
10651
10652                 },
10653
10654                inputblock
10655
10656            ];
10657            
10658            if(this.indicatorpos == 'right'){
10659                 
10660                 cfg.cn = [
10661                     {
10662                         tag: 'label',
10663                        //cls : 'input-group-addon',
10664                         html : this.fieldLabel
10665
10666                     },
10667                     {
10668                         tag : 'i',
10669                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10670                         tooltip : 'This field is required'
10671                     },
10672
10673                    inputblock
10674
10675                ];
10676
10677             }
10678
10679         } else {
10680             
10681             cfg.cn = [
10682
10683                     inputblock
10684
10685             ];
10686                 
10687                 
10688         };
10689         
10690         if (this.parentType === 'Navbar' &&  this.parent().bar) {
10691            cfg.cls += ' navbar-form';
10692         }
10693         
10694         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10695             // on BS4 we do this only if not form 
10696             cfg.cls += ' navbar-form';
10697             cfg.tag = 'li';
10698         }
10699         
10700         return cfg;
10701         
10702     },
10703     /**
10704      * return the real input element.
10705      */
10706     inputEl: function ()
10707     {
10708         return this.el.select('input.form-control',true).first();
10709     },
10710     
10711     tooltipEl : function()
10712     {
10713         return this.inputEl();
10714     },
10715     
10716     indicatorEl : function()
10717     {
10718         if (Roo.bootstrap.version == 4) {
10719             return false; // not enabled in v4 yet.
10720         }
10721         
10722         var indicator = this.el.select('i.roo-required-indicator',true).first();
10723         
10724         if(!indicator){
10725             return false;
10726         }
10727         
10728         return indicator;
10729         
10730     },
10731     
10732     setDisabled : function(v)
10733     {
10734         var i  = this.inputEl().dom;
10735         if (!v) {
10736             i.removeAttribute('disabled');
10737             return;
10738             
10739         }
10740         i.setAttribute('disabled','true');
10741     },
10742     initEvents : function()
10743     {
10744           
10745         this.inputEl().on("keydown" , this.fireKey,  this);
10746         this.inputEl().on("focus", this.onFocus,  this);
10747         this.inputEl().on("blur", this.onBlur,  this);
10748         
10749         this.inputEl().relayEvent('keyup', this);
10750         
10751         this.indicator = this.indicatorEl();
10752         
10753         if(this.indicator){
10754             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
10755         }
10756  
10757         // reference to original value for reset
10758         this.originalValue = this.getValue();
10759         //Roo.form.TextField.superclass.initEvents.call(this);
10760         if(this.validationEvent == 'keyup'){
10761             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
10762             this.inputEl().on('keyup', this.filterValidation, this);
10763         }
10764         else if(this.validationEvent !== false){
10765             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
10766         }
10767         
10768         if(this.selectOnFocus){
10769             this.on("focus", this.preFocus, this);
10770             
10771         }
10772         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
10773             this.inputEl().on("keypress", this.filterKeys, this);
10774         } else {
10775             this.inputEl().relayEvent('keypress', this);
10776         }
10777        /* if(this.grow){
10778             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
10779             this.el.on("click", this.autoSize,  this);
10780         }
10781         */
10782         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
10783             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
10784         }
10785         
10786         if (typeof(this.before) == 'object') {
10787             this.before.render(this.el.select('.roo-input-before',true).first());
10788         }
10789         if (typeof(this.after) == 'object') {
10790             this.after.render(this.el.select('.roo-input-after',true).first());
10791         }
10792         
10793         this.inputEl().on('change', this.onChange, this);
10794         
10795     },
10796     filterValidation : function(e){
10797         if(!e.isNavKeyPress()){
10798             this.validationTask.delay(this.validationDelay);
10799         }
10800     },
10801      /**
10802      * Validates the field value
10803      * @return {Boolean} True if the value is valid, else false
10804      */
10805     validate : function(){
10806         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
10807         if(this.disabled || this.validateValue(this.getRawValue())){
10808             this.markValid();
10809             return true;
10810         }
10811         
10812         this.markInvalid();
10813         return false;
10814     },
10815     
10816     
10817     /**
10818      * Validates a value according to the field's validation rules and marks the field as invalid
10819      * if the validation fails
10820      * @param {Mixed} value The value to validate
10821      * @return {Boolean} True if the value is valid, else false
10822      */
10823     validateValue : function(value)
10824     {
10825         if(this.getVisibilityEl().hasClass('hidden')){
10826             return true;
10827         }
10828         
10829         if(value.length < 1)  { // if it's blank
10830             if(this.allowBlank){
10831                 return true;
10832             }
10833             return false;
10834         }
10835         
10836         if(value.length < this.minLength){
10837             return false;
10838         }
10839         if(value.length > this.maxLength){
10840             return false;
10841         }
10842         if(this.vtype){
10843             var vt = Roo.form.VTypes;
10844             if(!vt[this.vtype](value, this)){
10845                 return false;
10846             }
10847         }
10848         if(typeof this.validator == "function"){
10849             var msg = this.validator(value);
10850             if(msg !== true){
10851                 return false;
10852             }
10853             if (typeof(msg) == 'string') {
10854                 this.invalidText = msg;
10855             }
10856         }
10857         
10858         if(this.regex && !this.regex.test(value)){
10859             return false;
10860         }
10861         
10862         return true;
10863     },
10864     
10865      // private
10866     fireKey : function(e){
10867         //Roo.log('field ' + e.getKey());
10868         if(e.isNavKeyPress()){
10869             this.fireEvent("specialkey", this, e);
10870         }
10871     },
10872     focus : function (selectText){
10873         if(this.rendered){
10874             this.inputEl().focus();
10875             if(selectText === true){
10876                 this.inputEl().dom.select();
10877             }
10878         }
10879         return this;
10880     } ,
10881     
10882     onFocus : function(){
10883         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10884            // this.el.addClass(this.focusClass);
10885         }
10886         if(!this.hasFocus){
10887             this.hasFocus = true;
10888             this.startValue = this.getValue();
10889             this.fireEvent("focus", this);
10890         }
10891     },
10892     
10893     beforeBlur : Roo.emptyFn,
10894
10895     
10896     // private
10897     onBlur : function(){
10898         this.beforeBlur();
10899         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10900             //this.el.removeClass(this.focusClass);
10901         }
10902         this.hasFocus = false;
10903         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
10904             this.validate();
10905         }
10906         var v = this.getValue();
10907         if(String(v) !== String(this.startValue)){
10908             this.fireEvent('change', this, v, this.startValue);
10909         }
10910         this.fireEvent("blur", this);
10911     },
10912     
10913     onChange : function(e)
10914     {
10915         var v = this.getValue();
10916         if(String(v) !== String(this.startValue)){
10917             this.fireEvent('change', this, v, this.startValue);
10918         }
10919         
10920     },
10921     
10922     /**
10923      * Resets the current field value to the originally loaded value and clears any validation messages
10924      */
10925     reset : function(){
10926         this.setValue(this.originalValue);
10927         this.validate();
10928     },
10929      /**
10930      * Returns the name of the field
10931      * @return {Mixed} name The name field
10932      */
10933     getName: function(){
10934         return this.name;
10935     },
10936      /**
10937      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
10938      * @return {Mixed} value The field value
10939      */
10940     getValue : function(){
10941         
10942         var v = this.inputEl().getValue();
10943         
10944         return v;
10945     },
10946     /**
10947      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
10948      * @return {Mixed} value The field value
10949      */
10950     getRawValue : function(){
10951         var v = this.inputEl().getValue();
10952         
10953         return v;
10954     },
10955     
10956     /**
10957      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
10958      * @param {Mixed} value The value to set
10959      */
10960     setRawValue : function(v){
10961         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
10962     },
10963     
10964     selectText : function(start, end){
10965         var v = this.getRawValue();
10966         if(v.length > 0){
10967             start = start === undefined ? 0 : start;
10968             end = end === undefined ? v.length : end;
10969             var d = this.inputEl().dom;
10970             if(d.setSelectionRange){
10971                 d.setSelectionRange(start, end);
10972             }else if(d.createTextRange){
10973                 var range = d.createTextRange();
10974                 range.moveStart("character", start);
10975                 range.moveEnd("character", v.length-end);
10976                 range.select();
10977             }
10978         }
10979     },
10980     
10981     /**
10982      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
10983      * @param {Mixed} value The value to set
10984      */
10985     setValue : function(v){
10986         this.value = v;
10987         if(this.rendered){
10988             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
10989             this.validate();
10990         }
10991     },
10992     
10993     /*
10994     processValue : function(value){
10995         if(this.stripCharsRe){
10996             var newValue = value.replace(this.stripCharsRe, '');
10997             if(newValue !== value){
10998                 this.setRawValue(newValue);
10999                 return newValue;
11000             }
11001         }
11002         return value;
11003     },
11004   */
11005     preFocus : function(){
11006         
11007         if(this.selectOnFocus){
11008             this.inputEl().dom.select();
11009         }
11010     },
11011     filterKeys : function(e){
11012         var k = e.getKey();
11013         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11014             return;
11015         }
11016         var c = e.getCharCode(), cc = String.fromCharCode(c);
11017         if(Roo.isIE && (e.isSpecialKey() || !cc)){
11018             return;
11019         }
11020         if(!this.maskRe.test(cc)){
11021             e.stopEvent();
11022         }
11023     },
11024      /**
11025      * Clear any invalid styles/messages for this field
11026      */
11027     clearInvalid : function(){
11028         
11029         if(!this.el || this.preventMark){ // not rendered
11030             return;
11031         }
11032         
11033         
11034         this.el.removeClass([this.invalidClass, 'is-invalid']);
11035         
11036         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11037             
11038             var feedback = this.el.select('.form-control-feedback', true).first();
11039             
11040             if(feedback){
11041                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11042             }
11043             
11044         }
11045         
11046         if(this.indicator){
11047             this.indicator.removeClass('visible');
11048             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11049         }
11050         
11051         this.fireEvent('valid', this);
11052     },
11053     
11054      /**
11055      * Mark this field as valid
11056      */
11057     markValid : function()
11058     {
11059         if(!this.el  || this.preventMark){ // not rendered...
11060             return;
11061         }
11062         
11063         this.el.removeClass([this.invalidClass, this.validClass]);
11064         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11065
11066         var feedback = this.el.select('.form-control-feedback', true).first();
11067             
11068         if(feedback){
11069             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11070         }
11071         
11072         if(this.indicator){
11073             this.indicator.removeClass('visible');
11074             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11075         }
11076         
11077         if(this.disabled){
11078             return;
11079         }
11080         
11081            
11082         if(this.allowBlank && !this.getRawValue().length){
11083             return;
11084         }
11085         if (Roo.bootstrap.version == 3) {
11086             this.el.addClass(this.validClass);
11087         } else {
11088             this.inputEl().addClass('is-valid');
11089         }
11090
11091         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11092             
11093             var feedback = this.el.select('.form-control-feedback', true).first();
11094             
11095             if(feedback){
11096                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11097                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11098             }
11099             
11100         }
11101         
11102         this.fireEvent('valid', this);
11103     },
11104     
11105      /**
11106      * Mark this field as invalid
11107      * @param {String} msg The validation message
11108      */
11109     markInvalid : function(msg)
11110     {
11111         if(!this.el  || this.preventMark){ // not rendered
11112             return;
11113         }
11114         
11115         this.el.removeClass([this.invalidClass, this.validClass]);
11116         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11117         
11118         var feedback = this.el.select('.form-control-feedback', true).first();
11119             
11120         if(feedback){
11121             this.el.select('.form-control-feedback', true).first().removeClass(
11122                     [this.invalidFeedbackClass, this.validFeedbackClass]);
11123         }
11124
11125         if(this.disabled){
11126             return;
11127         }
11128         
11129         if(this.allowBlank && !this.getRawValue().length){
11130             return;
11131         }
11132         
11133         if(this.indicator){
11134             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11135             this.indicator.addClass('visible');
11136         }
11137         if (Roo.bootstrap.version == 3) {
11138             this.el.addClass(this.invalidClass);
11139         } else {
11140             this.inputEl().addClass('is-invalid');
11141         }
11142         
11143         
11144         
11145         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11146             
11147             var feedback = this.el.select('.form-control-feedback', true).first();
11148             
11149             if(feedback){
11150                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11151                 
11152                 if(this.getValue().length || this.forceFeedback){
11153                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11154                 }
11155                 
11156             }
11157             
11158         }
11159         
11160         this.fireEvent('invalid', this, msg);
11161     },
11162     // private
11163     SafariOnKeyDown : function(event)
11164     {
11165         // this is a workaround for a password hang bug on chrome/ webkit.
11166         if (this.inputEl().dom.type != 'password') {
11167             return;
11168         }
11169         
11170         var isSelectAll = false;
11171         
11172         if(this.inputEl().dom.selectionEnd > 0){
11173             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11174         }
11175         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11176             event.preventDefault();
11177             this.setValue('');
11178             return;
11179         }
11180         
11181         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11182             
11183             event.preventDefault();
11184             // this is very hacky as keydown always get's upper case.
11185             //
11186             var cc = String.fromCharCode(event.getCharCode());
11187             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
11188             
11189         }
11190     },
11191     adjustWidth : function(tag, w){
11192         tag = tag.toLowerCase();
11193         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11194             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11195                 if(tag == 'input'){
11196                     return w + 2;
11197                 }
11198                 if(tag == 'textarea'){
11199                     return w-2;
11200                 }
11201             }else if(Roo.isOpera){
11202                 if(tag == 'input'){
11203                     return w + 2;
11204                 }
11205                 if(tag == 'textarea'){
11206                     return w-2;
11207                 }
11208             }
11209         }
11210         return w;
11211     },
11212     
11213     setFieldLabel : function(v)
11214     {
11215         if(!this.rendered){
11216             return;
11217         }
11218         
11219         if(this.indicatorEl()){
11220             var ar = this.el.select('label > span',true);
11221             
11222             if (ar.elements.length) {
11223                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11224                 this.fieldLabel = v;
11225                 return;
11226             }
11227             
11228             var br = this.el.select('label',true);
11229             
11230             if(br.elements.length) {
11231                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11232                 this.fieldLabel = v;
11233                 return;
11234             }
11235             
11236             Roo.log('Cannot Found any of label > span || label in input');
11237             return;
11238         }
11239         
11240         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11241         this.fieldLabel = v;
11242         
11243         
11244     }
11245 });
11246
11247  
11248 /*
11249  * - LGPL
11250  *
11251  * Input
11252  * 
11253  */
11254
11255 /**
11256  * @class Roo.bootstrap.TextArea
11257  * @extends Roo.bootstrap.Input
11258  * Bootstrap TextArea class
11259  * @cfg {Number} cols Specifies the visible width of a text area
11260  * @cfg {Number} rows Specifies the visible number of lines in a text area
11261  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11262  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11263  * @cfg {string} html text
11264  * 
11265  * @constructor
11266  * Create a new TextArea
11267  * @param {Object} config The config object
11268  */
11269
11270 Roo.bootstrap.TextArea = function(config){
11271     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11272    
11273 };
11274
11275 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11276      
11277     cols : false,
11278     rows : 5,
11279     readOnly : false,
11280     warp : 'soft',
11281     resize : false,
11282     value: false,
11283     html: false,
11284     
11285     getAutoCreate : function(){
11286         
11287         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11288         
11289         var id = Roo.id();
11290         
11291         var cfg = {};
11292         
11293         if(this.inputType != 'hidden'){
11294             cfg.cls = 'form-group' //input-group
11295         }
11296         
11297         var input =  {
11298             tag: 'textarea',
11299             id : id,
11300             warp : this.warp,
11301             rows : this.rows,
11302             value : this.value || '',
11303             html: this.html || '',
11304             cls : 'form-control',
11305             placeholder : this.placeholder || '' 
11306             
11307         };
11308         
11309         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11310             input.maxLength = this.maxLength;
11311         }
11312         
11313         if(this.resize){
11314             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11315         }
11316         
11317         if(this.cols){
11318             input.cols = this.cols;
11319         }
11320         
11321         if (this.readOnly) {
11322             input.readonly = true;
11323         }
11324         
11325         if (this.name) {
11326             input.name = this.name;
11327         }
11328         
11329         if (this.size) {
11330             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11331         }
11332         
11333         var settings=this;
11334         ['xs','sm','md','lg'].map(function(size){
11335             if (settings[size]) {
11336                 cfg.cls += ' col-' + size + '-' + settings[size];
11337             }
11338         });
11339         
11340         var inputblock = input;
11341         
11342         if(this.hasFeedback && !this.allowBlank){
11343             
11344             var feedback = {
11345                 tag: 'span',
11346                 cls: 'glyphicon form-control-feedback'
11347             };
11348
11349             inputblock = {
11350                 cls : 'has-feedback',
11351                 cn :  [
11352                     input,
11353                     feedback
11354                 ] 
11355             };  
11356         }
11357         
11358         
11359         if (this.before || this.after) {
11360             
11361             inputblock = {
11362                 cls : 'input-group',
11363                 cn :  [] 
11364             };
11365             if (this.before) {
11366                 inputblock.cn.push({
11367                     tag :'span',
11368                     cls : 'input-group-addon',
11369                     html : this.before
11370                 });
11371             }
11372             
11373             inputblock.cn.push(input);
11374             
11375             if(this.hasFeedback && !this.allowBlank){
11376                 inputblock.cls += ' has-feedback';
11377                 inputblock.cn.push(feedback);
11378             }
11379             
11380             if (this.after) {
11381                 inputblock.cn.push({
11382                     tag :'span',
11383                     cls : 'input-group-addon',
11384                     html : this.after
11385                 });
11386             }
11387             
11388         }
11389         
11390         if (align ==='left' && this.fieldLabel.length) {
11391             cfg.cn = [
11392                 {
11393                     tag: 'label',
11394                     'for' :  id,
11395                     cls : 'control-label',
11396                     html : this.fieldLabel
11397                 },
11398                 {
11399                     cls : "",
11400                     cn: [
11401                         inputblock
11402                     ]
11403                 }
11404
11405             ];
11406             
11407             if(this.labelWidth > 12){
11408                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11409             }
11410
11411             if(this.labelWidth < 13 && this.labelmd == 0){
11412                 this.labelmd = this.labelWidth;
11413             }
11414
11415             if(this.labellg > 0){
11416                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11417                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11418             }
11419
11420             if(this.labelmd > 0){
11421                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11422                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11423             }
11424
11425             if(this.labelsm > 0){
11426                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11427                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11428             }
11429
11430             if(this.labelxs > 0){
11431                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11432                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11433             }
11434             
11435         } else if ( this.fieldLabel.length) {
11436             cfg.cn = [
11437
11438                {
11439                    tag: 'label',
11440                    //cls : 'input-group-addon',
11441                    html : this.fieldLabel
11442
11443                },
11444
11445                inputblock
11446
11447            ];
11448
11449         } else {
11450
11451             cfg.cn = [
11452
11453                 inputblock
11454
11455             ];
11456                 
11457         }
11458         
11459         if (this.disabled) {
11460             input.disabled=true;
11461         }
11462         
11463         return cfg;
11464         
11465     },
11466     /**
11467      * return the real textarea element.
11468      */
11469     inputEl: function ()
11470     {
11471         return this.el.select('textarea.form-control',true).first();
11472     },
11473     
11474     /**
11475      * Clear any invalid styles/messages for this field
11476      */
11477     clearInvalid : function()
11478     {
11479         
11480         if(!this.el || this.preventMark){ // not rendered
11481             return;
11482         }
11483         
11484         var label = this.el.select('label', true).first();
11485         var icon = this.el.select('i.fa-star', true).first();
11486         
11487         if(label && icon){
11488             icon.remove();
11489         }
11490         this.el.removeClass( this.validClass);
11491         this.inputEl().removeClass('is-invalid');
11492          
11493         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11494             
11495             var feedback = this.el.select('.form-control-feedback', true).first();
11496             
11497             if(feedback){
11498                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11499             }
11500             
11501         }
11502         
11503         this.fireEvent('valid', this);
11504     },
11505     
11506      /**
11507      * Mark this field as valid
11508      */
11509     markValid : function()
11510     {
11511         if(!this.el  || this.preventMark){ // not rendered
11512             return;
11513         }
11514         
11515         this.el.removeClass([this.invalidClass, this.validClass]);
11516         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11517         
11518         var feedback = this.el.select('.form-control-feedback', true).first();
11519             
11520         if(feedback){
11521             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11522         }
11523
11524         if(this.disabled || this.allowBlank){
11525             return;
11526         }
11527         
11528         var label = this.el.select('label', true).first();
11529         var icon = this.el.select('i.fa-star', true).first();
11530         
11531         if(label && icon){
11532             icon.remove();
11533         }
11534         if (Roo.bootstrap.version == 3) {
11535             this.el.addClass(this.validClass);
11536         } else {
11537             this.inputEl().addClass('is-valid');
11538         }
11539         
11540         
11541         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11542             
11543             var feedback = this.el.select('.form-control-feedback', true).first();
11544             
11545             if(feedback){
11546                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11547                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11548             }
11549             
11550         }
11551         
11552         this.fireEvent('valid', this);
11553     },
11554     
11555      /**
11556      * Mark this field as invalid
11557      * @param {String} msg The validation message
11558      */
11559     markInvalid : function(msg)
11560     {
11561         if(!this.el  || this.preventMark){ // not rendered
11562             return;
11563         }
11564         
11565         this.el.removeClass([this.invalidClass, this.validClass]);
11566         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11567         
11568         var feedback = this.el.select('.form-control-feedback', true).first();
11569             
11570         if(feedback){
11571             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11572         }
11573
11574         if(this.disabled || this.allowBlank){
11575             return;
11576         }
11577         
11578         var label = this.el.select('label', true).first();
11579         var icon = this.el.select('i.fa-star', true).first();
11580         
11581         if(!this.getValue().length && label && !icon){
11582             this.el.createChild({
11583                 tag : 'i',
11584                 cls : 'text-danger fa fa-lg fa-star',
11585                 tooltip : 'This field is required',
11586                 style : 'margin-right:5px;'
11587             }, label, true);
11588         }
11589         
11590         if (Roo.bootstrap.version == 3) {
11591             this.el.addClass(this.invalidClass);
11592         } else {
11593             this.inputEl().addClass('is-invalid');
11594         }
11595         
11596         // fixme ... this may be depricated need to test..
11597         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11598             
11599             var feedback = this.el.select('.form-control-feedback', true).first();
11600             
11601             if(feedback){
11602                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11603                 
11604                 if(this.getValue().length || this.forceFeedback){
11605                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11606                 }
11607                 
11608             }
11609             
11610         }
11611         
11612         this.fireEvent('invalid', this, msg);
11613     }
11614 });
11615
11616  
11617 /*
11618  * - LGPL
11619  *
11620  * trigger field - base class for combo..
11621  * 
11622  */
11623  
11624 /**
11625  * @class Roo.bootstrap.TriggerField
11626  * @extends Roo.bootstrap.Input
11627  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11628  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11629  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11630  * for which you can provide a custom implementation.  For example:
11631  * <pre><code>
11632 var trigger = new Roo.bootstrap.TriggerField();
11633 trigger.onTriggerClick = myTriggerFn;
11634 trigger.applyTo('my-field');
11635 </code></pre>
11636  *
11637  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11638  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11639  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
11640  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11641  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11642
11643  * @constructor
11644  * Create a new TriggerField.
11645  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11646  * to the base TextField)
11647  */
11648 Roo.bootstrap.TriggerField = function(config){
11649     this.mimicing = false;
11650     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11651 };
11652
11653 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
11654     /**
11655      * @cfg {String} triggerClass A CSS class to apply to the trigger
11656      */
11657      /**
11658      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11659      */
11660     hideTrigger:false,
11661
11662     /**
11663      * @cfg {Boolean} removable (true|false) special filter default false
11664      */
11665     removable : false,
11666     
11667     /** @cfg {Boolean} grow @hide */
11668     /** @cfg {Number} growMin @hide */
11669     /** @cfg {Number} growMax @hide */
11670
11671     /**
11672      * @hide 
11673      * @method
11674      */
11675     autoSize: Roo.emptyFn,
11676     // private
11677     monitorTab : true,
11678     // private
11679     deferHeight : true,
11680
11681     
11682     actionMode : 'wrap',
11683     
11684     caret : false,
11685     
11686     
11687     getAutoCreate : function(){
11688        
11689         var align = this.labelAlign || this.parentLabelAlign();
11690         
11691         var id = Roo.id();
11692         
11693         var cfg = {
11694             cls: 'form-group' //input-group
11695         };
11696         
11697         
11698         var input =  {
11699             tag: 'input',
11700             id : id,
11701             type : this.inputType,
11702             cls : 'form-control',
11703             autocomplete: 'new-password',
11704             placeholder : this.placeholder || '' 
11705             
11706         };
11707         if (this.name) {
11708             input.name = this.name;
11709         }
11710         if (this.size) {
11711             input.cls += ' input-' + this.size;
11712         }
11713         
11714         if (this.disabled) {
11715             input.disabled=true;
11716         }
11717         
11718         var inputblock = input;
11719         
11720         if(this.hasFeedback && !this.allowBlank){
11721             
11722             var feedback = {
11723                 tag: 'span',
11724                 cls: 'glyphicon form-control-feedback'
11725             };
11726             
11727             if(this.removable && !this.editable  ){
11728                 inputblock = {
11729                     cls : 'has-feedback',
11730                     cn :  [
11731                         inputblock,
11732                         {
11733                             tag: 'button',
11734                             html : 'x',
11735                             cls : 'roo-combo-removable-btn close'
11736                         },
11737                         feedback
11738                     ] 
11739                 };
11740             } else {
11741                 inputblock = {
11742                     cls : 'has-feedback',
11743                     cn :  [
11744                         inputblock,
11745                         feedback
11746                     ] 
11747                 };
11748             }
11749
11750         } else {
11751             if(this.removable && !this.editable ){
11752                 inputblock = {
11753                     cls : 'roo-removable',
11754                     cn :  [
11755                         inputblock,
11756                         {
11757                             tag: 'button',
11758                             html : 'x',
11759                             cls : 'roo-combo-removable-btn close'
11760                         }
11761                     ] 
11762                 };
11763             }
11764         }
11765         
11766         if (this.before || this.after) {
11767             
11768             inputblock = {
11769                 cls : 'input-group',
11770                 cn :  [] 
11771             };
11772             if (this.before) {
11773                 inputblock.cn.push({
11774                     tag :'span',
11775                     cls : 'input-group-addon input-group-prepend input-group-text',
11776                     html : this.before
11777                 });
11778             }
11779             
11780             inputblock.cn.push(input);
11781             
11782             if(this.hasFeedback && !this.allowBlank){
11783                 inputblock.cls += ' has-feedback';
11784                 inputblock.cn.push(feedback);
11785             }
11786             
11787             if (this.after) {
11788                 inputblock.cn.push({
11789                     tag :'span',
11790                     cls : 'input-group-addon input-group-append input-group-text',
11791                     html : this.after
11792                 });
11793             }
11794             
11795         };
11796         
11797       
11798         
11799         var ibwrap = inputblock;
11800         
11801         if(this.multiple){
11802             ibwrap = {
11803                 tag: 'ul',
11804                 cls: 'roo-select2-choices',
11805                 cn:[
11806                     {
11807                         tag: 'li',
11808                         cls: 'roo-select2-search-field',
11809                         cn: [
11810
11811                             inputblock
11812                         ]
11813                     }
11814                 ]
11815             };
11816                 
11817         }
11818         
11819         var combobox = {
11820             cls: 'roo-select2-container input-group',
11821             cn: [
11822                  {
11823                     tag: 'input',
11824                     type : 'hidden',
11825                     cls: 'form-hidden-field'
11826                 },
11827                 ibwrap
11828             ]
11829         };
11830         
11831         if(!this.multiple && this.showToggleBtn){
11832             
11833             var caret = {
11834                         tag: 'span',
11835                         cls: 'caret'
11836              };
11837             if (this.caret != false) {
11838                 caret = {
11839                      tag: 'i',
11840                      cls: 'fa fa-' + this.caret
11841                 };
11842                 
11843             }
11844             
11845             combobox.cn.push({
11846                 tag :'span',
11847                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
11848                 cn : [
11849                     Roo.bootstrap.version == 3 ? caret : '',
11850                     {
11851                         tag: 'span',
11852                         cls: 'combobox-clear',
11853                         cn  : [
11854                             {
11855                                 tag : 'i',
11856                                 cls: 'icon-remove'
11857                             }
11858                         ]
11859                     }
11860                 ]
11861
11862             })
11863         }
11864         
11865         if(this.multiple){
11866             combobox.cls += ' roo-select2-container-multi';
11867         }
11868          var indicator = {
11869             tag : 'i',
11870             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11871             tooltip : 'This field is required'
11872         };
11873         if (Roo.bootstrap.version == 4) {
11874             indicator = {
11875                 tag : 'i',
11876                 style : 'display:none'
11877             };
11878         }
11879         
11880         
11881         if (align ==='left' && this.fieldLabel.length) {
11882             
11883             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
11884
11885             cfg.cn = [
11886                 indicator,
11887                 {
11888                     tag: 'label',
11889                     'for' :  id,
11890                     cls : 'control-label',
11891                     html : this.fieldLabel
11892
11893                 },
11894                 {
11895                     cls : "", 
11896                     cn: [
11897                         combobox
11898                     ]
11899                 }
11900
11901             ];
11902             
11903             var labelCfg = cfg.cn[1];
11904             var contentCfg = cfg.cn[2];
11905             
11906             if(this.indicatorpos == 'right'){
11907                 cfg.cn = [
11908                     {
11909                         tag: 'label',
11910                         'for' :  id,
11911                         cls : 'control-label',
11912                         cn : [
11913                             {
11914                                 tag : 'span',
11915                                 html : this.fieldLabel
11916                             },
11917                             indicator
11918                         ]
11919                     },
11920                     {
11921                         cls : "", 
11922                         cn: [
11923                             combobox
11924                         ]
11925                     }
11926
11927                 ];
11928                 
11929                 labelCfg = cfg.cn[0];
11930                 contentCfg = cfg.cn[1];
11931             }
11932             
11933             if(this.labelWidth > 12){
11934                 labelCfg.style = "width: " + this.labelWidth + 'px';
11935             }
11936             
11937             if(this.labelWidth < 13 && this.labelmd == 0){
11938                 this.labelmd = this.labelWidth;
11939             }
11940             
11941             if(this.labellg > 0){
11942                 labelCfg.cls += ' col-lg-' + this.labellg;
11943                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11944             }
11945             
11946             if(this.labelmd > 0){
11947                 labelCfg.cls += ' col-md-' + this.labelmd;
11948                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11949             }
11950             
11951             if(this.labelsm > 0){
11952                 labelCfg.cls += ' col-sm-' + this.labelsm;
11953                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11954             }
11955             
11956             if(this.labelxs > 0){
11957                 labelCfg.cls += ' col-xs-' + this.labelxs;
11958                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11959             }
11960             
11961         } else if ( this.fieldLabel.length) {
11962 //                Roo.log(" label");
11963             cfg.cn = [
11964                 indicator,
11965                {
11966                    tag: 'label',
11967                    //cls : 'input-group-addon',
11968                    html : this.fieldLabel
11969
11970                },
11971
11972                combobox
11973
11974             ];
11975             
11976             if(this.indicatorpos == 'right'){
11977                 
11978                 cfg.cn = [
11979                     {
11980                        tag: 'label',
11981                        cn : [
11982                            {
11983                                tag : 'span',
11984                                html : this.fieldLabel
11985                            },
11986                            indicator
11987                        ]
11988
11989                     },
11990                     combobox
11991
11992                 ];
11993
11994             }
11995
11996         } else {
11997             
11998 //                Roo.log(" no label && no align");
11999                 cfg = combobox
12000                      
12001                 
12002         }
12003         
12004         var settings=this;
12005         ['xs','sm','md','lg'].map(function(size){
12006             if (settings[size]) {
12007                 cfg.cls += ' col-' + size + '-' + settings[size];
12008             }
12009         });
12010         
12011         return cfg;
12012         
12013     },
12014     
12015     
12016     
12017     // private
12018     onResize : function(w, h){
12019 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12020 //        if(typeof w == 'number'){
12021 //            var x = w - this.trigger.getWidth();
12022 //            this.inputEl().setWidth(this.adjustWidth('input', x));
12023 //            this.trigger.setStyle('left', x+'px');
12024 //        }
12025     },
12026
12027     // private
12028     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12029
12030     // private
12031     getResizeEl : function(){
12032         return this.inputEl();
12033     },
12034
12035     // private
12036     getPositionEl : function(){
12037         return this.inputEl();
12038     },
12039
12040     // private
12041     alignErrorIcon : function(){
12042         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12043     },
12044
12045     // private
12046     initEvents : function(){
12047         
12048         this.createList();
12049         
12050         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12051         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12052         if(!this.multiple && this.showToggleBtn){
12053             this.trigger = this.el.select('span.dropdown-toggle',true).first();
12054             if(this.hideTrigger){
12055                 this.trigger.setDisplayed(false);
12056             }
12057             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12058         }
12059         
12060         if(this.multiple){
12061             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12062         }
12063         
12064         if(this.removable && !this.editable && !this.tickable){
12065             var close = this.closeTriggerEl();
12066             
12067             if(close){
12068                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12069                 close.on('click', this.removeBtnClick, this, close);
12070             }
12071         }
12072         
12073         //this.trigger.addClassOnOver('x-form-trigger-over');
12074         //this.trigger.addClassOnClick('x-form-trigger-click');
12075         
12076         //if(!this.width){
12077         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12078         //}
12079     },
12080     
12081     closeTriggerEl : function()
12082     {
12083         var close = this.el.select('.roo-combo-removable-btn', true).first();
12084         return close ? close : false;
12085     },
12086     
12087     removeBtnClick : function(e, h, el)
12088     {
12089         e.preventDefault();
12090         
12091         if(this.fireEvent("remove", this) !== false){
12092             this.reset();
12093             this.fireEvent("afterremove", this)
12094         }
12095     },
12096     
12097     createList : function()
12098     {
12099         this.list = Roo.get(document.body).createChild({
12100             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12101             cls: 'typeahead typeahead-long dropdown-menu',
12102             style: 'display:none'
12103         });
12104         
12105         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12106         
12107     },
12108
12109     // private
12110     initTrigger : function(){
12111        
12112     },
12113
12114     // private
12115     onDestroy : function(){
12116         if(this.trigger){
12117             this.trigger.removeAllListeners();
12118           //  this.trigger.remove();
12119         }
12120         //if(this.wrap){
12121         //    this.wrap.remove();
12122         //}
12123         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12124     },
12125
12126     // private
12127     onFocus : function(){
12128         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12129         /*
12130         if(!this.mimicing){
12131             this.wrap.addClass('x-trigger-wrap-focus');
12132             this.mimicing = true;
12133             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12134             if(this.monitorTab){
12135                 this.el.on("keydown", this.checkTab, this);
12136             }
12137         }
12138         */
12139     },
12140
12141     // private
12142     checkTab : function(e){
12143         if(e.getKey() == e.TAB){
12144             this.triggerBlur();
12145         }
12146     },
12147
12148     // private
12149     onBlur : function(){
12150         // do nothing
12151     },
12152
12153     // private
12154     mimicBlur : function(e, t){
12155         /*
12156         if(!this.wrap.contains(t) && this.validateBlur()){
12157             this.triggerBlur();
12158         }
12159         */
12160     },
12161
12162     // private
12163     triggerBlur : function(){
12164         this.mimicing = false;
12165         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12166         if(this.monitorTab){
12167             this.el.un("keydown", this.checkTab, this);
12168         }
12169         //this.wrap.removeClass('x-trigger-wrap-focus');
12170         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12171     },
12172
12173     // private
12174     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12175     validateBlur : function(e, t){
12176         return true;
12177     },
12178
12179     // private
12180     onDisable : function(){
12181         this.inputEl().dom.disabled = true;
12182         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12183         //if(this.wrap){
12184         //    this.wrap.addClass('x-item-disabled');
12185         //}
12186     },
12187
12188     // private
12189     onEnable : function(){
12190         this.inputEl().dom.disabled = false;
12191         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12192         //if(this.wrap){
12193         //    this.el.removeClass('x-item-disabled');
12194         //}
12195     },
12196
12197     // private
12198     onShow : function(){
12199         var ae = this.getActionEl();
12200         
12201         if(ae){
12202             ae.dom.style.display = '';
12203             ae.dom.style.visibility = 'visible';
12204         }
12205     },
12206
12207     // private
12208     
12209     onHide : function(){
12210         var ae = this.getActionEl();
12211         ae.dom.style.display = 'none';
12212     },
12213
12214     /**
12215      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
12216      * by an implementing function.
12217      * @method
12218      * @param {EventObject} e
12219      */
12220     onTriggerClick : Roo.emptyFn
12221 });
12222  
12223 /*
12224 * Licence: LGPL
12225 */
12226
12227 /**
12228  * @class Roo.bootstrap.CardUploader
12229  * @extends Roo.bootstrap.Button
12230  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12231  * @cfg {Number} errorTimeout default 3000
12232  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
12233  * @cfg {Array}  html The button text.
12234
12235  *
12236  * @constructor
12237  * Create a new CardUploader
12238  * @param {Object} config The config object
12239  */
12240
12241 Roo.bootstrap.CardUploader = function(config){
12242     
12243  
12244     
12245     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12246     
12247     
12248     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
12249         return r.data.id
12250         });
12251     
12252     
12253 };
12254
12255 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
12256     
12257      
12258     errorTimeout : 3000,
12259      
12260     images : false,
12261    
12262     fileCollection : false,
12263     allowBlank : true,
12264     
12265     getAutoCreate : function()
12266     {
12267         
12268         var cfg =  {
12269             cls :'form-group' ,
12270             cn : [
12271                
12272                 {
12273                     tag: 'label',
12274                    //cls : 'input-group-addon',
12275                     html : this.fieldLabel
12276
12277                 },
12278
12279                 {
12280                     tag: 'input',
12281                     type : 'hidden',
12282                     value : this.value,
12283                     cls : 'd-none  form-control'
12284                 },
12285                 
12286                 {
12287                     tag: 'input',
12288                     multiple : 'multiple',
12289                     type : 'file',
12290                     cls : 'd-none  roo-card-upload-selector'
12291                 },
12292                 
12293                 {
12294                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12295                 },
12296                 {
12297                     cls : 'card-columns roo-card-uploader-container'
12298                 }
12299
12300             ]
12301         };
12302            
12303          
12304         return cfg;
12305     },
12306     
12307     getChildContainer : function() /// what children are added to.
12308     {
12309         return this.containerEl;
12310     },
12311    
12312     getButtonContainer : function() /// what children are added to.
12313     {
12314         return this.el.select(".roo-card-uploader-button-container").first();
12315     },
12316    
12317     initEvents : function()
12318     {
12319         
12320         Roo.bootstrap.Input.prototype.initEvents.call(this);
12321         
12322         var t = this;
12323         this.addxtype({
12324             xns: Roo.bootstrap,
12325
12326             xtype : 'Button',
12327             container_method : 'getButtonContainer' ,            
12328             html :  this.html, // fix changable?
12329             cls : 'w-100 ',
12330             listeners : {
12331                 'click' : function(btn, e) {
12332                     t.onClick(e);
12333                 }
12334             }
12335         });
12336         
12337         
12338         
12339         
12340         this.urlAPI = (window.createObjectURL && window) || 
12341                                 (window.URL && URL.revokeObjectURL && URL) || 
12342                                 (window.webkitURL && webkitURL);
12343                         
12344          
12345          
12346          
12347         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12348         
12349         this.selectorEl.on('change', this.onFileSelected, this);
12350         if (this.images) {
12351             var t = this;
12352             this.images.forEach(function(img) {
12353                 t.addCard(img)
12354             });
12355             this.images = false;
12356         }
12357         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12358          
12359        
12360     },
12361     
12362    
12363     onClick : function(e)
12364     {
12365         e.preventDefault();
12366          
12367         this.selectorEl.dom.click();
12368          
12369     },
12370     
12371     onFileSelected : function(e)
12372     {
12373         e.preventDefault();
12374         
12375         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12376             return;
12377         }
12378         
12379         Roo.each(this.selectorEl.dom.files, function(file){    
12380             this.addFile(file);
12381         }, this);
12382          
12383     },
12384     
12385       
12386     
12387       
12388     
12389     addFile : function(file)
12390     {
12391            
12392         if(typeof(file) === 'string'){
12393             throw "Add file by name?"; // should not happen
12394             return;
12395         }
12396         
12397         if(!file || !this.urlAPI){
12398             return;
12399         }
12400         
12401         // file;
12402         // file.type;
12403         
12404         var _this = this;
12405         
12406         
12407         var url = _this.urlAPI.createObjectURL( file);
12408            
12409         this.addCard({
12410             id : Roo.bootstrap.CardUploader.ID--,
12411             is_uploaded : false,
12412             src : url,
12413             title : file.name,
12414             mimetype : file.type,
12415             preview : false,
12416             is_deleted : 0
12417         })
12418         
12419     },
12420     
12421     addCard : function (data)
12422     {
12423         // hidden input element?
12424         // if the file is not an image...
12425         //then we need to use something other that and header_image
12426         var t = this;
12427         //   remove.....
12428         var footer = [
12429             {
12430                 xns : Roo.bootstrap,
12431                 xtype : 'CardFooter',
12432                 items: [
12433                     {
12434                         xns : Roo.bootstrap,
12435                         xtype : 'Element',
12436                         cls : 'd-flex',
12437                         items : [
12438                             
12439                             {
12440                                 xns : Roo.bootstrap,
12441                                 xtype : 'Button',
12442                                 html : String.format("<small>{0}</small>", data.title),
12443                                 cls : 'col-11 text-left',
12444                                 size: 'sm',
12445                                 weight: 'link',
12446                                 fa : 'download',
12447                                 listeners : {
12448                                     click : function() {
12449                                         this.downloadCard(data.id)
12450                                     }
12451                                 }
12452                             },
12453                           
12454                             {
12455                                 xns : Roo.bootstrap,
12456                                 xtype : 'Button',
12457                                 
12458                                 size : 'sm',
12459                                 weight: 'danger',
12460                                 cls : 'col-1',
12461                                 fa : 'times',
12462                                 listeners : {
12463                                     click : function() {
12464                                         t.removeCard(data.id)
12465                                     }
12466                                 }
12467                             }
12468                         ]
12469                     }
12470                     
12471                 ] 
12472             }
12473             
12474         ];
12475
12476         var cn = this.addxtype(
12477             {
12478                  
12479                 xns : Roo.bootstrap,
12480                 xtype : 'Card',
12481                 closeable : true,
12482                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12483                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
12484                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
12485                 data : data,
12486                 html : false,
12487                  
12488                 items : footer,
12489                 initEvents : function() {
12490                     Roo.bootstrap.Card.prototype.initEvents.call(this);
12491                     this.imgEl = this.el.select('.card-img-top').first();
12492                     if (this.imgEl) {
12493                         this.imgEl.on('click', function() { t.previewCard( data.id); }, this);
12494                         this.imgEl.set({ 'pointer' : 'cursor' });
12495                                   
12496                     }
12497                     
12498                   
12499                 }
12500                 
12501             }
12502         );
12503         // dont' really need ot update items.
12504         // this.items.push(cn);
12505         this.fileCollection.add(cn);
12506         this.updateInput();
12507         
12508     },
12509     removeCard : function(id)
12510     {
12511         
12512         var card  = this.fileCollection.get(id);
12513         card.data.is_deleted = 1;
12514         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12515         this.fileCollection.remove(card);
12516         //this.items = this.items.filter(function(e) { return e != card });
12517         // dont' really need ot update items.
12518         card.el.dom.parentNode.removeChild(card.el.dom);
12519         
12520     },
12521     reset: function()
12522     {
12523         this.fileCollection.each(function(card) {
12524             card.el.dom.parentNode.removeChild(card.el.dom);    
12525         });
12526         this.fileCollection.clear();
12527         this.updateInput();
12528     },
12529     
12530     updateInput : function()
12531     {
12532         var data = [];
12533         this.fileCollection.each(function(e) {
12534             data.push(e.data);
12535         });
12536         
12537         this.inputEl().dom.value = JSON.stringify(data);
12538     }
12539     
12540     
12541 });
12542
12543
12544 Roo.bootstrap.CardUploader.ID = -1;/*
12545  * Based on:
12546  * Ext JS Library 1.1.1
12547  * Copyright(c) 2006-2007, Ext JS, LLC.
12548  *
12549  * Originally Released Under LGPL - original licence link has changed is not relivant.
12550  *
12551  * Fork - LGPL
12552  * <script type="text/javascript">
12553  */
12554
12555
12556 /**
12557  * @class Roo.data.SortTypes
12558  * @singleton
12559  * Defines the default sorting (casting?) comparison functions used when sorting data.
12560  */
12561 Roo.data.SortTypes = {
12562     /**
12563      * Default sort that does nothing
12564      * @param {Mixed} s The value being converted
12565      * @return {Mixed} The comparison value
12566      */
12567     none : function(s){
12568         return s;
12569     },
12570     
12571     /**
12572      * The regular expression used to strip tags
12573      * @type {RegExp}
12574      * @property
12575      */
12576     stripTagsRE : /<\/?[^>]+>/gi,
12577     
12578     /**
12579      * Strips all HTML tags to sort on text only
12580      * @param {Mixed} s The value being converted
12581      * @return {String} The comparison value
12582      */
12583     asText : function(s){
12584         return String(s).replace(this.stripTagsRE, "");
12585     },
12586     
12587     /**
12588      * Strips all HTML tags to sort on text only - Case insensitive
12589      * @param {Mixed} s The value being converted
12590      * @return {String} The comparison value
12591      */
12592     asUCText : function(s){
12593         return String(s).toUpperCase().replace(this.stripTagsRE, "");
12594     },
12595     
12596     /**
12597      * Case insensitive string
12598      * @param {Mixed} s The value being converted
12599      * @return {String} The comparison value
12600      */
12601     asUCString : function(s) {
12602         return String(s).toUpperCase();
12603     },
12604     
12605     /**
12606      * Date sorting
12607      * @param {Mixed} s The value being converted
12608      * @return {Number} The comparison value
12609      */
12610     asDate : function(s) {
12611         if(!s){
12612             return 0;
12613         }
12614         if(s instanceof Date){
12615             return s.getTime();
12616         }
12617         return Date.parse(String(s));
12618     },
12619     
12620     /**
12621      * Float sorting
12622      * @param {Mixed} s The value being converted
12623      * @return {Float} The comparison value
12624      */
12625     asFloat : function(s) {
12626         var val = parseFloat(String(s).replace(/,/g, ""));
12627         if(isNaN(val)) {
12628             val = 0;
12629         }
12630         return val;
12631     },
12632     
12633     /**
12634      * Integer sorting
12635      * @param {Mixed} s The value being converted
12636      * @return {Number} The comparison value
12637      */
12638     asInt : function(s) {
12639         var val = parseInt(String(s).replace(/,/g, ""));
12640         if(isNaN(val)) {
12641             val = 0;
12642         }
12643         return val;
12644     }
12645 };/*
12646  * Based on:
12647  * Ext JS Library 1.1.1
12648  * Copyright(c) 2006-2007, Ext JS, LLC.
12649  *
12650  * Originally Released Under LGPL - original licence link has changed is not relivant.
12651  *
12652  * Fork - LGPL
12653  * <script type="text/javascript">
12654  */
12655
12656 /**
12657 * @class Roo.data.Record
12658  * Instances of this class encapsulate both record <em>definition</em> information, and record
12659  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12660  * to access Records cached in an {@link Roo.data.Store} object.<br>
12661  * <p>
12662  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12663  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12664  * objects.<br>
12665  * <p>
12666  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12667  * @constructor
12668  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12669  * {@link #create}. The parameters are the same.
12670  * @param {Array} data An associative Array of data values keyed by the field name.
12671  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12672  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12673  * not specified an integer id is generated.
12674  */
12675 Roo.data.Record = function(data, id){
12676     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12677     this.data = data;
12678 };
12679
12680 /**
12681  * Generate a constructor for a specific record layout.
12682  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12683  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12684  * Each field definition object may contain the following properties: <ul>
12685  * <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,
12686  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12687  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12688  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12689  * is being used, then this is a string containing the javascript expression to reference the data relative to 
12690  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12691  * to the data item relative to the record element. If the mapping expression is the same as the field name,
12692  * this may be omitted.</p></li>
12693  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12694  * <ul><li>auto (Default, implies no conversion)</li>
12695  * <li>string</li>
12696  * <li>int</li>
12697  * <li>float</li>
12698  * <li>boolean</li>
12699  * <li>date</li></ul></p></li>
12700  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12701  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12702  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12703  * by the Reader into an object that will be stored in the Record. It is passed the
12704  * following parameters:<ul>
12705  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12706  * </ul></p></li>
12707  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12708  * </ul>
12709  * <br>usage:<br><pre><code>
12710 var TopicRecord = Roo.data.Record.create(
12711     {name: 'title', mapping: 'topic_title'},
12712     {name: 'author', mapping: 'username'},
12713     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12714     {name: 'lastPost', mapping: 'post_time', type: 'date'},
12715     {name: 'lastPoster', mapping: 'user2'},
12716     {name: 'excerpt', mapping: 'post_text'}
12717 );
12718
12719 var myNewRecord = new TopicRecord({
12720     title: 'Do my job please',
12721     author: 'noobie',
12722     totalPosts: 1,
12723     lastPost: new Date(),
12724     lastPoster: 'Animal',
12725     excerpt: 'No way dude!'
12726 });
12727 myStore.add(myNewRecord);
12728 </code></pre>
12729  * @method create
12730  * @static
12731  */
12732 Roo.data.Record.create = function(o){
12733     var f = function(){
12734         f.superclass.constructor.apply(this, arguments);
12735     };
12736     Roo.extend(f, Roo.data.Record);
12737     var p = f.prototype;
12738     p.fields = new Roo.util.MixedCollection(false, function(field){
12739         return field.name;
12740     });
12741     for(var i = 0, len = o.length; i < len; i++){
12742         p.fields.add(new Roo.data.Field(o[i]));
12743     }
12744     f.getField = function(name){
12745         return p.fields.get(name);  
12746     };
12747     return f;
12748 };
12749
12750 Roo.data.Record.AUTO_ID = 1000;
12751 Roo.data.Record.EDIT = 'edit';
12752 Roo.data.Record.REJECT = 'reject';
12753 Roo.data.Record.COMMIT = 'commit';
12754
12755 Roo.data.Record.prototype = {
12756     /**
12757      * Readonly flag - true if this record has been modified.
12758      * @type Boolean
12759      */
12760     dirty : false,
12761     editing : false,
12762     error: null,
12763     modified: null,
12764
12765     // private
12766     join : function(store){
12767         this.store = store;
12768     },
12769
12770     /**
12771      * Set the named field to the specified value.
12772      * @param {String} name The name of the field to set.
12773      * @param {Object} value The value to set the field to.
12774      */
12775     set : function(name, value){
12776         if(this.data[name] == value){
12777             return;
12778         }
12779         this.dirty = true;
12780         if(!this.modified){
12781             this.modified = {};
12782         }
12783         if(typeof this.modified[name] == 'undefined'){
12784             this.modified[name] = this.data[name];
12785         }
12786         this.data[name] = value;
12787         if(!this.editing && this.store){
12788             this.store.afterEdit(this);
12789         }       
12790     },
12791
12792     /**
12793      * Get the value of the named field.
12794      * @param {String} name The name of the field to get the value of.
12795      * @return {Object} The value of the field.
12796      */
12797     get : function(name){
12798         return this.data[name]; 
12799     },
12800
12801     // private
12802     beginEdit : function(){
12803         this.editing = true;
12804         this.modified = {}; 
12805     },
12806
12807     // private
12808     cancelEdit : function(){
12809         this.editing = false;
12810         delete this.modified;
12811     },
12812
12813     // private
12814     endEdit : function(){
12815         this.editing = false;
12816         if(this.dirty && this.store){
12817             this.store.afterEdit(this);
12818         }
12819     },
12820
12821     /**
12822      * Usually called by the {@link Roo.data.Store} which owns the Record.
12823      * Rejects all changes made to the Record since either creation, or the last commit operation.
12824      * Modified fields are reverted to their original values.
12825      * <p>
12826      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
12827      * of reject operations.
12828      */
12829     reject : function(){
12830         var m = this.modified;
12831         for(var n in m){
12832             if(typeof m[n] != "function"){
12833                 this.data[n] = m[n];
12834             }
12835         }
12836         this.dirty = false;
12837         delete this.modified;
12838         this.editing = false;
12839         if(this.store){
12840             this.store.afterReject(this);
12841         }
12842     },
12843
12844     /**
12845      * Usually called by the {@link Roo.data.Store} which owns the Record.
12846      * Commits all changes made to the Record since either creation, or the last commit operation.
12847      * <p>
12848      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
12849      * of commit operations.
12850      */
12851     commit : function(){
12852         this.dirty = false;
12853         delete this.modified;
12854         this.editing = false;
12855         if(this.store){
12856             this.store.afterCommit(this);
12857         }
12858     },
12859
12860     // private
12861     hasError : function(){
12862         return this.error != null;
12863     },
12864
12865     // private
12866     clearError : function(){
12867         this.error = null;
12868     },
12869
12870     /**
12871      * Creates a copy of this record.
12872      * @param {String} id (optional) A new record id if you don't want to use this record's id
12873      * @return {Record}
12874      */
12875     copy : function(newId) {
12876         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
12877     }
12878 };/*
12879  * Based on:
12880  * Ext JS Library 1.1.1
12881  * Copyright(c) 2006-2007, Ext JS, LLC.
12882  *
12883  * Originally Released Under LGPL - original licence link has changed is not relivant.
12884  *
12885  * Fork - LGPL
12886  * <script type="text/javascript">
12887  */
12888
12889
12890
12891 /**
12892  * @class Roo.data.Store
12893  * @extends Roo.util.Observable
12894  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
12895  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
12896  * <p>
12897  * 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
12898  * has no knowledge of the format of the data returned by the Proxy.<br>
12899  * <p>
12900  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
12901  * instances from the data object. These records are cached and made available through accessor functions.
12902  * @constructor
12903  * Creates a new Store.
12904  * @param {Object} config A config object containing the objects needed for the Store to access data,
12905  * and read the data into Records.
12906  */
12907 Roo.data.Store = function(config){
12908     this.data = new Roo.util.MixedCollection(false);
12909     this.data.getKey = function(o){
12910         return o.id;
12911     };
12912     this.baseParams = {};
12913     // private
12914     this.paramNames = {
12915         "start" : "start",
12916         "limit" : "limit",
12917         "sort" : "sort",
12918         "dir" : "dir",
12919         "multisort" : "_multisort"
12920     };
12921
12922     if(config && config.data){
12923         this.inlineData = config.data;
12924         delete config.data;
12925     }
12926
12927     Roo.apply(this, config);
12928     
12929     if(this.reader){ // reader passed
12930         this.reader = Roo.factory(this.reader, Roo.data);
12931         this.reader.xmodule = this.xmodule || false;
12932         if(!this.recordType){
12933             this.recordType = this.reader.recordType;
12934         }
12935         if(this.reader.onMetaChange){
12936             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
12937         }
12938     }
12939
12940     if(this.recordType){
12941         this.fields = this.recordType.prototype.fields;
12942     }
12943     this.modified = [];
12944
12945     this.addEvents({
12946         /**
12947          * @event datachanged
12948          * Fires when the data cache has changed, and a widget which is using this Store
12949          * as a Record cache should refresh its view.
12950          * @param {Store} this
12951          */
12952         datachanged : true,
12953         /**
12954          * @event metachange
12955          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
12956          * @param {Store} this
12957          * @param {Object} meta The JSON metadata
12958          */
12959         metachange : true,
12960         /**
12961          * @event add
12962          * Fires when Records have been added to the Store
12963          * @param {Store} this
12964          * @param {Roo.data.Record[]} records The array of Records added
12965          * @param {Number} index The index at which the record(s) were added
12966          */
12967         add : true,
12968         /**
12969          * @event remove
12970          * Fires when a Record has been removed from the Store
12971          * @param {Store} this
12972          * @param {Roo.data.Record} record The Record that was removed
12973          * @param {Number} index The index at which the record was removed
12974          */
12975         remove : true,
12976         /**
12977          * @event update
12978          * Fires when a Record has been updated
12979          * @param {Store} this
12980          * @param {Roo.data.Record} record The Record that was updated
12981          * @param {String} operation The update operation being performed.  Value may be one of:
12982          * <pre><code>
12983  Roo.data.Record.EDIT
12984  Roo.data.Record.REJECT
12985  Roo.data.Record.COMMIT
12986          * </code></pre>
12987          */
12988         update : true,
12989         /**
12990          * @event clear
12991          * Fires when the data cache has been cleared.
12992          * @param {Store} this
12993          */
12994         clear : true,
12995         /**
12996          * @event beforeload
12997          * Fires before a request is made for a new data object.  If the beforeload handler returns false
12998          * the load action will be canceled.
12999          * @param {Store} this
13000          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13001          */
13002         beforeload : true,
13003         /**
13004          * @event beforeloadadd
13005          * Fires after a new set of Records has been loaded.
13006          * @param {Store} this
13007          * @param {Roo.data.Record[]} records The Records that were loaded
13008          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13009          */
13010         beforeloadadd : true,
13011         /**
13012          * @event load
13013          * Fires after a new set of Records has been loaded, before they are added to the store.
13014          * @param {Store} this
13015          * @param {Roo.data.Record[]} records The Records that were loaded
13016          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13017          * @params {Object} return from reader
13018          */
13019         load : true,
13020         /**
13021          * @event loadexception
13022          * Fires if an exception occurs in the Proxy during loading.
13023          * Called with the signature of the Proxy's "loadexception" event.
13024          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13025          * 
13026          * @param {Proxy} 
13027          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13028          * @param {Object} load options 
13029          * @param {Object} jsonData from your request (normally this contains the Exception)
13030          */
13031         loadexception : true
13032     });
13033     
13034     if(this.proxy){
13035         this.proxy = Roo.factory(this.proxy, Roo.data);
13036         this.proxy.xmodule = this.xmodule || false;
13037         this.relayEvents(this.proxy,  ["loadexception"]);
13038     }
13039     this.sortToggle = {};
13040     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13041
13042     Roo.data.Store.superclass.constructor.call(this);
13043
13044     if(this.inlineData){
13045         this.loadData(this.inlineData);
13046         delete this.inlineData;
13047     }
13048 };
13049
13050 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13051      /**
13052     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13053     * without a remote query - used by combo/forms at present.
13054     */
13055     
13056     /**
13057     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13058     */
13059     /**
13060     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13061     */
13062     /**
13063     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13064     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13065     */
13066     /**
13067     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13068     * on any HTTP request
13069     */
13070     /**
13071     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13072     */
13073     /**
13074     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13075     */
13076     multiSort: false,
13077     /**
13078     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13079     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13080     */
13081     remoteSort : false,
13082
13083     /**
13084     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13085      * loaded or when a record is removed. (defaults to false).
13086     */
13087     pruneModifiedRecords : false,
13088
13089     // private
13090     lastOptions : null,
13091
13092     /**
13093      * Add Records to the Store and fires the add event.
13094      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13095      */
13096     add : function(records){
13097         records = [].concat(records);
13098         for(var i = 0, len = records.length; i < len; i++){
13099             records[i].join(this);
13100         }
13101         var index = this.data.length;
13102         this.data.addAll(records);
13103         this.fireEvent("add", this, records, index);
13104     },
13105
13106     /**
13107      * Remove a Record from the Store and fires the remove event.
13108      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13109      */
13110     remove : function(record){
13111         var index = this.data.indexOf(record);
13112         this.data.removeAt(index);
13113  
13114         if(this.pruneModifiedRecords){
13115             this.modified.remove(record);
13116         }
13117         this.fireEvent("remove", this, record, index);
13118     },
13119
13120     /**
13121      * Remove all Records from the Store and fires the clear event.
13122      */
13123     removeAll : function(){
13124         this.data.clear();
13125         if(this.pruneModifiedRecords){
13126             this.modified = [];
13127         }
13128         this.fireEvent("clear", this);
13129     },
13130
13131     /**
13132      * Inserts Records to the Store at the given index and fires the add event.
13133      * @param {Number} index The start index at which to insert the passed Records.
13134      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13135      */
13136     insert : function(index, records){
13137         records = [].concat(records);
13138         for(var i = 0, len = records.length; i < len; i++){
13139             this.data.insert(index, records[i]);
13140             records[i].join(this);
13141         }
13142         this.fireEvent("add", this, records, index);
13143     },
13144
13145     /**
13146      * Get the index within the cache of the passed Record.
13147      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13148      * @return {Number} The index of the passed Record. Returns -1 if not found.
13149      */
13150     indexOf : function(record){
13151         return this.data.indexOf(record);
13152     },
13153
13154     /**
13155      * Get the index within the cache of the Record with the passed id.
13156      * @param {String} id The id of the Record to find.
13157      * @return {Number} The index of the Record. Returns -1 if not found.
13158      */
13159     indexOfId : function(id){
13160         return this.data.indexOfKey(id);
13161     },
13162
13163     /**
13164      * Get the Record with the specified id.
13165      * @param {String} id The id of the Record to find.
13166      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13167      */
13168     getById : function(id){
13169         return this.data.key(id);
13170     },
13171
13172     /**
13173      * Get the Record at the specified index.
13174      * @param {Number} index The index of the Record to find.
13175      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13176      */
13177     getAt : function(index){
13178         return this.data.itemAt(index);
13179     },
13180
13181     /**
13182      * Returns a range of Records between specified indices.
13183      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13184      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13185      * @return {Roo.data.Record[]} An array of Records
13186      */
13187     getRange : function(start, end){
13188         return this.data.getRange(start, end);
13189     },
13190
13191     // private
13192     storeOptions : function(o){
13193         o = Roo.apply({}, o);
13194         delete o.callback;
13195         delete o.scope;
13196         this.lastOptions = o;
13197     },
13198
13199     /**
13200      * Loads the Record cache from the configured Proxy using the configured Reader.
13201      * <p>
13202      * If using remote paging, then the first load call must specify the <em>start</em>
13203      * and <em>limit</em> properties in the options.params property to establish the initial
13204      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13205      * <p>
13206      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13207      * and this call will return before the new data has been loaded. Perform any post-processing
13208      * in a callback function, or in a "load" event handler.</strong>
13209      * <p>
13210      * @param {Object} options An object containing properties which control loading options:<ul>
13211      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13212      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13213      * passed the following arguments:<ul>
13214      * <li>r : Roo.data.Record[]</li>
13215      * <li>options: Options object from the load call</li>
13216      * <li>success: Boolean success indicator</li></ul></li>
13217      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13218      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13219      * </ul>
13220      */
13221     load : function(options){
13222         options = options || {};
13223         if(this.fireEvent("beforeload", this, options) !== false){
13224             this.storeOptions(options);
13225             var p = Roo.apply(options.params || {}, this.baseParams);
13226             // if meta was not loaded from remote source.. try requesting it.
13227             if (!this.reader.metaFromRemote) {
13228                 p._requestMeta = 1;
13229             }
13230             if(this.sortInfo && this.remoteSort){
13231                 var pn = this.paramNames;
13232                 p[pn["sort"]] = this.sortInfo.field;
13233                 p[pn["dir"]] = this.sortInfo.direction;
13234             }
13235             if (this.multiSort) {
13236                 var pn = this.paramNames;
13237                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13238             }
13239             
13240             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13241         }
13242     },
13243
13244     /**
13245      * Reloads the Record cache from the configured Proxy using the configured Reader and
13246      * the options from the last load operation performed.
13247      * @param {Object} options (optional) An object containing properties which may override the options
13248      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13249      * the most recently used options are reused).
13250      */
13251     reload : function(options){
13252         this.load(Roo.applyIf(options||{}, this.lastOptions));
13253     },
13254
13255     // private
13256     // Called as a callback by the Reader during a load operation.
13257     loadRecords : function(o, options, success){
13258         if(!o || success === false){
13259             if(success !== false){
13260                 this.fireEvent("load", this, [], options, o);
13261             }
13262             if(options.callback){
13263                 options.callback.call(options.scope || this, [], options, false);
13264             }
13265             return;
13266         }
13267         // if data returned failure - throw an exception.
13268         if (o.success === false) {
13269             // show a message if no listener is registered.
13270             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13271                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13272             }
13273             // loadmask wil be hooked into this..
13274             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13275             return;
13276         }
13277         var r = o.records, t = o.totalRecords || r.length;
13278         
13279         this.fireEvent("beforeloadadd", this, r, options, o);
13280         
13281         if(!options || options.add !== true){
13282             if(this.pruneModifiedRecords){
13283                 this.modified = [];
13284             }
13285             for(var i = 0, len = r.length; i < len; i++){
13286                 r[i].join(this);
13287             }
13288             if(this.snapshot){
13289                 this.data = this.snapshot;
13290                 delete this.snapshot;
13291             }
13292             this.data.clear();
13293             this.data.addAll(r);
13294             this.totalLength = t;
13295             this.applySort();
13296             this.fireEvent("datachanged", this);
13297         }else{
13298             this.totalLength = Math.max(t, this.data.length+r.length);
13299             this.add(r);
13300         }
13301         
13302         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13303                 
13304             var e = new Roo.data.Record({});
13305
13306             e.set(this.parent.displayField, this.parent.emptyTitle);
13307             e.set(this.parent.valueField, '');
13308
13309             this.insert(0, e);
13310         }
13311             
13312         this.fireEvent("load", this, r, options, o);
13313         if(options.callback){
13314             options.callback.call(options.scope || this, r, options, true);
13315         }
13316     },
13317
13318
13319     /**
13320      * Loads data from a passed data block. A Reader which understands the format of the data
13321      * must have been configured in the constructor.
13322      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13323      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13324      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13325      */
13326     loadData : function(o, append){
13327         var r = this.reader.readRecords(o);
13328         this.loadRecords(r, {add: append}, true);
13329     },
13330     
13331      /**
13332      * using 'cn' the nested child reader read the child array into it's child stores.
13333      * @param {Object} rec The record with a 'children array
13334      */
13335     loadDataFromChildren : function(rec)
13336     {
13337         this.loadData(this.reader.toLoadData(rec));
13338     },
13339     
13340
13341     /**
13342      * Gets the number of cached records.
13343      * <p>
13344      * <em>If using paging, this may not be the total size of the dataset. If the data object
13345      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13346      * the data set size</em>
13347      */
13348     getCount : function(){
13349         return this.data.length || 0;
13350     },
13351
13352     /**
13353      * Gets the total number of records in the dataset as returned by the server.
13354      * <p>
13355      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13356      * the dataset size</em>
13357      */
13358     getTotalCount : function(){
13359         return this.totalLength || 0;
13360     },
13361
13362     /**
13363      * Returns the sort state of the Store as an object with two properties:
13364      * <pre><code>
13365  field {String} The name of the field by which the Records are sorted
13366  direction {String} The sort order, "ASC" or "DESC"
13367      * </code></pre>
13368      */
13369     getSortState : function(){
13370         return this.sortInfo;
13371     },
13372
13373     // private
13374     applySort : function(){
13375         if(this.sortInfo && !this.remoteSort){
13376             var s = this.sortInfo, f = s.field;
13377             var st = this.fields.get(f).sortType;
13378             var fn = function(r1, r2){
13379                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13380                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13381             };
13382             this.data.sort(s.direction, fn);
13383             if(this.snapshot && this.snapshot != this.data){
13384                 this.snapshot.sort(s.direction, fn);
13385             }
13386         }
13387     },
13388
13389     /**
13390      * Sets the default sort column and order to be used by the next load operation.
13391      * @param {String} fieldName The name of the field to sort by.
13392      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13393      */
13394     setDefaultSort : function(field, dir){
13395         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13396     },
13397
13398     /**
13399      * Sort the Records.
13400      * If remote sorting is used, the sort is performed on the server, and the cache is
13401      * reloaded. If local sorting is used, the cache is sorted internally.
13402      * @param {String} fieldName The name of the field to sort by.
13403      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13404      */
13405     sort : function(fieldName, dir){
13406         var f = this.fields.get(fieldName);
13407         if(!dir){
13408             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13409             
13410             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13411                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13412             }else{
13413                 dir = f.sortDir;
13414             }
13415         }
13416         this.sortToggle[f.name] = dir;
13417         this.sortInfo = {field: f.name, direction: dir};
13418         if(!this.remoteSort){
13419             this.applySort();
13420             this.fireEvent("datachanged", this);
13421         }else{
13422             this.load(this.lastOptions);
13423         }
13424     },
13425
13426     /**
13427      * Calls the specified function for each of the Records in the cache.
13428      * @param {Function} fn The function to call. The Record is passed as the first parameter.
13429      * Returning <em>false</em> aborts and exits the iteration.
13430      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13431      */
13432     each : function(fn, scope){
13433         this.data.each(fn, scope);
13434     },
13435
13436     /**
13437      * Gets all records modified since the last commit.  Modified records are persisted across load operations
13438      * (e.g., during paging).
13439      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13440      */
13441     getModifiedRecords : function(){
13442         return this.modified;
13443     },
13444
13445     // private
13446     createFilterFn : function(property, value, anyMatch){
13447         if(!value.exec){ // not a regex
13448             value = String(value);
13449             if(value.length == 0){
13450                 return false;
13451             }
13452             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13453         }
13454         return function(r){
13455             return value.test(r.data[property]);
13456         };
13457     },
13458
13459     /**
13460      * Sums the value of <i>property</i> for each record between start and end and returns the result.
13461      * @param {String} property A field on your records
13462      * @param {Number} start The record index to start at (defaults to 0)
13463      * @param {Number} end The last record index to include (defaults to length - 1)
13464      * @return {Number} The sum
13465      */
13466     sum : function(property, start, end){
13467         var rs = this.data.items, v = 0;
13468         start = start || 0;
13469         end = (end || end === 0) ? end : rs.length-1;
13470
13471         for(var i = start; i <= end; i++){
13472             v += (rs[i].data[property] || 0);
13473         }
13474         return v;
13475     },
13476
13477     /**
13478      * Filter the records by a specified property.
13479      * @param {String} field A field on your records
13480      * @param {String/RegExp} value Either a string that the field
13481      * should start with or a RegExp to test against the field
13482      * @param {Boolean} anyMatch True to match any part not just the beginning
13483      */
13484     filter : function(property, value, anyMatch){
13485         var fn = this.createFilterFn(property, value, anyMatch);
13486         return fn ? this.filterBy(fn) : this.clearFilter();
13487     },
13488
13489     /**
13490      * Filter by a function. The specified function will be called with each
13491      * record in this data source. If the function returns true the record is included,
13492      * otherwise it is filtered.
13493      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13494      * @param {Object} scope (optional) The scope of the function (defaults to this)
13495      */
13496     filterBy : function(fn, scope){
13497         this.snapshot = this.snapshot || this.data;
13498         this.data = this.queryBy(fn, scope||this);
13499         this.fireEvent("datachanged", this);
13500     },
13501
13502     /**
13503      * Query the records by a specified property.
13504      * @param {String} field A field on your records
13505      * @param {String/RegExp} value Either a string that the field
13506      * should start with or a RegExp to test against the field
13507      * @param {Boolean} anyMatch True to match any part not just the beginning
13508      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13509      */
13510     query : function(property, value, anyMatch){
13511         var fn = this.createFilterFn(property, value, anyMatch);
13512         return fn ? this.queryBy(fn) : this.data.clone();
13513     },
13514
13515     /**
13516      * Query by a function. The specified function will be called with each
13517      * record in this data source. If the function returns true the record is included
13518      * in the results.
13519      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13520      * @param {Object} scope (optional) The scope of the function (defaults to this)
13521       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13522      **/
13523     queryBy : function(fn, scope){
13524         var data = this.snapshot || this.data;
13525         return data.filterBy(fn, scope||this);
13526     },
13527
13528     /**
13529      * Collects unique values for a particular dataIndex from this store.
13530      * @param {String} dataIndex The property to collect
13531      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13532      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13533      * @return {Array} An array of the unique values
13534      **/
13535     collect : function(dataIndex, allowNull, bypassFilter){
13536         var d = (bypassFilter === true && this.snapshot) ?
13537                 this.snapshot.items : this.data.items;
13538         var v, sv, r = [], l = {};
13539         for(var i = 0, len = d.length; i < len; i++){
13540             v = d[i].data[dataIndex];
13541             sv = String(v);
13542             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13543                 l[sv] = true;
13544                 r[r.length] = v;
13545             }
13546         }
13547         return r;
13548     },
13549
13550     /**
13551      * Revert to a view of the Record cache with no filtering applied.
13552      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13553      */
13554     clearFilter : function(suppressEvent){
13555         if(this.snapshot && this.snapshot != this.data){
13556             this.data = this.snapshot;
13557             delete this.snapshot;
13558             if(suppressEvent !== true){
13559                 this.fireEvent("datachanged", this);
13560             }
13561         }
13562     },
13563
13564     // private
13565     afterEdit : function(record){
13566         if(this.modified.indexOf(record) == -1){
13567             this.modified.push(record);
13568         }
13569         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13570     },
13571     
13572     // private
13573     afterReject : function(record){
13574         this.modified.remove(record);
13575         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13576     },
13577
13578     // private
13579     afterCommit : function(record){
13580         this.modified.remove(record);
13581         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13582     },
13583
13584     /**
13585      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13586      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13587      */
13588     commitChanges : function(){
13589         var m = this.modified.slice(0);
13590         this.modified = [];
13591         for(var i = 0, len = m.length; i < len; i++){
13592             m[i].commit();
13593         }
13594     },
13595
13596     /**
13597      * Cancel outstanding changes on all changed records.
13598      */
13599     rejectChanges : function(){
13600         var m = this.modified.slice(0);
13601         this.modified = [];
13602         for(var i = 0, len = m.length; i < len; i++){
13603             m[i].reject();
13604         }
13605     },
13606
13607     onMetaChange : function(meta, rtype, o){
13608         this.recordType = rtype;
13609         this.fields = rtype.prototype.fields;
13610         delete this.snapshot;
13611         this.sortInfo = meta.sortInfo || this.sortInfo;
13612         this.modified = [];
13613         this.fireEvent('metachange', this, this.reader.meta);
13614     },
13615     
13616     moveIndex : function(data, type)
13617     {
13618         var index = this.indexOf(data);
13619         
13620         var newIndex = index + type;
13621         
13622         this.remove(data);
13623         
13624         this.insert(newIndex, data);
13625         
13626     }
13627 });/*
13628  * Based on:
13629  * Ext JS Library 1.1.1
13630  * Copyright(c) 2006-2007, Ext JS, LLC.
13631  *
13632  * Originally Released Under LGPL - original licence link has changed is not relivant.
13633  *
13634  * Fork - LGPL
13635  * <script type="text/javascript">
13636  */
13637
13638 /**
13639  * @class Roo.data.SimpleStore
13640  * @extends Roo.data.Store
13641  * Small helper class to make creating Stores from Array data easier.
13642  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13643  * @cfg {Array} fields An array of field definition objects, or field name strings.
13644  * @cfg {Object} an existing reader (eg. copied from another store)
13645  * @cfg {Array} data The multi-dimensional array of data
13646  * @constructor
13647  * @param {Object} config
13648  */
13649 Roo.data.SimpleStore = function(config)
13650 {
13651     Roo.data.SimpleStore.superclass.constructor.call(this, {
13652         isLocal : true,
13653         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13654                 id: config.id
13655             },
13656             Roo.data.Record.create(config.fields)
13657         ),
13658         proxy : new Roo.data.MemoryProxy(config.data)
13659     });
13660     this.load();
13661 };
13662 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13663  * Based on:
13664  * Ext JS Library 1.1.1
13665  * Copyright(c) 2006-2007, Ext JS, LLC.
13666  *
13667  * Originally Released Under LGPL - original licence link has changed is not relivant.
13668  *
13669  * Fork - LGPL
13670  * <script type="text/javascript">
13671  */
13672
13673 /**
13674 /**
13675  * @extends Roo.data.Store
13676  * @class Roo.data.JsonStore
13677  * Small helper class to make creating Stores for JSON data easier. <br/>
13678 <pre><code>
13679 var store = new Roo.data.JsonStore({
13680     url: 'get-images.php',
13681     root: 'images',
13682     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13683 });
13684 </code></pre>
13685  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13686  * JsonReader and HttpProxy (unless inline data is provided).</b>
13687  * @cfg {Array} fields An array of field definition objects, or field name strings.
13688  * @constructor
13689  * @param {Object} config
13690  */
13691 Roo.data.JsonStore = function(c){
13692     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13693         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13694         reader: new Roo.data.JsonReader(c, c.fields)
13695     }));
13696 };
13697 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13698  * Based on:
13699  * Ext JS Library 1.1.1
13700  * Copyright(c) 2006-2007, Ext JS, LLC.
13701  *
13702  * Originally Released Under LGPL - original licence link has changed is not relivant.
13703  *
13704  * Fork - LGPL
13705  * <script type="text/javascript">
13706  */
13707
13708  
13709 Roo.data.Field = function(config){
13710     if(typeof config == "string"){
13711         config = {name: config};
13712     }
13713     Roo.apply(this, config);
13714     
13715     if(!this.type){
13716         this.type = "auto";
13717     }
13718     
13719     var st = Roo.data.SortTypes;
13720     // named sortTypes are supported, here we look them up
13721     if(typeof this.sortType == "string"){
13722         this.sortType = st[this.sortType];
13723     }
13724     
13725     // set default sortType for strings and dates
13726     if(!this.sortType){
13727         switch(this.type){
13728             case "string":
13729                 this.sortType = st.asUCString;
13730                 break;
13731             case "date":
13732                 this.sortType = st.asDate;
13733                 break;
13734             default:
13735                 this.sortType = st.none;
13736         }
13737     }
13738
13739     // define once
13740     var stripRe = /[\$,%]/g;
13741
13742     // prebuilt conversion function for this field, instead of
13743     // switching every time we're reading a value
13744     if(!this.convert){
13745         var cv, dateFormat = this.dateFormat;
13746         switch(this.type){
13747             case "":
13748             case "auto":
13749             case undefined:
13750                 cv = function(v){ return v; };
13751                 break;
13752             case "string":
13753                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
13754                 break;
13755             case "int":
13756                 cv = function(v){
13757                     return v !== undefined && v !== null && v !== '' ?
13758                            parseInt(String(v).replace(stripRe, ""), 10) : '';
13759                     };
13760                 break;
13761             case "float":
13762                 cv = function(v){
13763                     return v !== undefined && v !== null && v !== '' ?
13764                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
13765                     };
13766                 break;
13767             case "bool":
13768             case "boolean":
13769                 cv = function(v){ return v === true || v === "true" || v == 1; };
13770                 break;
13771             case "date":
13772                 cv = function(v){
13773                     if(!v){
13774                         return '';
13775                     }
13776                     if(v instanceof Date){
13777                         return v;
13778                     }
13779                     if(dateFormat){
13780                         if(dateFormat == "timestamp"){
13781                             return new Date(v*1000);
13782                         }
13783                         return Date.parseDate(v, dateFormat);
13784                     }
13785                     var parsed = Date.parse(v);
13786                     return parsed ? new Date(parsed) : null;
13787                 };
13788              break;
13789             
13790         }
13791         this.convert = cv;
13792     }
13793 };
13794
13795 Roo.data.Field.prototype = {
13796     dateFormat: null,
13797     defaultValue: "",
13798     mapping: null,
13799     sortType : null,
13800     sortDir : "ASC"
13801 };/*
13802  * Based on:
13803  * Ext JS Library 1.1.1
13804  * Copyright(c) 2006-2007, Ext JS, LLC.
13805  *
13806  * Originally Released Under LGPL - original licence link has changed is not relivant.
13807  *
13808  * Fork - LGPL
13809  * <script type="text/javascript">
13810  */
13811  
13812 // Base class for reading structured data from a data source.  This class is intended to be
13813 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
13814
13815 /**
13816  * @class Roo.data.DataReader
13817  * Base class for reading structured data from a data source.  This class is intended to be
13818  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
13819  */
13820
13821 Roo.data.DataReader = function(meta, recordType){
13822     
13823     this.meta = meta;
13824     
13825     this.recordType = recordType instanceof Array ? 
13826         Roo.data.Record.create(recordType) : recordType;
13827 };
13828
13829 Roo.data.DataReader.prototype = {
13830     
13831     
13832     readerType : 'Data',
13833      /**
13834      * Create an empty record
13835      * @param {Object} data (optional) - overlay some values
13836      * @return {Roo.data.Record} record created.
13837      */
13838     newRow :  function(d) {
13839         var da =  {};
13840         this.recordType.prototype.fields.each(function(c) {
13841             switch( c.type) {
13842                 case 'int' : da[c.name] = 0; break;
13843                 case 'date' : da[c.name] = new Date(); break;
13844                 case 'float' : da[c.name] = 0.0; break;
13845                 case 'boolean' : da[c.name] = false; break;
13846                 default : da[c.name] = ""; break;
13847             }
13848             
13849         });
13850         return new this.recordType(Roo.apply(da, d));
13851     }
13852     
13853     
13854 };/*
13855  * Based on:
13856  * Ext JS Library 1.1.1
13857  * Copyright(c) 2006-2007, Ext JS, LLC.
13858  *
13859  * Originally Released Under LGPL - original licence link has changed is not relivant.
13860  *
13861  * Fork - LGPL
13862  * <script type="text/javascript">
13863  */
13864
13865 /**
13866  * @class Roo.data.DataProxy
13867  * @extends Roo.data.Observable
13868  * This class is an abstract base class for implementations which provide retrieval of
13869  * unformatted data objects.<br>
13870  * <p>
13871  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
13872  * (of the appropriate type which knows how to parse the data object) to provide a block of
13873  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
13874  * <p>
13875  * Custom implementations must implement the load method as described in
13876  * {@link Roo.data.HttpProxy#load}.
13877  */
13878 Roo.data.DataProxy = function(){
13879     this.addEvents({
13880         /**
13881          * @event beforeload
13882          * Fires before a network request is made to retrieve a data object.
13883          * @param {Object} This DataProxy object.
13884          * @param {Object} params The params parameter to the load function.
13885          */
13886         beforeload : true,
13887         /**
13888          * @event load
13889          * Fires before the load method's callback is called.
13890          * @param {Object} This DataProxy object.
13891          * @param {Object} o The data object.
13892          * @param {Object} arg The callback argument object passed to the load function.
13893          */
13894         load : true,
13895         /**
13896          * @event loadexception
13897          * Fires if an Exception occurs during data retrieval.
13898          * @param {Object} This DataProxy object.
13899          * @param {Object} o The data object.
13900          * @param {Object} arg The callback argument object passed to the load function.
13901          * @param {Object} e The Exception.
13902          */
13903         loadexception : true
13904     });
13905     Roo.data.DataProxy.superclass.constructor.call(this);
13906 };
13907
13908 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
13909
13910     /**
13911      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
13912      */
13913 /*
13914  * Based on:
13915  * Ext JS Library 1.1.1
13916  * Copyright(c) 2006-2007, Ext JS, LLC.
13917  *
13918  * Originally Released Under LGPL - original licence link has changed is not relivant.
13919  *
13920  * Fork - LGPL
13921  * <script type="text/javascript">
13922  */
13923 /**
13924  * @class Roo.data.MemoryProxy
13925  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
13926  * to the Reader when its load method is called.
13927  * @constructor
13928  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
13929  */
13930 Roo.data.MemoryProxy = function(data){
13931     if (data.data) {
13932         data = data.data;
13933     }
13934     Roo.data.MemoryProxy.superclass.constructor.call(this);
13935     this.data = data;
13936 };
13937
13938 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
13939     
13940     /**
13941      * Load data from the requested source (in this case an in-memory
13942      * data object passed to the constructor), read the data object into
13943      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
13944      * process that block using the passed callback.
13945      * @param {Object} params This parameter is not used by the MemoryProxy class.
13946      * @param {Roo.data.DataReader} reader The Reader object which converts the data
13947      * object into a block of Roo.data.Records.
13948      * @param {Function} callback The function into which to pass the block of Roo.data.records.
13949      * The function must be passed <ul>
13950      * <li>The Record block object</li>
13951      * <li>The "arg" argument from the load function</li>
13952      * <li>A boolean success indicator</li>
13953      * </ul>
13954      * @param {Object} scope The scope in which to call the callback
13955      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
13956      */
13957     load : function(params, reader, callback, scope, arg){
13958         params = params || {};
13959         var result;
13960         try {
13961             result = reader.readRecords(params.data ? params.data :this.data);
13962         }catch(e){
13963             this.fireEvent("loadexception", this, arg, null, e);
13964             callback.call(scope, null, arg, false);
13965             return;
13966         }
13967         callback.call(scope, result, arg, true);
13968     },
13969     
13970     // private
13971     update : function(params, records){
13972         
13973     }
13974 });/*
13975  * Based on:
13976  * Ext JS Library 1.1.1
13977  * Copyright(c) 2006-2007, Ext JS, LLC.
13978  *
13979  * Originally Released Under LGPL - original licence link has changed is not relivant.
13980  *
13981  * Fork - LGPL
13982  * <script type="text/javascript">
13983  */
13984 /**
13985  * @class Roo.data.HttpProxy
13986  * @extends Roo.data.DataProxy
13987  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
13988  * configured to reference a certain URL.<br><br>
13989  * <p>
13990  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
13991  * from which the running page was served.<br><br>
13992  * <p>
13993  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
13994  * <p>
13995  * Be aware that to enable the browser to parse an XML document, the server must set
13996  * the Content-Type header in the HTTP response to "text/xml".
13997  * @constructor
13998  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
13999  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
14000  * will be used to make the request.
14001  */
14002 Roo.data.HttpProxy = function(conn){
14003     Roo.data.HttpProxy.superclass.constructor.call(this);
14004     // is conn a conn config or a real conn?
14005     this.conn = conn;
14006     this.useAjax = !conn || !conn.events;
14007   
14008 };
14009
14010 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14011     // thse are take from connection...
14012     
14013     /**
14014      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14015      */
14016     /**
14017      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14018      * extra parameters to each request made by this object. (defaults to undefined)
14019      */
14020     /**
14021      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14022      *  to each request made by this object. (defaults to undefined)
14023      */
14024     /**
14025      * @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)
14026      */
14027     /**
14028      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14029      */
14030      /**
14031      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14032      * @type Boolean
14033      */
14034   
14035
14036     /**
14037      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14038      * @type Boolean
14039      */
14040     /**
14041      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14042      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14043      * a finer-grained basis than the DataProxy events.
14044      */
14045     getConnection : function(){
14046         return this.useAjax ? Roo.Ajax : this.conn;
14047     },
14048
14049     /**
14050      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14051      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14052      * process that block using the passed callback.
14053      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14054      * for the request to the remote server.
14055      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14056      * object into a block of Roo.data.Records.
14057      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14058      * The function must be passed <ul>
14059      * <li>The Record block object</li>
14060      * <li>The "arg" argument from the load function</li>
14061      * <li>A boolean success indicator</li>
14062      * </ul>
14063      * @param {Object} scope The scope in which to call the callback
14064      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14065      */
14066     load : function(params, reader, callback, scope, arg){
14067         if(this.fireEvent("beforeload", this, params) !== false){
14068             var  o = {
14069                 params : params || {},
14070                 request: {
14071                     callback : callback,
14072                     scope : scope,
14073                     arg : arg
14074                 },
14075                 reader: reader,
14076                 callback : this.loadResponse,
14077                 scope: this
14078             };
14079             if(this.useAjax){
14080                 Roo.applyIf(o, this.conn);
14081                 if(this.activeRequest){
14082                     Roo.Ajax.abort(this.activeRequest);
14083                 }
14084                 this.activeRequest = Roo.Ajax.request(o);
14085             }else{
14086                 this.conn.request(o);
14087             }
14088         }else{
14089             callback.call(scope||this, null, arg, false);
14090         }
14091     },
14092
14093     // private
14094     loadResponse : function(o, success, response){
14095         delete this.activeRequest;
14096         if(!success){
14097             this.fireEvent("loadexception", this, o, response);
14098             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14099             return;
14100         }
14101         var result;
14102         try {
14103             result = o.reader.read(response);
14104         }catch(e){
14105             this.fireEvent("loadexception", this, o, response, e);
14106             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14107             return;
14108         }
14109         
14110         this.fireEvent("load", this, o, o.request.arg);
14111         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14112     },
14113
14114     // private
14115     update : function(dataSet){
14116
14117     },
14118
14119     // private
14120     updateResponse : function(dataSet){
14121
14122     }
14123 });/*
14124  * Based on:
14125  * Ext JS Library 1.1.1
14126  * Copyright(c) 2006-2007, Ext JS, LLC.
14127  *
14128  * Originally Released Under LGPL - original licence link has changed is not relivant.
14129  *
14130  * Fork - LGPL
14131  * <script type="text/javascript">
14132  */
14133
14134 /**
14135  * @class Roo.data.ScriptTagProxy
14136  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14137  * other than the originating domain of the running page.<br><br>
14138  * <p>
14139  * <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
14140  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14141  * <p>
14142  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14143  * source code that is used as the source inside a &lt;script> tag.<br><br>
14144  * <p>
14145  * In order for the browser to process the returned data, the server must wrap the data object
14146  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14147  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14148  * depending on whether the callback name was passed:
14149  * <p>
14150  * <pre><code>
14151 boolean scriptTag = false;
14152 String cb = request.getParameter("callback");
14153 if (cb != null) {
14154     scriptTag = true;
14155     response.setContentType("text/javascript");
14156 } else {
14157     response.setContentType("application/x-json");
14158 }
14159 Writer out = response.getWriter();
14160 if (scriptTag) {
14161     out.write(cb + "(");
14162 }
14163 out.print(dataBlock.toJsonString());
14164 if (scriptTag) {
14165     out.write(");");
14166 }
14167 </pre></code>
14168  *
14169  * @constructor
14170  * @param {Object} config A configuration object.
14171  */
14172 Roo.data.ScriptTagProxy = function(config){
14173     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14174     Roo.apply(this, config);
14175     this.head = document.getElementsByTagName("head")[0];
14176 };
14177
14178 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14179
14180 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14181     /**
14182      * @cfg {String} url The URL from which to request the data object.
14183      */
14184     /**
14185      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14186      */
14187     timeout : 30000,
14188     /**
14189      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14190      * the server the name of the callback function set up by the load call to process the returned data object.
14191      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14192      * javascript output which calls this named function passing the data object as its only parameter.
14193      */
14194     callbackParam : "callback",
14195     /**
14196      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14197      * name to the request.
14198      */
14199     nocache : true,
14200
14201     /**
14202      * Load data from the configured URL, read the data object into
14203      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14204      * process that block using the passed callback.
14205      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14206      * for the request to the remote server.
14207      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14208      * object into a block of Roo.data.Records.
14209      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14210      * The function must be passed <ul>
14211      * <li>The Record block object</li>
14212      * <li>The "arg" argument from the load function</li>
14213      * <li>A boolean success indicator</li>
14214      * </ul>
14215      * @param {Object} scope The scope in which to call the callback
14216      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14217      */
14218     load : function(params, reader, callback, scope, arg){
14219         if(this.fireEvent("beforeload", this, params) !== false){
14220
14221             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14222
14223             var url = this.url;
14224             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14225             if(this.nocache){
14226                 url += "&_dc=" + (new Date().getTime());
14227             }
14228             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14229             var trans = {
14230                 id : transId,
14231                 cb : "stcCallback"+transId,
14232                 scriptId : "stcScript"+transId,
14233                 params : params,
14234                 arg : arg,
14235                 url : url,
14236                 callback : callback,
14237                 scope : scope,
14238                 reader : reader
14239             };
14240             var conn = this;
14241
14242             window[trans.cb] = function(o){
14243                 conn.handleResponse(o, trans);
14244             };
14245
14246             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14247
14248             if(this.autoAbort !== false){
14249                 this.abort();
14250             }
14251
14252             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14253
14254             var script = document.createElement("script");
14255             script.setAttribute("src", url);
14256             script.setAttribute("type", "text/javascript");
14257             script.setAttribute("id", trans.scriptId);
14258             this.head.appendChild(script);
14259
14260             this.trans = trans;
14261         }else{
14262             callback.call(scope||this, null, arg, false);
14263         }
14264     },
14265
14266     // private
14267     isLoading : function(){
14268         return this.trans ? true : false;
14269     },
14270
14271     /**
14272      * Abort the current server request.
14273      */
14274     abort : function(){
14275         if(this.isLoading()){
14276             this.destroyTrans(this.trans);
14277         }
14278     },
14279
14280     // private
14281     destroyTrans : function(trans, isLoaded){
14282         this.head.removeChild(document.getElementById(trans.scriptId));
14283         clearTimeout(trans.timeoutId);
14284         if(isLoaded){
14285             window[trans.cb] = undefined;
14286             try{
14287                 delete window[trans.cb];
14288             }catch(e){}
14289         }else{
14290             // if hasn't been loaded, wait for load to remove it to prevent script error
14291             window[trans.cb] = function(){
14292                 window[trans.cb] = undefined;
14293                 try{
14294                     delete window[trans.cb];
14295                 }catch(e){}
14296             };
14297         }
14298     },
14299
14300     // private
14301     handleResponse : function(o, trans){
14302         this.trans = false;
14303         this.destroyTrans(trans, true);
14304         var result;
14305         try {
14306             result = trans.reader.readRecords(o);
14307         }catch(e){
14308             this.fireEvent("loadexception", this, o, trans.arg, e);
14309             trans.callback.call(trans.scope||window, null, trans.arg, false);
14310             return;
14311         }
14312         this.fireEvent("load", this, o, trans.arg);
14313         trans.callback.call(trans.scope||window, result, trans.arg, true);
14314     },
14315
14316     // private
14317     handleFailure : function(trans){
14318         this.trans = false;
14319         this.destroyTrans(trans, false);
14320         this.fireEvent("loadexception", this, null, trans.arg);
14321         trans.callback.call(trans.scope||window, null, trans.arg, false);
14322     }
14323 });/*
14324  * Based on:
14325  * Ext JS Library 1.1.1
14326  * Copyright(c) 2006-2007, Ext JS, LLC.
14327  *
14328  * Originally Released Under LGPL - original licence link has changed is not relivant.
14329  *
14330  * Fork - LGPL
14331  * <script type="text/javascript">
14332  */
14333
14334 /**
14335  * @class Roo.data.JsonReader
14336  * @extends Roo.data.DataReader
14337  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14338  * based on mappings in a provided Roo.data.Record constructor.
14339  * 
14340  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14341  * in the reply previously. 
14342  * 
14343  * <p>
14344  * Example code:
14345  * <pre><code>
14346 var RecordDef = Roo.data.Record.create([
14347     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14348     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14349 ]);
14350 var myReader = new Roo.data.JsonReader({
14351     totalProperty: "results",    // The property which contains the total dataset size (optional)
14352     root: "rows",                // The property which contains an Array of row objects
14353     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14354 }, RecordDef);
14355 </code></pre>
14356  * <p>
14357  * This would consume a JSON file like this:
14358  * <pre><code>
14359 { 'results': 2, 'rows': [
14360     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14361     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14362 }
14363 </code></pre>
14364  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14365  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14366  * paged from the remote server.
14367  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14368  * @cfg {String} root name of the property which contains the Array of row objects.
14369  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14370  * @cfg {Array} fields Array of field definition objects
14371  * @constructor
14372  * Create a new JsonReader
14373  * @param {Object} meta Metadata configuration options
14374  * @param {Object} recordType Either an Array of field definition objects,
14375  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14376  */
14377 Roo.data.JsonReader = function(meta, recordType){
14378     
14379     meta = meta || {};
14380     // set some defaults:
14381     Roo.applyIf(meta, {
14382         totalProperty: 'total',
14383         successProperty : 'success',
14384         root : 'data',
14385         id : 'id'
14386     });
14387     
14388     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14389 };
14390 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14391     
14392     readerType : 'Json',
14393     
14394     /**
14395      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
14396      * Used by Store query builder to append _requestMeta to params.
14397      * 
14398      */
14399     metaFromRemote : false,
14400     /**
14401      * This method is only used by a DataProxy which has retrieved data from a remote server.
14402      * @param {Object} response The XHR object which contains the JSON data in its responseText.
14403      * @return {Object} data A data block which is used by an Roo.data.Store object as
14404      * a cache of Roo.data.Records.
14405      */
14406     read : function(response){
14407         var json = response.responseText;
14408        
14409         var o = /* eval:var:o */ eval("("+json+")");
14410         if(!o) {
14411             throw {message: "JsonReader.read: Json object not found"};
14412         }
14413         
14414         if(o.metaData){
14415             
14416             delete this.ef;
14417             this.metaFromRemote = true;
14418             this.meta = o.metaData;
14419             this.recordType = Roo.data.Record.create(o.metaData.fields);
14420             this.onMetaChange(this.meta, this.recordType, o);
14421         }
14422         return this.readRecords(o);
14423     },
14424
14425     // private function a store will implement
14426     onMetaChange : function(meta, recordType, o){
14427
14428     },
14429
14430     /**
14431          * @ignore
14432          */
14433     simpleAccess: function(obj, subsc) {
14434         return obj[subsc];
14435     },
14436
14437         /**
14438          * @ignore
14439          */
14440     getJsonAccessor: function(){
14441         var re = /[\[\.]/;
14442         return function(expr) {
14443             try {
14444                 return(re.test(expr))
14445                     ? new Function("obj", "return obj." + expr)
14446                     : function(obj){
14447                         return obj[expr];
14448                     };
14449             } catch(e){}
14450             return Roo.emptyFn;
14451         };
14452     }(),
14453
14454     /**
14455      * Create a data block containing Roo.data.Records from an XML document.
14456      * @param {Object} o An object which contains an Array of row objects in the property specified
14457      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14458      * which contains the total size of the dataset.
14459      * @return {Object} data A data block which is used by an Roo.data.Store object as
14460      * a cache of Roo.data.Records.
14461      */
14462     readRecords : function(o){
14463         /**
14464          * After any data loads, the raw JSON data is available for further custom processing.
14465          * @type Object
14466          */
14467         this.o = o;
14468         var s = this.meta, Record = this.recordType,
14469             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14470
14471 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
14472         if (!this.ef) {
14473             if(s.totalProperty) {
14474                     this.getTotal = this.getJsonAccessor(s.totalProperty);
14475                 }
14476                 if(s.successProperty) {
14477                     this.getSuccess = this.getJsonAccessor(s.successProperty);
14478                 }
14479                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14480                 if (s.id) {
14481                         var g = this.getJsonAccessor(s.id);
14482                         this.getId = function(rec) {
14483                                 var r = g(rec);  
14484                                 return (r === undefined || r === "") ? null : r;
14485                         };
14486                 } else {
14487                         this.getId = function(){return null;};
14488                 }
14489             this.ef = [];
14490             for(var jj = 0; jj < fl; jj++){
14491                 f = fi[jj];
14492                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14493                 this.ef[jj] = this.getJsonAccessor(map);
14494             }
14495         }
14496
14497         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14498         if(s.totalProperty){
14499             var vt = parseInt(this.getTotal(o), 10);
14500             if(!isNaN(vt)){
14501                 totalRecords = vt;
14502             }
14503         }
14504         if(s.successProperty){
14505             var vs = this.getSuccess(o);
14506             if(vs === false || vs === 'false'){
14507                 success = false;
14508             }
14509         }
14510         var records = [];
14511         for(var i = 0; i < c; i++){
14512                 var n = root[i];
14513             var values = {};
14514             var id = this.getId(n);
14515             for(var j = 0; j < fl; j++){
14516                 f = fi[j];
14517             var v = this.ef[j](n);
14518             if (!f.convert) {
14519                 Roo.log('missing convert for ' + f.name);
14520                 Roo.log(f);
14521                 continue;
14522             }
14523             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14524             }
14525             var record = new Record(values, id);
14526             record.json = n;
14527             records[i] = record;
14528         }
14529         return {
14530             raw : o,
14531             success : success,
14532             records : records,
14533             totalRecords : totalRecords
14534         };
14535     },
14536     // used when loading children.. @see loadDataFromChildren
14537     toLoadData: function(rec)
14538     {
14539         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14540         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14541         return { data : data, total : data.length };
14542         
14543     }
14544 });/*
14545  * Based on:
14546  * Ext JS Library 1.1.1
14547  * Copyright(c) 2006-2007, Ext JS, LLC.
14548  *
14549  * Originally Released Under LGPL - original licence link has changed is not relivant.
14550  *
14551  * Fork - LGPL
14552  * <script type="text/javascript">
14553  */
14554
14555 /**
14556  * @class Roo.data.ArrayReader
14557  * @extends Roo.data.DataReader
14558  * Data reader class to create an Array of Roo.data.Record objects from an Array.
14559  * Each element of that Array represents a row of data fields. The
14560  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14561  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14562  * <p>
14563  * Example code:.
14564  * <pre><code>
14565 var RecordDef = Roo.data.Record.create([
14566     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
14567     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
14568 ]);
14569 var myReader = new Roo.data.ArrayReader({
14570     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
14571 }, RecordDef);
14572 </code></pre>
14573  * <p>
14574  * This would consume an Array like this:
14575  * <pre><code>
14576 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14577   </code></pre>
14578  
14579  * @constructor
14580  * Create a new JsonReader
14581  * @param {Object} meta Metadata configuration options.
14582  * @param {Object|Array} recordType Either an Array of field definition objects
14583  * 
14584  * @cfg {Array} fields Array of field definition objects
14585  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14586  * as specified to {@link Roo.data.Record#create},
14587  * or an {@link Roo.data.Record} object
14588  *
14589  * 
14590  * created using {@link Roo.data.Record#create}.
14591  */
14592 Roo.data.ArrayReader = function(meta, recordType)
14593 {    
14594     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14595 };
14596
14597 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14598     
14599       /**
14600      * Create a data block containing Roo.data.Records from an XML document.
14601      * @param {Object} o An Array of row objects which represents the dataset.
14602      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14603      * a cache of Roo.data.Records.
14604      */
14605     readRecords : function(o)
14606     {
14607         var sid = this.meta ? this.meta.id : null;
14608         var recordType = this.recordType, fields = recordType.prototype.fields;
14609         var records = [];
14610         var root = o;
14611         for(var i = 0; i < root.length; i++){
14612                 var n = root[i];
14613             var values = {};
14614             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14615             for(var j = 0, jlen = fields.length; j < jlen; j++){
14616                 var f = fields.items[j];
14617                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14618                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14619                 v = f.convert(v);
14620                 values[f.name] = v;
14621             }
14622             var record = new recordType(values, id);
14623             record.json = n;
14624             records[records.length] = record;
14625         }
14626         return {
14627             records : records,
14628             totalRecords : records.length
14629         };
14630     },
14631     // used when loading children.. @see loadDataFromChildren
14632     toLoadData: function(rec)
14633     {
14634         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14635         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14636         
14637     }
14638     
14639     
14640 });/*
14641  * - LGPL
14642  * * 
14643  */
14644
14645 /**
14646  * @class Roo.bootstrap.ComboBox
14647  * @extends Roo.bootstrap.TriggerField
14648  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14649  * @cfg {Boolean} append (true|false) default false
14650  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14651  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14652  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14653  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14654  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14655  * @cfg {Boolean} animate default true
14656  * @cfg {Boolean} emptyResultText only for touch device
14657  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14658  * @cfg {String} emptyTitle default ''
14659  * @constructor
14660  * Create a new ComboBox.
14661  * @param {Object} config Configuration options
14662  */
14663 Roo.bootstrap.ComboBox = function(config){
14664     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14665     this.addEvents({
14666         /**
14667          * @event expand
14668          * Fires when the dropdown list is expanded
14669         * @param {Roo.bootstrap.ComboBox} combo This combo box
14670         */
14671         'expand' : true,
14672         /**
14673          * @event collapse
14674          * Fires when the dropdown list is collapsed
14675         * @param {Roo.bootstrap.ComboBox} combo This combo box
14676         */
14677         'collapse' : true,
14678         /**
14679          * @event beforeselect
14680          * Fires before a list item is selected. Return false to cancel the selection.
14681         * @param {Roo.bootstrap.ComboBox} combo This combo box
14682         * @param {Roo.data.Record} record The data record returned from the underlying store
14683         * @param {Number} index The index of the selected item in the dropdown list
14684         */
14685         'beforeselect' : true,
14686         /**
14687          * @event select
14688          * Fires when a list item is selected
14689         * @param {Roo.bootstrap.ComboBox} combo This combo box
14690         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14691         * @param {Number} index The index of the selected item in the dropdown list
14692         */
14693         'select' : true,
14694         /**
14695          * @event beforequery
14696          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14697          * The event object passed has these properties:
14698         * @param {Roo.bootstrap.ComboBox} combo This combo box
14699         * @param {String} query The query
14700         * @param {Boolean} forceAll true to force "all" query
14701         * @param {Boolean} cancel true to cancel the query
14702         * @param {Object} e The query event object
14703         */
14704         'beforequery': true,
14705          /**
14706          * @event add
14707          * Fires when the 'add' icon is pressed (add a listener to enable add button)
14708         * @param {Roo.bootstrap.ComboBox} combo This combo box
14709         */
14710         'add' : true,
14711         /**
14712          * @event edit
14713          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14714         * @param {Roo.bootstrap.ComboBox} combo This combo box
14715         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14716         */
14717         'edit' : true,
14718         /**
14719          * @event remove
14720          * Fires when the remove value from the combobox array
14721         * @param {Roo.bootstrap.ComboBox} combo This combo box
14722         */
14723         'remove' : true,
14724         /**
14725          * @event afterremove
14726          * Fires when the remove value from the combobox array
14727         * @param {Roo.bootstrap.ComboBox} combo This combo box
14728         */
14729         'afterremove' : true,
14730         /**
14731          * @event specialfilter
14732          * Fires when specialfilter
14733             * @param {Roo.bootstrap.ComboBox} combo This combo box
14734             */
14735         'specialfilter' : true,
14736         /**
14737          * @event tick
14738          * Fires when tick the element
14739             * @param {Roo.bootstrap.ComboBox} combo This combo box
14740             */
14741         'tick' : true,
14742         /**
14743          * @event touchviewdisplay
14744          * Fires when touch view require special display (default is using displayField)
14745             * @param {Roo.bootstrap.ComboBox} combo This combo box
14746             * @param {Object} cfg set html .
14747             */
14748         'touchviewdisplay' : true
14749         
14750     });
14751     
14752     this.item = [];
14753     this.tickItems = [];
14754     
14755     this.selectedIndex = -1;
14756     if(this.mode == 'local'){
14757         if(config.queryDelay === undefined){
14758             this.queryDelay = 10;
14759         }
14760         if(config.minChars === undefined){
14761             this.minChars = 0;
14762         }
14763     }
14764 };
14765
14766 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
14767      
14768     /**
14769      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
14770      * rendering into an Roo.Editor, defaults to false)
14771      */
14772     /**
14773      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
14774      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
14775      */
14776     /**
14777      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
14778      */
14779     /**
14780      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
14781      * the dropdown list (defaults to undefined, with no header element)
14782      */
14783
14784      /**
14785      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
14786      */
14787      
14788      /**
14789      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
14790      */
14791     listWidth: undefined,
14792     /**
14793      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
14794      * mode = 'remote' or 'text' if mode = 'local')
14795      */
14796     displayField: undefined,
14797     
14798     /**
14799      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
14800      * mode = 'remote' or 'value' if mode = 'local'). 
14801      * Note: use of a valueField requires the user make a selection
14802      * in order for a value to be mapped.
14803      */
14804     valueField: undefined,
14805     /**
14806      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
14807      */
14808     modalTitle : '',
14809     
14810     /**
14811      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
14812      * field's data value (defaults to the underlying DOM element's name)
14813      */
14814     hiddenName: undefined,
14815     /**
14816      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
14817      */
14818     listClass: '',
14819     /**
14820      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
14821      */
14822     selectedClass: 'active',
14823     
14824     /**
14825      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14826      */
14827     shadow:'sides',
14828     /**
14829      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
14830      * anchor positions (defaults to 'tl-bl')
14831      */
14832     listAlign: 'tl-bl?',
14833     /**
14834      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
14835      */
14836     maxHeight: 300,
14837     /**
14838      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
14839      * query specified by the allQuery config option (defaults to 'query')
14840      */
14841     triggerAction: 'query',
14842     /**
14843      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
14844      * (defaults to 4, does not apply if editable = false)
14845      */
14846     minChars : 4,
14847     /**
14848      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
14849      * delay (typeAheadDelay) if it matches a known value (defaults to false)
14850      */
14851     typeAhead: false,
14852     /**
14853      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
14854      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
14855      */
14856     queryDelay: 500,
14857     /**
14858      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
14859      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
14860      */
14861     pageSize: 0,
14862     /**
14863      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
14864      * when editable = true (defaults to false)
14865      */
14866     selectOnFocus:false,
14867     /**
14868      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
14869      */
14870     queryParam: 'query',
14871     /**
14872      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
14873      * when mode = 'remote' (defaults to 'Loading...')
14874      */
14875     loadingText: 'Loading...',
14876     /**
14877      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
14878      */
14879     resizable: false,
14880     /**
14881      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
14882      */
14883     handleHeight : 8,
14884     /**
14885      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
14886      * traditional select (defaults to true)
14887      */
14888     editable: true,
14889     /**
14890      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
14891      */
14892     allQuery: '',
14893     /**
14894      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
14895      */
14896     mode: 'remote',
14897     /**
14898      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
14899      * listWidth has a higher value)
14900      */
14901     minListWidth : 70,
14902     /**
14903      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
14904      * allow the user to set arbitrary text into the field (defaults to false)
14905      */
14906     forceSelection:false,
14907     /**
14908      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
14909      * if typeAhead = true (defaults to 250)
14910      */
14911     typeAheadDelay : 250,
14912     /**
14913      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
14914      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
14915      */
14916     valueNotFoundText : undefined,
14917     /**
14918      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
14919      */
14920     blockFocus : false,
14921     
14922     /**
14923      * @cfg {Boolean} disableClear Disable showing of clear button.
14924      */
14925     disableClear : false,
14926     /**
14927      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
14928      */
14929     alwaysQuery : false,
14930     
14931     /**
14932      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
14933      */
14934     multiple : false,
14935     
14936     /**
14937      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
14938      */
14939     invalidClass : "has-warning",
14940     
14941     /**
14942      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
14943      */
14944     validClass : "has-success",
14945     
14946     /**
14947      * @cfg {Boolean} specialFilter (true|false) special filter default false
14948      */
14949     specialFilter : false,
14950     
14951     /**
14952      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
14953      */
14954     mobileTouchView : true,
14955     
14956     /**
14957      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
14958      */
14959     useNativeIOS : false,
14960     
14961     /**
14962      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
14963      */
14964     mobile_restrict_height : false,
14965     
14966     ios_options : false,
14967     
14968     //private
14969     addicon : false,
14970     editicon: false,
14971     
14972     page: 0,
14973     hasQuery: false,
14974     append: false,
14975     loadNext: false,
14976     autoFocus : true,
14977     tickable : false,
14978     btnPosition : 'right',
14979     triggerList : true,
14980     showToggleBtn : true,
14981     animate : true,
14982     emptyResultText: 'Empty',
14983     triggerText : 'Select',
14984     emptyTitle : '',
14985     
14986     // element that contains real text value.. (when hidden is used..)
14987     
14988     getAutoCreate : function()
14989     {   
14990         var cfg = false;
14991         //render
14992         /*
14993          * Render classic select for iso
14994          */
14995         
14996         if(Roo.isIOS && this.useNativeIOS){
14997             cfg = this.getAutoCreateNativeIOS();
14998             return cfg;
14999         }
15000         
15001         /*
15002          * Touch Devices
15003          */
15004         
15005         if(Roo.isTouch && this.mobileTouchView){
15006             cfg = this.getAutoCreateTouchView();
15007             return cfg;;
15008         }
15009         
15010         /*
15011          *  Normal ComboBox
15012          */
15013         if(!this.tickable){
15014             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15015             return cfg;
15016         }
15017         
15018         /*
15019          *  ComboBox with tickable selections
15020          */
15021              
15022         var align = this.labelAlign || this.parentLabelAlign();
15023         
15024         cfg = {
15025             cls : 'form-group roo-combobox-tickable' //input-group
15026         };
15027         
15028         var btn_text_select = '';
15029         var btn_text_done = '';
15030         var btn_text_cancel = '';
15031         
15032         if (this.btn_text_show) {
15033             btn_text_select = 'Select';
15034             btn_text_done = 'Done';
15035             btn_text_cancel = 'Cancel'; 
15036         }
15037         
15038         var buttons = {
15039             tag : 'div',
15040             cls : 'tickable-buttons',
15041             cn : [
15042                 {
15043                     tag : 'button',
15044                     type : 'button',
15045                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15046                     //html : this.triggerText
15047                     html: btn_text_select
15048                 },
15049                 {
15050                     tag : 'button',
15051                     type : 'button',
15052                     name : 'ok',
15053                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15054                     //html : 'Done'
15055                     html: btn_text_done
15056                 },
15057                 {
15058                     tag : 'button',
15059                     type : 'button',
15060                     name : 'cancel',
15061                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15062                     //html : 'Cancel'
15063                     html: btn_text_cancel
15064                 }
15065             ]
15066         };
15067         
15068         if(this.editable){
15069             buttons.cn.unshift({
15070                 tag: 'input',
15071                 cls: 'roo-select2-search-field-input'
15072             });
15073         }
15074         
15075         var _this = this;
15076         
15077         Roo.each(buttons.cn, function(c){
15078             if (_this.size) {
15079                 c.cls += ' btn-' + _this.size;
15080             }
15081
15082             if (_this.disabled) {
15083                 c.disabled = true;
15084             }
15085         });
15086         
15087         var box = {
15088             tag: 'div',
15089             style : 'display: contents',
15090             cn: [
15091                 {
15092                     tag: 'input',
15093                     type : 'hidden',
15094                     cls: 'form-hidden-field'
15095                 },
15096                 {
15097                     tag: 'ul',
15098                     cls: 'roo-select2-choices',
15099                     cn:[
15100                         {
15101                             tag: 'li',
15102                             cls: 'roo-select2-search-field',
15103                             cn: [
15104                                 buttons
15105                             ]
15106                         }
15107                     ]
15108                 }
15109             ]
15110         };
15111         
15112         var combobox = {
15113             cls: 'roo-select2-container input-group roo-select2-container-multi',
15114             cn: [
15115                 
15116                 box
15117 //                {
15118 //                    tag: 'ul',
15119 //                    cls: 'typeahead typeahead-long dropdown-menu',
15120 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15121 //                }
15122             ]
15123         };
15124         
15125         if(this.hasFeedback && !this.allowBlank){
15126             
15127             var feedback = {
15128                 tag: 'span',
15129                 cls: 'glyphicon form-control-feedback'
15130             };
15131
15132             combobox.cn.push(feedback);
15133         }
15134         
15135         
15136         
15137         var indicator = {
15138             tag : 'i',
15139             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15140             tooltip : 'This field is required'
15141         };
15142         if (Roo.bootstrap.version == 4) {
15143             indicator = {
15144                 tag : 'i',
15145                 style : 'display:none'
15146             };
15147         }
15148         if (align ==='left' && this.fieldLabel.length) {
15149             
15150             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15151             
15152             cfg.cn = [
15153                 indicator,
15154                 {
15155                     tag: 'label',
15156                     'for' :  id,
15157                     cls : 'control-label col-form-label',
15158                     html : this.fieldLabel
15159
15160                 },
15161                 {
15162                     cls : "", 
15163                     cn: [
15164                         combobox
15165                     ]
15166                 }
15167
15168             ];
15169             
15170             var labelCfg = cfg.cn[1];
15171             var contentCfg = cfg.cn[2];
15172             
15173
15174             if(this.indicatorpos == 'right'){
15175                 
15176                 cfg.cn = [
15177                     {
15178                         tag: 'label',
15179                         'for' :  id,
15180                         cls : 'control-label col-form-label',
15181                         cn : [
15182                             {
15183                                 tag : 'span',
15184                                 html : this.fieldLabel
15185                             },
15186                             indicator
15187                         ]
15188                     },
15189                     {
15190                         cls : "",
15191                         cn: [
15192                             combobox
15193                         ]
15194                     }
15195
15196                 ];
15197                 
15198                 
15199                 
15200                 labelCfg = cfg.cn[0];
15201                 contentCfg = cfg.cn[1];
15202             
15203             }
15204             
15205             if(this.labelWidth > 12){
15206                 labelCfg.style = "width: " + this.labelWidth + 'px';
15207             }
15208             
15209             if(this.labelWidth < 13 && this.labelmd == 0){
15210                 this.labelmd = this.labelWidth;
15211             }
15212             
15213             if(this.labellg > 0){
15214                 labelCfg.cls += ' col-lg-' + this.labellg;
15215                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15216             }
15217             
15218             if(this.labelmd > 0){
15219                 labelCfg.cls += ' col-md-' + this.labelmd;
15220                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15221             }
15222             
15223             if(this.labelsm > 0){
15224                 labelCfg.cls += ' col-sm-' + this.labelsm;
15225                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15226             }
15227             
15228             if(this.labelxs > 0){
15229                 labelCfg.cls += ' col-xs-' + this.labelxs;
15230                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15231             }
15232                 
15233                 
15234         } else if ( this.fieldLabel.length) {
15235 //                Roo.log(" label");
15236                  cfg.cn = [
15237                    indicator,
15238                     {
15239                         tag: 'label',
15240                         //cls : 'input-group-addon',
15241                         html : this.fieldLabel
15242                     },
15243                     combobox
15244                 ];
15245                 
15246                 if(this.indicatorpos == 'right'){
15247                     cfg.cn = [
15248                         {
15249                             tag: 'label',
15250                             //cls : 'input-group-addon',
15251                             html : this.fieldLabel
15252                         },
15253                         indicator,
15254                         combobox
15255                     ];
15256                     
15257                 }
15258
15259         } else {
15260             
15261 //                Roo.log(" no label && no align");
15262                 cfg = combobox
15263                      
15264                 
15265         }
15266          
15267         var settings=this;
15268         ['xs','sm','md','lg'].map(function(size){
15269             if (settings[size]) {
15270                 cfg.cls += ' col-' + size + '-' + settings[size];
15271             }
15272         });
15273         
15274         return cfg;
15275         
15276     },
15277     
15278     _initEventsCalled : false,
15279     
15280     // private
15281     initEvents: function()
15282     {   
15283         if (this._initEventsCalled) { // as we call render... prevent looping...
15284             return;
15285         }
15286         this._initEventsCalled = true;
15287         
15288         if (!this.store) {
15289             throw "can not find store for combo";
15290         }
15291         
15292         this.indicator = this.indicatorEl();
15293         
15294         this.store = Roo.factory(this.store, Roo.data);
15295         this.store.parent = this;
15296         
15297         // if we are building from html. then this element is so complex, that we can not really
15298         // use the rendered HTML.
15299         // so we have to trash and replace the previous code.
15300         if (Roo.XComponent.build_from_html) {
15301             // remove this element....
15302             var e = this.el.dom, k=0;
15303             while (e ) { e = e.previousSibling;  ++k;}
15304
15305             this.el.remove();
15306             
15307             this.el=false;
15308             this.rendered = false;
15309             
15310             this.render(this.parent().getChildContainer(true), k);
15311         }
15312         
15313         if(Roo.isIOS && this.useNativeIOS){
15314             this.initIOSView();
15315             return;
15316         }
15317         
15318         /*
15319          * Touch Devices
15320          */
15321         
15322         if(Roo.isTouch && this.mobileTouchView){
15323             this.initTouchView();
15324             return;
15325         }
15326         
15327         if(this.tickable){
15328             this.initTickableEvents();
15329             return;
15330         }
15331         
15332         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15333         
15334         if(this.hiddenName){
15335             
15336             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15337             
15338             this.hiddenField.dom.value =
15339                 this.hiddenValue !== undefined ? this.hiddenValue :
15340                 this.value !== undefined ? this.value : '';
15341
15342             // prevent input submission
15343             this.el.dom.removeAttribute('name');
15344             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15345              
15346              
15347         }
15348         //if(Roo.isGecko){
15349         //    this.el.dom.setAttribute('autocomplete', 'off');
15350         //}
15351         
15352         var cls = 'x-combo-list';
15353         
15354         //this.list = new Roo.Layer({
15355         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15356         //});
15357         
15358         var _this = this;
15359         
15360         (function(){
15361             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15362             _this.list.setWidth(lw);
15363         }).defer(100);
15364         
15365         this.list.on('mouseover', this.onViewOver, this);
15366         this.list.on('mousemove', this.onViewMove, this);
15367         this.list.on('scroll', this.onViewScroll, this);
15368         
15369         /*
15370         this.list.swallowEvent('mousewheel');
15371         this.assetHeight = 0;
15372
15373         if(this.title){
15374             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15375             this.assetHeight += this.header.getHeight();
15376         }
15377
15378         this.innerList = this.list.createChild({cls:cls+'-inner'});
15379         this.innerList.on('mouseover', this.onViewOver, this);
15380         this.innerList.on('mousemove', this.onViewMove, this);
15381         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15382         
15383         if(this.allowBlank && !this.pageSize && !this.disableClear){
15384             this.footer = this.list.createChild({cls:cls+'-ft'});
15385             this.pageTb = new Roo.Toolbar(this.footer);
15386            
15387         }
15388         if(this.pageSize){
15389             this.footer = this.list.createChild({cls:cls+'-ft'});
15390             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15391                     {pageSize: this.pageSize});
15392             
15393         }
15394         
15395         if (this.pageTb && this.allowBlank && !this.disableClear) {
15396             var _this = this;
15397             this.pageTb.add(new Roo.Toolbar.Fill(), {
15398                 cls: 'x-btn-icon x-btn-clear',
15399                 text: '&#160;',
15400                 handler: function()
15401                 {
15402                     _this.collapse();
15403                     _this.clearValue();
15404                     _this.onSelect(false, -1);
15405                 }
15406             });
15407         }
15408         if (this.footer) {
15409             this.assetHeight += this.footer.getHeight();
15410         }
15411         */
15412             
15413         if(!this.tpl){
15414             this.tpl = Roo.bootstrap.version == 4 ?
15415                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
15416                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15417         }
15418
15419         this.view = new Roo.View(this.list, this.tpl, {
15420             singleSelect:true, store: this.store, selectedClass: this.selectedClass
15421         });
15422         //this.view.wrapEl.setDisplayed(false);
15423         this.view.on('click', this.onViewClick, this);
15424         
15425         
15426         this.store.on('beforeload', this.onBeforeLoad, this);
15427         this.store.on('load', this.onLoad, this);
15428         this.store.on('loadexception', this.onLoadException, this);
15429         /*
15430         if(this.resizable){
15431             this.resizer = new Roo.Resizable(this.list,  {
15432                pinned:true, handles:'se'
15433             });
15434             this.resizer.on('resize', function(r, w, h){
15435                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15436                 this.listWidth = w;
15437                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15438                 this.restrictHeight();
15439             }, this);
15440             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15441         }
15442         */
15443         if(!this.editable){
15444             this.editable = true;
15445             this.setEditable(false);
15446         }
15447         
15448         /*
15449         
15450         if (typeof(this.events.add.listeners) != 'undefined') {
15451             
15452             this.addicon = this.wrap.createChild(
15453                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
15454        
15455             this.addicon.on('click', function(e) {
15456                 this.fireEvent('add', this);
15457             }, this);
15458         }
15459         if (typeof(this.events.edit.listeners) != 'undefined') {
15460             
15461             this.editicon = this.wrap.createChild(
15462                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
15463             if (this.addicon) {
15464                 this.editicon.setStyle('margin-left', '40px');
15465             }
15466             this.editicon.on('click', function(e) {
15467                 
15468                 // we fire even  if inothing is selected..
15469                 this.fireEvent('edit', this, this.lastData );
15470                 
15471             }, this);
15472         }
15473         */
15474         
15475         this.keyNav = new Roo.KeyNav(this.inputEl(), {
15476             "up" : function(e){
15477                 this.inKeyMode = true;
15478                 this.selectPrev();
15479             },
15480
15481             "down" : function(e){
15482                 if(!this.isExpanded()){
15483                     this.onTriggerClick();
15484                 }else{
15485                     this.inKeyMode = true;
15486                     this.selectNext();
15487                 }
15488             },
15489
15490             "enter" : function(e){
15491 //                this.onViewClick();
15492                 //return true;
15493                 this.collapse();
15494                 
15495                 if(this.fireEvent("specialkey", this, e)){
15496                     this.onViewClick(false);
15497                 }
15498                 
15499                 return true;
15500             },
15501
15502             "esc" : function(e){
15503                 this.collapse();
15504             },
15505
15506             "tab" : function(e){
15507                 this.collapse();
15508                 
15509                 if(this.fireEvent("specialkey", this, e)){
15510                     this.onViewClick(false);
15511                 }
15512                 
15513                 return true;
15514             },
15515
15516             scope : this,
15517
15518             doRelay : function(foo, bar, hname){
15519                 if(hname == 'down' || this.scope.isExpanded()){
15520                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15521                 }
15522                 return true;
15523             },
15524
15525             forceKeyDown: true
15526         });
15527         
15528         
15529         this.queryDelay = Math.max(this.queryDelay || 10,
15530                 this.mode == 'local' ? 10 : 250);
15531         
15532         
15533         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15534         
15535         if(this.typeAhead){
15536             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15537         }
15538         if(this.editable !== false){
15539             this.inputEl().on("keyup", this.onKeyUp, this);
15540         }
15541         if(this.forceSelection){
15542             this.inputEl().on('blur', this.doForce, this);
15543         }
15544         
15545         if(this.multiple){
15546             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15547             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15548         }
15549     },
15550     
15551     initTickableEvents: function()
15552     {   
15553         this.createList();
15554         
15555         if(this.hiddenName){
15556             
15557             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15558             
15559             this.hiddenField.dom.value =
15560                 this.hiddenValue !== undefined ? this.hiddenValue :
15561                 this.value !== undefined ? this.value : '';
15562
15563             // prevent input submission
15564             this.el.dom.removeAttribute('name');
15565             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15566              
15567              
15568         }
15569         
15570 //        this.list = this.el.select('ul.dropdown-menu',true).first();
15571         
15572         this.choices = this.el.select('ul.roo-select2-choices', true).first();
15573         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15574         if(this.triggerList){
15575             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15576         }
15577          
15578         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15579         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15580         
15581         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15582         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15583         
15584         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15585         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15586         
15587         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15588         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15589         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15590         
15591         this.okBtn.hide();
15592         this.cancelBtn.hide();
15593         
15594         var _this = this;
15595         
15596         (function(){
15597             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15598             _this.list.setWidth(lw);
15599         }).defer(100);
15600         
15601         this.list.on('mouseover', this.onViewOver, this);
15602         this.list.on('mousemove', this.onViewMove, this);
15603         
15604         this.list.on('scroll', this.onViewScroll, this);
15605         
15606         if(!this.tpl){
15607             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
15608                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15609         }
15610
15611         this.view = new Roo.View(this.list, this.tpl, {
15612             singleSelect:true,
15613             tickable:true,
15614             parent:this,
15615             store: this.store,
15616             selectedClass: this.selectedClass
15617         });
15618         
15619         //this.view.wrapEl.setDisplayed(false);
15620         this.view.on('click', this.onViewClick, this);
15621         
15622         
15623         
15624         this.store.on('beforeload', this.onBeforeLoad, this);
15625         this.store.on('load', this.onLoad, this);
15626         this.store.on('loadexception', this.onLoadException, this);
15627         
15628         if(this.editable){
15629             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15630                 "up" : function(e){
15631                     this.inKeyMode = true;
15632                     this.selectPrev();
15633                 },
15634
15635                 "down" : function(e){
15636                     this.inKeyMode = true;
15637                     this.selectNext();
15638                 },
15639
15640                 "enter" : function(e){
15641                     if(this.fireEvent("specialkey", this, e)){
15642                         this.onViewClick(false);
15643                     }
15644                     
15645                     return true;
15646                 },
15647
15648                 "esc" : function(e){
15649                     this.onTickableFooterButtonClick(e, false, false);
15650                 },
15651
15652                 "tab" : function(e){
15653                     this.fireEvent("specialkey", this, e);
15654                     
15655                     this.onTickableFooterButtonClick(e, false, false);
15656                     
15657                     return true;
15658                 },
15659
15660                 scope : this,
15661
15662                 doRelay : function(e, fn, key){
15663                     if(this.scope.isExpanded()){
15664                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15665                     }
15666                     return true;
15667                 },
15668
15669                 forceKeyDown: true
15670             });
15671         }
15672         
15673         this.queryDelay = Math.max(this.queryDelay || 10,
15674                 this.mode == 'local' ? 10 : 250);
15675         
15676         
15677         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15678         
15679         if(this.typeAhead){
15680             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15681         }
15682         
15683         if(this.editable !== false){
15684             this.tickableInputEl().on("keyup", this.onKeyUp, this);
15685         }
15686         
15687         this.indicator = this.indicatorEl();
15688         
15689         if(this.indicator){
15690             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15691             this.indicator.hide();
15692         }
15693         
15694     },
15695
15696     onDestroy : function(){
15697         if(this.view){
15698             this.view.setStore(null);
15699             this.view.el.removeAllListeners();
15700             this.view.el.remove();
15701             this.view.purgeListeners();
15702         }
15703         if(this.list){
15704             this.list.dom.innerHTML  = '';
15705         }
15706         
15707         if(this.store){
15708             this.store.un('beforeload', this.onBeforeLoad, this);
15709             this.store.un('load', this.onLoad, this);
15710             this.store.un('loadexception', this.onLoadException, this);
15711         }
15712         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15713     },
15714
15715     // private
15716     fireKey : function(e){
15717         if(e.isNavKeyPress() && !this.list.isVisible()){
15718             this.fireEvent("specialkey", this, e);
15719         }
15720     },
15721
15722     // private
15723     onResize: function(w, h){
15724 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
15725 //        
15726 //        if(typeof w != 'number'){
15727 //            // we do not handle it!?!?
15728 //            return;
15729 //        }
15730 //        var tw = this.trigger.getWidth();
15731 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
15732 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
15733 //        var x = w - tw;
15734 //        this.inputEl().setWidth( this.adjustWidth('input', x));
15735 //            
15736 //        //this.trigger.setStyle('left', x+'px');
15737 //        
15738 //        if(this.list && this.listWidth === undefined){
15739 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
15740 //            this.list.setWidth(lw);
15741 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15742 //        }
15743         
15744     
15745         
15746     },
15747
15748     /**
15749      * Allow or prevent the user from directly editing the field text.  If false is passed,
15750      * the user will only be able to select from the items defined in the dropdown list.  This method
15751      * is the runtime equivalent of setting the 'editable' config option at config time.
15752      * @param {Boolean} value True to allow the user to directly edit the field text
15753      */
15754     setEditable : function(value){
15755         if(value == this.editable){
15756             return;
15757         }
15758         this.editable = value;
15759         if(!value){
15760             this.inputEl().dom.setAttribute('readOnly', true);
15761             this.inputEl().on('mousedown', this.onTriggerClick,  this);
15762             this.inputEl().addClass('x-combo-noedit');
15763         }else{
15764             this.inputEl().dom.setAttribute('readOnly', false);
15765             this.inputEl().un('mousedown', this.onTriggerClick,  this);
15766             this.inputEl().removeClass('x-combo-noedit');
15767         }
15768     },
15769
15770     // private
15771     
15772     onBeforeLoad : function(combo,opts){
15773         if(!this.hasFocus){
15774             return;
15775         }
15776          if (!opts.add) {
15777             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
15778          }
15779         this.restrictHeight();
15780         this.selectedIndex = -1;
15781     },
15782
15783     // private
15784     onLoad : function(){
15785         
15786         this.hasQuery = false;
15787         
15788         if(!this.hasFocus){
15789             return;
15790         }
15791         
15792         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
15793             this.loading.hide();
15794         }
15795         
15796         if(this.store.getCount() > 0){
15797             
15798             this.expand();
15799             this.restrictHeight();
15800             if(this.lastQuery == this.allQuery){
15801                 if(this.editable && !this.tickable){
15802                     this.inputEl().dom.select();
15803                 }
15804                 
15805                 if(
15806                     !this.selectByValue(this.value, true) &&
15807                     this.autoFocus && 
15808                     (
15809                         !this.store.lastOptions ||
15810                         typeof(this.store.lastOptions.add) == 'undefined' || 
15811                         this.store.lastOptions.add != true
15812                     )
15813                 ){
15814                     this.select(0, true);
15815                 }
15816             }else{
15817                 if(this.autoFocus){
15818                     this.selectNext();
15819                 }
15820                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
15821                     this.taTask.delay(this.typeAheadDelay);
15822                 }
15823             }
15824         }else{
15825             this.onEmptyResults();
15826         }
15827         
15828         //this.el.focus();
15829     },
15830     // private
15831     onLoadException : function()
15832     {
15833         this.hasQuery = false;
15834         
15835         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
15836             this.loading.hide();
15837         }
15838         
15839         if(this.tickable && this.editable){
15840             return;
15841         }
15842         
15843         this.collapse();
15844         // only causes errors at present
15845         //Roo.log(this.store.reader.jsonData);
15846         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
15847             // fixme
15848             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
15849         //}
15850         
15851         
15852     },
15853     // private
15854     onTypeAhead : function(){
15855         if(this.store.getCount() > 0){
15856             var r = this.store.getAt(0);
15857             var newValue = r.data[this.displayField];
15858             var len = newValue.length;
15859             var selStart = this.getRawValue().length;
15860             
15861             if(selStart != len){
15862                 this.setRawValue(newValue);
15863                 this.selectText(selStart, newValue.length);
15864             }
15865         }
15866     },
15867
15868     // private
15869     onSelect : function(record, index){
15870         
15871         if(this.fireEvent('beforeselect', this, record, index) !== false){
15872         
15873             this.setFromData(index > -1 ? record.data : false);
15874             
15875             this.collapse();
15876             this.fireEvent('select', this, record, index);
15877         }
15878     },
15879
15880     /**
15881      * Returns the currently selected field value or empty string if no value is set.
15882      * @return {String} value The selected value
15883      */
15884     getValue : function()
15885     {
15886         if(Roo.isIOS && this.useNativeIOS){
15887             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
15888         }
15889         
15890         if(this.multiple){
15891             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
15892         }
15893         
15894         if(this.valueField){
15895             return typeof this.value != 'undefined' ? this.value : '';
15896         }else{
15897             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
15898         }
15899     },
15900     
15901     getRawValue : function()
15902     {
15903         if(Roo.isIOS && this.useNativeIOS){
15904             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
15905         }
15906         
15907         var v = this.inputEl().getValue();
15908         
15909         return v;
15910     },
15911
15912     /**
15913      * Clears any text/value currently set in the field
15914      */
15915     clearValue : function(){
15916         
15917         if(this.hiddenField){
15918             this.hiddenField.dom.value = '';
15919         }
15920         this.value = '';
15921         this.setRawValue('');
15922         this.lastSelectionText = '';
15923         this.lastData = false;
15924         
15925         var close = this.closeTriggerEl();
15926         
15927         if(close){
15928             close.hide();
15929         }
15930         
15931         this.validate();
15932         
15933     },
15934
15935     /**
15936      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
15937      * will be displayed in the field.  If the value does not match the data value of an existing item,
15938      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
15939      * Otherwise the field will be blank (although the value will still be set).
15940      * @param {String} value The value to match
15941      */
15942     setValue : function(v)
15943     {
15944         if(Roo.isIOS && this.useNativeIOS){
15945             this.setIOSValue(v);
15946             return;
15947         }
15948         
15949         if(this.multiple){
15950             this.syncValue();
15951             return;
15952         }
15953         
15954         var text = v;
15955         if(this.valueField){
15956             var r = this.findRecord(this.valueField, v);
15957             if(r){
15958                 text = r.data[this.displayField];
15959             }else if(this.valueNotFoundText !== undefined){
15960                 text = this.valueNotFoundText;
15961             }
15962         }
15963         this.lastSelectionText = text;
15964         if(this.hiddenField){
15965             this.hiddenField.dom.value = v;
15966         }
15967         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
15968         this.value = v;
15969         
15970         var close = this.closeTriggerEl();
15971         
15972         if(close){
15973             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
15974         }
15975         
15976         this.validate();
15977     },
15978     /**
15979      * @property {Object} the last set data for the element
15980      */
15981     
15982     lastData : false,
15983     /**
15984      * Sets the value of the field based on a object which is related to the record format for the store.
15985      * @param {Object} value the value to set as. or false on reset?
15986      */
15987     setFromData : function(o){
15988         
15989         if(this.multiple){
15990             this.addItem(o);
15991             return;
15992         }
15993             
15994         var dv = ''; // display value
15995         var vv = ''; // value value..
15996         this.lastData = o;
15997         if (this.displayField) {
15998             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
15999         } else {
16000             // this is an error condition!!!
16001             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16002         }
16003         
16004         if(this.valueField){
16005             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16006         }
16007         
16008         var close = this.closeTriggerEl();
16009         
16010         if(close){
16011             if(dv.length || vv * 1 > 0){
16012                 close.show() ;
16013                 this.blockFocus=true;
16014             } else {
16015                 close.hide();
16016             }             
16017         }
16018         
16019         if(this.hiddenField){
16020             this.hiddenField.dom.value = vv;
16021             
16022             this.lastSelectionText = dv;
16023             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16024             this.value = vv;
16025             return;
16026         }
16027         // no hidden field.. - we store the value in 'value', but still display
16028         // display field!!!!
16029         this.lastSelectionText = dv;
16030         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16031         this.value = vv;
16032         
16033         
16034         
16035     },
16036     // private
16037     reset : function(){
16038         // overridden so that last data is reset..
16039         
16040         if(this.multiple){
16041             this.clearItem();
16042             return;
16043         }
16044         
16045         this.setValue(this.originalValue);
16046         //this.clearInvalid();
16047         this.lastData = false;
16048         if (this.view) {
16049             this.view.clearSelections();
16050         }
16051         
16052         this.validate();
16053     },
16054     // private
16055     findRecord : function(prop, value){
16056         var record;
16057         if(this.store.getCount() > 0){
16058             this.store.each(function(r){
16059                 if(r.data[prop] == value){
16060                     record = r;
16061                     return false;
16062                 }
16063                 return true;
16064             });
16065         }
16066         return record;
16067     },
16068     
16069     getName: function()
16070     {
16071         // returns hidden if it's set..
16072         if (!this.rendered) {return ''};
16073         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16074         
16075     },
16076     // private
16077     onViewMove : function(e, t){
16078         this.inKeyMode = false;
16079     },
16080
16081     // private
16082     onViewOver : function(e, t){
16083         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16084             return;
16085         }
16086         var item = this.view.findItemFromChild(t);
16087         
16088         if(item){
16089             var index = this.view.indexOf(item);
16090             this.select(index, false);
16091         }
16092     },
16093
16094     // private
16095     onViewClick : function(view, doFocus, el, e)
16096     {
16097         var index = this.view.getSelectedIndexes()[0];
16098         
16099         var r = this.store.getAt(index);
16100         
16101         if(this.tickable){
16102             
16103             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16104                 return;
16105             }
16106             
16107             var rm = false;
16108             var _this = this;
16109             
16110             Roo.each(this.tickItems, function(v,k){
16111                 
16112                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16113                     Roo.log(v);
16114                     _this.tickItems.splice(k, 1);
16115                     
16116                     if(typeof(e) == 'undefined' && view == false){
16117                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16118                     }
16119                     
16120                     rm = true;
16121                     return;
16122                 }
16123             });
16124             
16125             if(rm){
16126                 return;
16127             }
16128             
16129             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16130                 this.tickItems.push(r.data);
16131             }
16132             
16133             if(typeof(e) == 'undefined' && view == false){
16134                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16135             }
16136                     
16137             return;
16138         }
16139         
16140         if(r){
16141             this.onSelect(r, index);
16142         }
16143         if(doFocus !== false && !this.blockFocus){
16144             this.inputEl().focus();
16145         }
16146     },
16147
16148     // private
16149     restrictHeight : function(){
16150         //this.innerList.dom.style.height = '';
16151         //var inner = this.innerList.dom;
16152         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16153         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16154         //this.list.beginUpdate();
16155         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16156         this.list.alignTo(this.inputEl(), this.listAlign);
16157         this.list.alignTo(this.inputEl(), this.listAlign);
16158         //this.list.endUpdate();
16159     },
16160
16161     // private
16162     onEmptyResults : function(){
16163         
16164         if(this.tickable && this.editable){
16165             this.hasFocus = false;
16166             this.restrictHeight();
16167             return;
16168         }
16169         
16170         this.collapse();
16171     },
16172
16173     /**
16174      * Returns true if the dropdown list is expanded, else false.
16175      */
16176     isExpanded : function(){
16177         return this.list.isVisible();
16178     },
16179
16180     /**
16181      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16182      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16183      * @param {String} value The data value of the item to select
16184      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16185      * selected item if it is not currently in view (defaults to true)
16186      * @return {Boolean} True if the value matched an item in the list, else false
16187      */
16188     selectByValue : function(v, scrollIntoView){
16189         if(v !== undefined && v !== null){
16190             var r = this.findRecord(this.valueField || this.displayField, v);
16191             if(r){
16192                 this.select(this.store.indexOf(r), scrollIntoView);
16193                 return true;
16194             }
16195         }
16196         return false;
16197     },
16198
16199     /**
16200      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16201      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16202      * @param {Number} index The zero-based index of the list item to select
16203      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16204      * selected item if it is not currently in view (defaults to true)
16205      */
16206     select : function(index, scrollIntoView){
16207         this.selectedIndex = index;
16208         this.view.select(index);
16209         if(scrollIntoView !== false){
16210             var el = this.view.getNode(index);
16211             /*
16212              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16213              */
16214             if(el){
16215                 this.list.scrollChildIntoView(el, false);
16216             }
16217         }
16218     },
16219
16220     // private
16221     selectNext : function(){
16222         var ct = this.store.getCount();
16223         if(ct > 0){
16224             if(this.selectedIndex == -1){
16225                 this.select(0);
16226             }else if(this.selectedIndex < ct-1){
16227                 this.select(this.selectedIndex+1);
16228             }
16229         }
16230     },
16231
16232     // private
16233     selectPrev : function(){
16234         var ct = this.store.getCount();
16235         if(ct > 0){
16236             if(this.selectedIndex == -1){
16237                 this.select(0);
16238             }else if(this.selectedIndex != 0){
16239                 this.select(this.selectedIndex-1);
16240             }
16241         }
16242     },
16243
16244     // private
16245     onKeyUp : function(e){
16246         if(this.editable !== false && !e.isSpecialKey()){
16247             this.lastKey = e.getKey();
16248             this.dqTask.delay(this.queryDelay);
16249         }
16250     },
16251
16252     // private
16253     validateBlur : function(){
16254         return !this.list || !this.list.isVisible();   
16255     },
16256
16257     // private
16258     initQuery : function(){
16259         
16260         var v = this.getRawValue();
16261         
16262         if(this.tickable && this.editable){
16263             v = this.tickableInputEl().getValue();
16264         }
16265         
16266         this.doQuery(v);
16267     },
16268
16269     // private
16270     doForce : function(){
16271         if(this.inputEl().dom.value.length > 0){
16272             this.inputEl().dom.value =
16273                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16274              
16275         }
16276     },
16277
16278     /**
16279      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16280      * query allowing the query action to be canceled if needed.
16281      * @param {String} query The SQL query to execute
16282      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16283      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16284      * saved in the current store (defaults to false)
16285      */
16286     doQuery : function(q, forceAll){
16287         
16288         if(q === undefined || q === null){
16289             q = '';
16290         }
16291         var qe = {
16292             query: q,
16293             forceAll: forceAll,
16294             combo: this,
16295             cancel:false
16296         };
16297         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16298             return false;
16299         }
16300         q = qe.query;
16301         
16302         forceAll = qe.forceAll;
16303         if(forceAll === true || (q.length >= this.minChars)){
16304             
16305             this.hasQuery = true;
16306             
16307             if(this.lastQuery != q || this.alwaysQuery){
16308                 this.lastQuery = q;
16309                 if(this.mode == 'local'){
16310                     this.selectedIndex = -1;
16311                     if(forceAll){
16312                         this.store.clearFilter();
16313                     }else{
16314                         
16315                         if(this.specialFilter){
16316                             this.fireEvent('specialfilter', this);
16317                             this.onLoad();
16318                             return;
16319                         }
16320                         
16321                         this.store.filter(this.displayField, q);
16322                     }
16323                     
16324                     this.store.fireEvent("datachanged", this.store);
16325                     
16326                     this.onLoad();
16327                     
16328                     
16329                 }else{
16330                     
16331                     this.store.baseParams[this.queryParam] = q;
16332                     
16333                     var options = {params : this.getParams(q)};
16334                     
16335                     if(this.loadNext){
16336                         options.add = true;
16337                         options.params.start = this.page * this.pageSize;
16338                     }
16339                     
16340                     this.store.load(options);
16341                     
16342                     /*
16343                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16344                      *  we should expand the list on onLoad
16345                      *  so command out it
16346                      */
16347 //                    this.expand();
16348                 }
16349             }else{
16350                 this.selectedIndex = -1;
16351                 this.onLoad();   
16352             }
16353         }
16354         
16355         this.loadNext = false;
16356     },
16357     
16358     // private
16359     getParams : function(q){
16360         var p = {};
16361         //p[this.queryParam] = q;
16362         
16363         if(this.pageSize){
16364             p.start = 0;
16365             p.limit = this.pageSize;
16366         }
16367         return p;
16368     },
16369
16370     /**
16371      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16372      */
16373     collapse : function(){
16374         if(!this.isExpanded()){
16375             return;
16376         }
16377         
16378         this.list.hide();
16379         
16380         this.hasFocus = false;
16381         
16382         if(this.tickable){
16383             this.okBtn.hide();
16384             this.cancelBtn.hide();
16385             this.trigger.show();
16386             
16387             if(this.editable){
16388                 this.tickableInputEl().dom.value = '';
16389                 this.tickableInputEl().blur();
16390             }
16391             
16392         }
16393         
16394         Roo.get(document).un('mousedown', this.collapseIf, this);
16395         Roo.get(document).un('mousewheel', this.collapseIf, this);
16396         if (!this.editable) {
16397             Roo.get(document).un('keydown', this.listKeyPress, this);
16398         }
16399         this.fireEvent('collapse', this);
16400         
16401         this.validate();
16402     },
16403
16404     // private
16405     collapseIf : function(e){
16406         var in_combo  = e.within(this.el);
16407         var in_list =  e.within(this.list);
16408         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16409         
16410         if (in_combo || in_list || is_list) {
16411             //e.stopPropagation();
16412             return;
16413         }
16414         
16415         if(this.tickable){
16416             this.onTickableFooterButtonClick(e, false, false);
16417         }
16418
16419         this.collapse();
16420         
16421     },
16422
16423     /**
16424      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16425      */
16426     expand : function(){
16427        
16428         if(this.isExpanded() || !this.hasFocus){
16429             return;
16430         }
16431         
16432         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16433         this.list.setWidth(lw);
16434         
16435         Roo.log('expand');
16436         
16437         this.list.show();
16438         
16439         this.restrictHeight();
16440         
16441         if(this.tickable){
16442             
16443             this.tickItems = Roo.apply([], this.item);
16444             
16445             this.okBtn.show();
16446             this.cancelBtn.show();
16447             this.trigger.hide();
16448             
16449             if(this.editable){
16450                 this.tickableInputEl().focus();
16451             }
16452             
16453         }
16454         
16455         Roo.get(document).on('mousedown', this.collapseIf, this);
16456         Roo.get(document).on('mousewheel', this.collapseIf, this);
16457         if (!this.editable) {
16458             Roo.get(document).on('keydown', this.listKeyPress, this);
16459         }
16460         
16461         this.fireEvent('expand', this);
16462     },
16463
16464     // private
16465     // Implements the default empty TriggerField.onTriggerClick function
16466     onTriggerClick : function(e)
16467     {
16468         Roo.log('trigger click');
16469         
16470         if(this.disabled || !this.triggerList){
16471             return;
16472         }
16473         
16474         this.page = 0;
16475         this.loadNext = false;
16476         
16477         if(this.isExpanded()){
16478             this.collapse();
16479             if (!this.blockFocus) {
16480                 this.inputEl().focus();
16481             }
16482             
16483         }else {
16484             this.hasFocus = true;
16485             if(this.triggerAction == 'all') {
16486                 this.doQuery(this.allQuery, true);
16487             } else {
16488                 this.doQuery(this.getRawValue());
16489             }
16490             if (!this.blockFocus) {
16491                 this.inputEl().focus();
16492             }
16493         }
16494     },
16495     
16496     onTickableTriggerClick : function(e)
16497     {
16498         if(this.disabled){
16499             return;
16500         }
16501         
16502         this.page = 0;
16503         this.loadNext = false;
16504         this.hasFocus = true;
16505         
16506         if(this.triggerAction == 'all') {
16507             this.doQuery(this.allQuery, true);
16508         } else {
16509             this.doQuery(this.getRawValue());
16510         }
16511     },
16512     
16513     onSearchFieldClick : function(e)
16514     {
16515         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16516             this.onTickableFooterButtonClick(e, false, false);
16517             return;
16518         }
16519         
16520         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16521             return;
16522         }
16523         
16524         this.page = 0;
16525         this.loadNext = false;
16526         this.hasFocus = true;
16527         
16528         if(this.triggerAction == 'all') {
16529             this.doQuery(this.allQuery, true);
16530         } else {
16531             this.doQuery(this.getRawValue());
16532         }
16533     },
16534     
16535     listKeyPress : function(e)
16536     {
16537         //Roo.log('listkeypress');
16538         // scroll to first matching element based on key pres..
16539         if (e.isSpecialKey()) {
16540             return false;
16541         }
16542         var k = String.fromCharCode(e.getKey()).toUpperCase();
16543         //Roo.log(k);
16544         var match  = false;
16545         var csel = this.view.getSelectedNodes();
16546         var cselitem = false;
16547         if (csel.length) {
16548             var ix = this.view.indexOf(csel[0]);
16549             cselitem  = this.store.getAt(ix);
16550             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16551                 cselitem = false;
16552             }
16553             
16554         }
16555         
16556         this.store.each(function(v) { 
16557             if (cselitem) {
16558                 // start at existing selection.
16559                 if (cselitem.id == v.id) {
16560                     cselitem = false;
16561                 }
16562                 return true;
16563             }
16564                 
16565             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16566                 match = this.store.indexOf(v);
16567                 return false;
16568             }
16569             return true;
16570         }, this);
16571         
16572         if (match === false) {
16573             return true; // no more action?
16574         }
16575         // scroll to?
16576         this.view.select(match);
16577         var sn = Roo.get(this.view.getSelectedNodes()[0]);
16578         sn.scrollIntoView(sn.dom.parentNode, false);
16579     },
16580     
16581     onViewScroll : function(e, t){
16582         
16583         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){
16584             return;
16585         }
16586         
16587         this.hasQuery = true;
16588         
16589         this.loading = this.list.select('.loading', true).first();
16590         
16591         if(this.loading === null){
16592             this.list.createChild({
16593                 tag: 'div',
16594                 cls: 'loading roo-select2-more-results roo-select2-active',
16595                 html: 'Loading more results...'
16596             });
16597             
16598             this.loading = this.list.select('.loading', true).first();
16599             
16600             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16601             
16602             this.loading.hide();
16603         }
16604         
16605         this.loading.show();
16606         
16607         var _combo = this;
16608         
16609         this.page++;
16610         this.loadNext = true;
16611         
16612         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16613         
16614         return;
16615     },
16616     
16617     addItem : function(o)
16618     {   
16619         var dv = ''; // display value
16620         
16621         if (this.displayField) {
16622             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16623         } else {
16624             // this is an error condition!!!
16625             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16626         }
16627         
16628         if(!dv.length){
16629             return;
16630         }
16631         
16632         var choice = this.choices.createChild({
16633             tag: 'li',
16634             cls: 'roo-select2-search-choice',
16635             cn: [
16636                 {
16637                     tag: 'div',
16638                     html: dv
16639                 },
16640                 {
16641                     tag: 'a',
16642                     href: '#',
16643                     cls: 'roo-select2-search-choice-close fa fa-times',
16644                     tabindex: '-1'
16645                 }
16646             ]
16647             
16648         }, this.searchField);
16649         
16650         var close = choice.select('a.roo-select2-search-choice-close', true).first();
16651         
16652         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16653         
16654         this.item.push(o);
16655         
16656         this.lastData = o;
16657         
16658         this.syncValue();
16659         
16660         this.inputEl().dom.value = '';
16661         
16662         this.validate();
16663     },
16664     
16665     onRemoveItem : function(e, _self, o)
16666     {
16667         e.preventDefault();
16668         
16669         this.lastItem = Roo.apply([], this.item);
16670         
16671         var index = this.item.indexOf(o.data) * 1;
16672         
16673         if( index < 0){
16674             Roo.log('not this item?!');
16675             return;
16676         }
16677         
16678         this.item.splice(index, 1);
16679         o.item.remove();
16680         
16681         this.syncValue();
16682         
16683         this.fireEvent('remove', this, e);
16684         
16685         this.validate();
16686         
16687     },
16688     
16689     syncValue : function()
16690     {
16691         if(!this.item.length){
16692             this.clearValue();
16693             return;
16694         }
16695             
16696         var value = [];
16697         var _this = this;
16698         Roo.each(this.item, function(i){
16699             if(_this.valueField){
16700                 value.push(i[_this.valueField]);
16701                 return;
16702             }
16703
16704             value.push(i);
16705         });
16706
16707         this.value = value.join(',');
16708
16709         if(this.hiddenField){
16710             this.hiddenField.dom.value = this.value;
16711         }
16712         
16713         this.store.fireEvent("datachanged", this.store);
16714         
16715         this.validate();
16716     },
16717     
16718     clearItem : function()
16719     {
16720         if(!this.multiple){
16721             return;
16722         }
16723         
16724         this.item = [];
16725         
16726         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
16727            c.remove();
16728         });
16729         
16730         this.syncValue();
16731         
16732         this.validate();
16733         
16734         if(this.tickable && !Roo.isTouch){
16735             this.view.refresh();
16736         }
16737     },
16738     
16739     inputEl: function ()
16740     {
16741         if(Roo.isIOS && this.useNativeIOS){
16742             return this.el.select('select.roo-ios-select', true).first();
16743         }
16744         
16745         if(Roo.isTouch && this.mobileTouchView){
16746             return this.el.select('input.form-control',true).first();
16747         }
16748         
16749         if(this.tickable){
16750             return this.searchField;
16751         }
16752         
16753         return this.el.select('input.form-control',true).first();
16754     },
16755     
16756     onTickableFooterButtonClick : function(e, btn, el)
16757     {
16758         e.preventDefault();
16759         
16760         this.lastItem = Roo.apply([], this.item);
16761         
16762         if(btn && btn.name == 'cancel'){
16763             this.tickItems = Roo.apply([], this.item);
16764             this.collapse();
16765             return;
16766         }
16767         
16768         this.clearItem();
16769         
16770         var _this = this;
16771         
16772         Roo.each(this.tickItems, function(o){
16773             _this.addItem(o);
16774         });
16775         
16776         this.collapse();
16777         
16778     },
16779     
16780     validate : function()
16781     {
16782         if(this.getVisibilityEl().hasClass('hidden')){
16783             return true;
16784         }
16785         
16786         var v = this.getRawValue();
16787         
16788         if(this.multiple){
16789             v = this.getValue();
16790         }
16791         
16792         if(this.disabled || this.allowBlank || v.length){
16793             this.markValid();
16794             return true;
16795         }
16796         
16797         this.markInvalid();
16798         return false;
16799     },
16800     
16801     tickableInputEl : function()
16802     {
16803         if(!this.tickable || !this.editable){
16804             return this.inputEl();
16805         }
16806         
16807         return this.inputEl().select('.roo-select2-search-field-input', true).first();
16808     },
16809     
16810     
16811     getAutoCreateTouchView : function()
16812     {
16813         var id = Roo.id();
16814         
16815         var cfg = {
16816             cls: 'form-group' //input-group
16817         };
16818         
16819         var input =  {
16820             tag: 'input',
16821             id : id,
16822             type : this.inputType,
16823             cls : 'form-control x-combo-noedit',
16824             autocomplete: 'new-password',
16825             placeholder : this.placeholder || '',
16826             readonly : true
16827         };
16828         
16829         if (this.name) {
16830             input.name = this.name;
16831         }
16832         
16833         if (this.size) {
16834             input.cls += ' input-' + this.size;
16835         }
16836         
16837         if (this.disabled) {
16838             input.disabled = true;
16839         }
16840         
16841         var inputblock = {
16842             cls : '',
16843             cn : [
16844                 input
16845             ]
16846         };
16847         
16848         if(this.before){
16849             inputblock.cls += ' input-group';
16850             
16851             inputblock.cn.unshift({
16852                 tag :'span',
16853                 cls : 'input-group-addon input-group-prepend input-group-text',
16854                 html : this.before
16855             });
16856         }
16857         
16858         if(this.removable && !this.multiple){
16859             inputblock.cls += ' roo-removable';
16860             
16861             inputblock.cn.push({
16862                 tag: 'button',
16863                 html : 'x',
16864                 cls : 'roo-combo-removable-btn close'
16865             });
16866         }
16867
16868         if(this.hasFeedback && !this.allowBlank){
16869             
16870             inputblock.cls += ' has-feedback';
16871             
16872             inputblock.cn.push({
16873                 tag: 'span',
16874                 cls: 'glyphicon form-control-feedback'
16875             });
16876             
16877         }
16878         
16879         if (this.after) {
16880             
16881             inputblock.cls += (this.before) ? '' : ' input-group';
16882             
16883             inputblock.cn.push({
16884                 tag :'span',
16885                 cls : 'input-group-addon input-group-append input-group-text',
16886                 html : this.after
16887             });
16888         }
16889
16890         
16891         var ibwrap = inputblock;
16892         
16893         if(this.multiple){
16894             ibwrap = {
16895                 tag: 'ul',
16896                 cls: 'roo-select2-choices',
16897                 cn:[
16898                     {
16899                         tag: 'li',
16900                         cls: 'roo-select2-search-field',
16901                         cn: [
16902
16903                             inputblock
16904                         ]
16905                     }
16906                 ]
16907             };
16908         
16909             
16910         }
16911         
16912         var combobox = {
16913             cls: 'roo-select2-container input-group roo-touchview-combobox ',
16914             cn: [
16915                 {
16916                     tag: 'input',
16917                     type : 'hidden',
16918                     cls: 'form-hidden-field'
16919                 },
16920                 ibwrap
16921             ]
16922         };
16923         
16924         if(!this.multiple && this.showToggleBtn){
16925             
16926             var caret = {
16927                 cls: 'caret'
16928             };
16929             
16930             if (this.caret != false) {
16931                 caret = {
16932                      tag: 'i',
16933                      cls: 'fa fa-' + this.caret
16934                 };
16935                 
16936             }
16937             
16938             combobox.cn.push({
16939                 tag :'span',
16940                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
16941                 cn : [
16942                     Roo.bootstrap.version == 3 ? caret : '',
16943                     {
16944                         tag: 'span',
16945                         cls: 'combobox-clear',
16946                         cn  : [
16947                             {
16948                                 tag : 'i',
16949                                 cls: 'icon-remove'
16950                             }
16951                         ]
16952                     }
16953                 ]
16954
16955             })
16956         }
16957         
16958         if(this.multiple){
16959             combobox.cls += ' roo-select2-container-multi';
16960         }
16961         
16962         var align = this.labelAlign || this.parentLabelAlign();
16963         
16964         if (align ==='left' && this.fieldLabel.length) {
16965
16966             cfg.cn = [
16967                 {
16968                    tag : 'i',
16969                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
16970                    tooltip : 'This field is required'
16971                 },
16972                 {
16973                     tag: 'label',
16974                     cls : 'control-label col-form-label',
16975                     html : this.fieldLabel
16976
16977                 },
16978                 {
16979                     cls : '', 
16980                     cn: [
16981                         combobox
16982                     ]
16983                 }
16984             ];
16985             
16986             var labelCfg = cfg.cn[1];
16987             var contentCfg = cfg.cn[2];
16988             
16989
16990             if(this.indicatorpos == 'right'){
16991                 cfg.cn = [
16992                     {
16993                         tag: 'label',
16994                         'for' :  id,
16995                         cls : 'control-label col-form-label',
16996                         cn : [
16997                             {
16998                                 tag : 'span',
16999                                 html : this.fieldLabel
17000                             },
17001                             {
17002                                 tag : 'i',
17003                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17004                                 tooltip : 'This field is required'
17005                             }
17006                         ]
17007                     },
17008                     {
17009                         cls : "",
17010                         cn: [
17011                             combobox
17012                         ]
17013                     }
17014
17015                 ];
17016                 
17017                 labelCfg = cfg.cn[0];
17018                 contentCfg = cfg.cn[1];
17019             }
17020             
17021            
17022             
17023             if(this.labelWidth > 12){
17024                 labelCfg.style = "width: " + this.labelWidth + 'px';
17025             }
17026             
17027             if(this.labelWidth < 13 && this.labelmd == 0){
17028                 this.labelmd = this.labelWidth;
17029             }
17030             
17031             if(this.labellg > 0){
17032                 labelCfg.cls += ' col-lg-' + this.labellg;
17033                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17034             }
17035             
17036             if(this.labelmd > 0){
17037                 labelCfg.cls += ' col-md-' + this.labelmd;
17038                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17039             }
17040             
17041             if(this.labelsm > 0){
17042                 labelCfg.cls += ' col-sm-' + this.labelsm;
17043                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17044             }
17045             
17046             if(this.labelxs > 0){
17047                 labelCfg.cls += ' col-xs-' + this.labelxs;
17048                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17049             }
17050                 
17051                 
17052         } else if ( this.fieldLabel.length) {
17053             cfg.cn = [
17054                 {
17055                    tag : 'i',
17056                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17057                    tooltip : 'This field is required'
17058                 },
17059                 {
17060                     tag: 'label',
17061                     cls : 'control-label',
17062                     html : this.fieldLabel
17063
17064                 },
17065                 {
17066                     cls : '', 
17067                     cn: [
17068                         combobox
17069                     ]
17070                 }
17071             ];
17072             
17073             if(this.indicatorpos == 'right'){
17074                 cfg.cn = [
17075                     {
17076                         tag: 'label',
17077                         cls : 'control-label',
17078                         html : this.fieldLabel,
17079                         cn : [
17080                             {
17081                                tag : 'i',
17082                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17083                                tooltip : 'This field is required'
17084                             }
17085                         ]
17086                     },
17087                     {
17088                         cls : '', 
17089                         cn: [
17090                             combobox
17091                         ]
17092                     }
17093                 ];
17094             }
17095         } else {
17096             cfg.cn = combobox;    
17097         }
17098         
17099         
17100         var settings = this;
17101         
17102         ['xs','sm','md','lg'].map(function(size){
17103             if (settings[size]) {
17104                 cfg.cls += ' col-' + size + '-' + settings[size];
17105             }
17106         });
17107         
17108         return cfg;
17109     },
17110     
17111     initTouchView : function()
17112     {
17113         this.renderTouchView();
17114         
17115         this.touchViewEl.on('scroll', function(){
17116             this.el.dom.scrollTop = 0;
17117         }, this);
17118         
17119         this.originalValue = this.getValue();
17120         
17121         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17122         
17123         this.inputEl().on("click", this.showTouchView, this);
17124         if (this.triggerEl) {
17125             this.triggerEl.on("click", this.showTouchView, this);
17126         }
17127         
17128         
17129         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17130         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17131         
17132         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17133         
17134         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17135         this.store.on('load', this.onTouchViewLoad, this);
17136         this.store.on('loadexception', this.onTouchViewLoadException, this);
17137         
17138         if(this.hiddenName){
17139             
17140             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17141             
17142             this.hiddenField.dom.value =
17143                 this.hiddenValue !== undefined ? this.hiddenValue :
17144                 this.value !== undefined ? this.value : '';
17145         
17146             this.el.dom.removeAttribute('name');
17147             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17148         }
17149         
17150         if(this.multiple){
17151             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17152             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17153         }
17154         
17155         if(this.removable && !this.multiple){
17156             var close = this.closeTriggerEl();
17157             if(close){
17158                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17159                 close.on('click', this.removeBtnClick, this, close);
17160             }
17161         }
17162         /*
17163          * fix the bug in Safari iOS8
17164          */
17165         this.inputEl().on("focus", function(e){
17166             document.activeElement.blur();
17167         }, this);
17168         
17169         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17170         
17171         return;
17172         
17173         
17174     },
17175     
17176     renderTouchView : function()
17177     {
17178         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17179         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17180         
17181         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17182         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17183         
17184         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17185         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17186         this.touchViewBodyEl.setStyle('overflow', 'auto');
17187         
17188         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17189         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17190         
17191         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17192         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17193         
17194     },
17195     
17196     showTouchView : function()
17197     {
17198         if(this.disabled){
17199             return;
17200         }
17201         
17202         this.touchViewHeaderEl.hide();
17203
17204         if(this.modalTitle.length){
17205             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17206             this.touchViewHeaderEl.show();
17207         }
17208
17209         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17210         this.touchViewEl.show();
17211
17212         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17213         
17214         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17215         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17216
17217         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17218
17219         if(this.modalTitle.length){
17220             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17221         }
17222         
17223         this.touchViewBodyEl.setHeight(bodyHeight);
17224
17225         if(this.animate){
17226             var _this = this;
17227             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
17228         }else{
17229             this.touchViewEl.addClass('in');
17230         }
17231         
17232         if(this._touchViewMask){
17233             Roo.get(document.body).addClass("x-body-masked");
17234             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17235             this._touchViewMask.setStyle('z-index', 10000);
17236             this._touchViewMask.addClass('show');
17237         }
17238         
17239         this.doTouchViewQuery();
17240         
17241     },
17242     
17243     hideTouchView : function()
17244     {
17245         this.touchViewEl.removeClass('in');
17246
17247         if(this.animate){
17248             var _this = this;
17249             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17250         }else{
17251             this.touchViewEl.setStyle('display', 'none');
17252         }
17253         
17254         if(this._touchViewMask){
17255             this._touchViewMask.removeClass('show');
17256             Roo.get(document.body).removeClass("x-body-masked");
17257         }
17258     },
17259     
17260     setTouchViewValue : function()
17261     {
17262         if(this.multiple){
17263             this.clearItem();
17264         
17265             var _this = this;
17266
17267             Roo.each(this.tickItems, function(o){
17268                 this.addItem(o);
17269             }, this);
17270         }
17271         
17272         this.hideTouchView();
17273     },
17274     
17275     doTouchViewQuery : function()
17276     {
17277         var qe = {
17278             query: '',
17279             forceAll: true,
17280             combo: this,
17281             cancel:false
17282         };
17283         
17284         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17285             return false;
17286         }
17287         
17288         if(!this.alwaysQuery || this.mode == 'local'){
17289             this.onTouchViewLoad();
17290             return;
17291         }
17292         
17293         this.store.load();
17294     },
17295     
17296     onTouchViewBeforeLoad : function(combo,opts)
17297     {
17298         return;
17299     },
17300
17301     // private
17302     onTouchViewLoad : function()
17303     {
17304         if(this.store.getCount() < 1){
17305             this.onTouchViewEmptyResults();
17306             return;
17307         }
17308         
17309         this.clearTouchView();
17310         
17311         var rawValue = this.getRawValue();
17312         
17313         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17314         
17315         this.tickItems = [];
17316         
17317         this.store.data.each(function(d, rowIndex){
17318             var row = this.touchViewListGroup.createChild(template);
17319             
17320             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17321                 row.addClass(d.data.cls);
17322             }
17323             
17324             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17325                 var cfg = {
17326                     data : d.data,
17327                     html : d.data[this.displayField]
17328                 };
17329                 
17330                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17331                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17332                 }
17333             }
17334             row.removeClass('selected');
17335             if(!this.multiple && this.valueField &&
17336                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17337             {
17338                 // radio buttons..
17339                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17340                 row.addClass('selected');
17341             }
17342             
17343             if(this.multiple && this.valueField &&
17344                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17345             {
17346                 
17347                 // checkboxes...
17348                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17349                 this.tickItems.push(d.data);
17350             }
17351             
17352             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17353             
17354         }, this);
17355         
17356         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17357         
17358         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17359
17360         if(this.modalTitle.length){
17361             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17362         }
17363
17364         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17365         
17366         if(this.mobile_restrict_height && listHeight < bodyHeight){
17367             this.touchViewBodyEl.setHeight(listHeight);
17368         }
17369         
17370         var _this = this;
17371         
17372         if(firstChecked && listHeight > bodyHeight){
17373             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17374         }
17375         
17376     },
17377     
17378     onTouchViewLoadException : function()
17379     {
17380         this.hideTouchView();
17381     },
17382     
17383     onTouchViewEmptyResults : function()
17384     {
17385         this.clearTouchView();
17386         
17387         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17388         
17389         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17390         
17391     },
17392     
17393     clearTouchView : function()
17394     {
17395         this.touchViewListGroup.dom.innerHTML = '';
17396     },
17397     
17398     onTouchViewClick : function(e, el, o)
17399     {
17400         e.preventDefault();
17401         
17402         var row = o.row;
17403         var rowIndex = o.rowIndex;
17404         
17405         var r = this.store.getAt(rowIndex);
17406         
17407         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17408             
17409             if(!this.multiple){
17410                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17411                     c.dom.removeAttribute('checked');
17412                 }, this);
17413
17414                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17415
17416                 this.setFromData(r.data);
17417
17418                 var close = this.closeTriggerEl();
17419
17420                 if(close){
17421                     close.show();
17422                 }
17423
17424                 this.hideTouchView();
17425
17426                 this.fireEvent('select', this, r, rowIndex);
17427
17428                 return;
17429             }
17430
17431             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17432                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17433                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17434                 return;
17435             }
17436
17437             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17438             this.addItem(r.data);
17439             this.tickItems.push(r.data);
17440         }
17441     },
17442     
17443     getAutoCreateNativeIOS : function()
17444     {
17445         var cfg = {
17446             cls: 'form-group' //input-group,
17447         };
17448         
17449         var combobox =  {
17450             tag: 'select',
17451             cls : 'roo-ios-select'
17452         };
17453         
17454         if (this.name) {
17455             combobox.name = this.name;
17456         }
17457         
17458         if (this.disabled) {
17459             combobox.disabled = true;
17460         }
17461         
17462         var settings = this;
17463         
17464         ['xs','sm','md','lg'].map(function(size){
17465             if (settings[size]) {
17466                 cfg.cls += ' col-' + size + '-' + settings[size];
17467             }
17468         });
17469         
17470         cfg.cn = combobox;
17471         
17472         return cfg;
17473         
17474     },
17475     
17476     initIOSView : function()
17477     {
17478         this.store.on('load', this.onIOSViewLoad, this);
17479         
17480         return;
17481     },
17482     
17483     onIOSViewLoad : function()
17484     {
17485         if(this.store.getCount() < 1){
17486             return;
17487         }
17488         
17489         this.clearIOSView();
17490         
17491         if(this.allowBlank) {
17492             
17493             var default_text = '-- SELECT --';
17494             
17495             if(this.placeholder.length){
17496                 default_text = this.placeholder;
17497             }
17498             
17499             if(this.emptyTitle.length){
17500                 default_text += ' - ' + this.emptyTitle + ' -';
17501             }
17502             
17503             var opt = this.inputEl().createChild({
17504                 tag: 'option',
17505                 value : 0,
17506                 html : default_text
17507             });
17508             
17509             var o = {};
17510             o[this.valueField] = 0;
17511             o[this.displayField] = default_text;
17512             
17513             this.ios_options.push({
17514                 data : o,
17515                 el : opt
17516             });
17517             
17518         }
17519         
17520         this.store.data.each(function(d, rowIndex){
17521             
17522             var html = '';
17523             
17524             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17525                 html = d.data[this.displayField];
17526             }
17527             
17528             var value = '';
17529             
17530             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17531                 value = d.data[this.valueField];
17532             }
17533             
17534             var option = {
17535                 tag: 'option',
17536                 value : value,
17537                 html : html
17538             };
17539             
17540             if(this.value == d.data[this.valueField]){
17541                 option['selected'] = true;
17542             }
17543             
17544             var opt = this.inputEl().createChild(option);
17545             
17546             this.ios_options.push({
17547                 data : d.data,
17548                 el : opt
17549             });
17550             
17551         }, this);
17552         
17553         this.inputEl().on('change', function(){
17554            this.fireEvent('select', this);
17555         }, this);
17556         
17557     },
17558     
17559     clearIOSView: function()
17560     {
17561         this.inputEl().dom.innerHTML = '';
17562         
17563         this.ios_options = [];
17564     },
17565     
17566     setIOSValue: function(v)
17567     {
17568         this.value = v;
17569         
17570         if(!this.ios_options){
17571             return;
17572         }
17573         
17574         Roo.each(this.ios_options, function(opts){
17575            
17576            opts.el.dom.removeAttribute('selected');
17577            
17578            if(opts.data[this.valueField] != v){
17579                return;
17580            }
17581            
17582            opts.el.dom.setAttribute('selected', true);
17583            
17584         }, this);
17585     }
17586
17587     /** 
17588     * @cfg {Boolean} grow 
17589     * @hide 
17590     */
17591     /** 
17592     * @cfg {Number} growMin 
17593     * @hide 
17594     */
17595     /** 
17596     * @cfg {Number} growMax 
17597     * @hide 
17598     */
17599     /**
17600      * @hide
17601      * @method autoSize
17602      */
17603 });
17604
17605 Roo.apply(Roo.bootstrap.ComboBox,  {
17606     
17607     header : {
17608         tag: 'div',
17609         cls: 'modal-header',
17610         cn: [
17611             {
17612                 tag: 'h4',
17613                 cls: 'modal-title'
17614             }
17615         ]
17616     },
17617     
17618     body : {
17619         tag: 'div',
17620         cls: 'modal-body',
17621         cn: [
17622             {
17623                 tag: 'ul',
17624                 cls: 'list-group'
17625             }
17626         ]
17627     },
17628     
17629     listItemRadio : {
17630         tag: 'li',
17631         cls: 'list-group-item',
17632         cn: [
17633             {
17634                 tag: 'span',
17635                 cls: 'roo-combobox-list-group-item-value'
17636             },
17637             {
17638                 tag: 'div',
17639                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17640                 cn: [
17641                     {
17642                         tag: 'input',
17643                         type: 'radio'
17644                     },
17645                     {
17646                         tag: 'label'
17647                     }
17648                 ]
17649             }
17650         ]
17651     },
17652     
17653     listItemCheckbox : {
17654         tag: 'li',
17655         cls: 'list-group-item',
17656         cn: [
17657             {
17658                 tag: 'span',
17659                 cls: 'roo-combobox-list-group-item-value'
17660             },
17661             {
17662                 tag: 'div',
17663                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17664                 cn: [
17665                     {
17666                         tag: 'input',
17667                         type: 'checkbox'
17668                     },
17669                     {
17670                         tag: 'label'
17671                     }
17672                 ]
17673             }
17674         ]
17675     },
17676     
17677     emptyResult : {
17678         tag: 'div',
17679         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17680     },
17681     
17682     footer : {
17683         tag: 'div',
17684         cls: 'modal-footer',
17685         cn: [
17686             {
17687                 tag: 'div',
17688                 cls: 'row',
17689                 cn: [
17690                     {
17691                         tag: 'div',
17692                         cls: 'col-xs-6 text-left',
17693                         cn: {
17694                             tag: 'button',
17695                             cls: 'btn btn-danger roo-touch-view-cancel',
17696                             html: 'Cancel'
17697                         }
17698                     },
17699                     {
17700                         tag: 'div',
17701                         cls: 'col-xs-6 text-right',
17702                         cn: {
17703                             tag: 'button',
17704                             cls: 'btn btn-success roo-touch-view-ok',
17705                             html: 'OK'
17706                         }
17707                     }
17708                 ]
17709             }
17710         ]
17711         
17712     }
17713 });
17714
17715 Roo.apply(Roo.bootstrap.ComboBox,  {
17716     
17717     touchViewTemplate : {
17718         tag: 'div',
17719         cls: 'modal fade roo-combobox-touch-view',
17720         cn: [
17721             {
17722                 tag: 'div',
17723                 cls: 'modal-dialog',
17724                 style : 'position:fixed', // we have to fix position....
17725                 cn: [
17726                     {
17727                         tag: 'div',
17728                         cls: 'modal-content',
17729                         cn: [
17730                             Roo.bootstrap.ComboBox.header,
17731                             Roo.bootstrap.ComboBox.body,
17732                             Roo.bootstrap.ComboBox.footer
17733                         ]
17734                     }
17735                 ]
17736             }
17737         ]
17738     }
17739 });/*
17740  * Based on:
17741  * Ext JS Library 1.1.1
17742  * Copyright(c) 2006-2007, Ext JS, LLC.
17743  *
17744  * Originally Released Under LGPL - original licence link has changed is not relivant.
17745  *
17746  * Fork - LGPL
17747  * <script type="text/javascript">
17748  */
17749
17750 /**
17751  * @class Roo.View
17752  * @extends Roo.util.Observable
17753  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
17754  * This class also supports single and multi selection modes. <br>
17755  * Create a data model bound view:
17756  <pre><code>
17757  var store = new Roo.data.Store(...);
17758
17759  var view = new Roo.View({
17760     el : "my-element",
17761     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
17762  
17763     singleSelect: true,
17764     selectedClass: "ydataview-selected",
17765     store: store
17766  });
17767
17768  // listen for node click?
17769  view.on("click", function(vw, index, node, e){
17770  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
17771  });
17772
17773  // load XML data
17774  dataModel.load("foobar.xml");
17775  </code></pre>
17776  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
17777  * <br><br>
17778  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
17779  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
17780  * 
17781  * Note: old style constructor is still suported (container, template, config)
17782  * 
17783  * @constructor
17784  * Create a new View
17785  * @param {Object} config The config object
17786  * 
17787  */
17788 Roo.View = function(config, depreciated_tpl, depreciated_config){
17789     
17790     this.parent = false;
17791     
17792     if (typeof(depreciated_tpl) == 'undefined') {
17793         // new way.. - universal constructor.
17794         Roo.apply(this, config);
17795         this.el  = Roo.get(this.el);
17796     } else {
17797         // old format..
17798         this.el  = Roo.get(config);
17799         this.tpl = depreciated_tpl;
17800         Roo.apply(this, depreciated_config);
17801     }
17802     this.wrapEl  = this.el.wrap().wrap();
17803     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
17804     
17805     
17806     if(typeof(this.tpl) == "string"){
17807         this.tpl = new Roo.Template(this.tpl);
17808     } else {
17809         // support xtype ctors..
17810         this.tpl = new Roo.factory(this.tpl, Roo);
17811     }
17812     
17813     
17814     this.tpl.compile();
17815     
17816     /** @private */
17817     this.addEvents({
17818         /**
17819          * @event beforeclick
17820          * Fires before a click is processed. Returns false to cancel the default action.
17821          * @param {Roo.View} this
17822          * @param {Number} index The index of the target node
17823          * @param {HTMLElement} node The target node
17824          * @param {Roo.EventObject} e The raw event object
17825          */
17826             "beforeclick" : true,
17827         /**
17828          * @event click
17829          * Fires when a template node is clicked.
17830          * @param {Roo.View} this
17831          * @param {Number} index The index of the target node
17832          * @param {HTMLElement} node The target node
17833          * @param {Roo.EventObject} e The raw event object
17834          */
17835             "click" : true,
17836         /**
17837          * @event dblclick
17838          * Fires when a template node is double clicked.
17839          * @param {Roo.View} this
17840          * @param {Number} index The index of the target node
17841          * @param {HTMLElement} node The target node
17842          * @param {Roo.EventObject} e The raw event object
17843          */
17844             "dblclick" : true,
17845         /**
17846          * @event contextmenu
17847          * Fires when a template node is right clicked.
17848          * @param {Roo.View} this
17849          * @param {Number} index The index of the target node
17850          * @param {HTMLElement} node The target node
17851          * @param {Roo.EventObject} e The raw event object
17852          */
17853             "contextmenu" : true,
17854         /**
17855          * @event selectionchange
17856          * Fires when the selected nodes change.
17857          * @param {Roo.View} this
17858          * @param {Array} selections Array of the selected nodes
17859          */
17860             "selectionchange" : true,
17861     
17862         /**
17863          * @event beforeselect
17864          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
17865          * @param {Roo.View} this
17866          * @param {HTMLElement} node The node to be selected
17867          * @param {Array} selections Array of currently selected nodes
17868          */
17869             "beforeselect" : true,
17870         /**
17871          * @event preparedata
17872          * Fires on every row to render, to allow you to change the data.
17873          * @param {Roo.View} this
17874          * @param {Object} data to be rendered (change this)
17875          */
17876           "preparedata" : true
17877           
17878           
17879         });
17880
17881
17882
17883     this.el.on({
17884         "click": this.onClick,
17885         "dblclick": this.onDblClick,
17886         "contextmenu": this.onContextMenu,
17887         scope:this
17888     });
17889
17890     this.selections = [];
17891     this.nodes = [];
17892     this.cmp = new Roo.CompositeElementLite([]);
17893     if(this.store){
17894         this.store = Roo.factory(this.store, Roo.data);
17895         this.setStore(this.store, true);
17896     }
17897     
17898     if ( this.footer && this.footer.xtype) {
17899            
17900          var fctr = this.wrapEl.appendChild(document.createElement("div"));
17901         
17902         this.footer.dataSource = this.store;
17903         this.footer.container = fctr;
17904         this.footer = Roo.factory(this.footer, Roo);
17905         fctr.insertFirst(this.el);
17906         
17907         // this is a bit insane - as the paging toolbar seems to detach the el..
17908 //        dom.parentNode.parentNode.parentNode
17909          // they get detached?
17910     }
17911     
17912     
17913     Roo.View.superclass.constructor.call(this);
17914     
17915     
17916 };
17917
17918 Roo.extend(Roo.View, Roo.util.Observable, {
17919     
17920      /**
17921      * @cfg {Roo.data.Store} store Data store to load data from.
17922      */
17923     store : false,
17924     
17925     /**
17926      * @cfg {String|Roo.Element} el The container element.
17927      */
17928     el : '',
17929     
17930     /**
17931      * @cfg {String|Roo.Template} tpl The template used by this View 
17932      */
17933     tpl : false,
17934     /**
17935      * @cfg {String} dataName the named area of the template to use as the data area
17936      *                          Works with domtemplates roo-name="name"
17937      */
17938     dataName: false,
17939     /**
17940      * @cfg {String} selectedClass The css class to add to selected nodes
17941      */
17942     selectedClass : "x-view-selected",
17943      /**
17944      * @cfg {String} emptyText The empty text to show when nothing is loaded.
17945      */
17946     emptyText : "",
17947     
17948     /**
17949      * @cfg {String} text to display on mask (default Loading)
17950      */
17951     mask : false,
17952     /**
17953      * @cfg {Boolean} multiSelect Allow multiple selection
17954      */
17955     multiSelect : false,
17956     /**
17957      * @cfg {Boolean} singleSelect Allow single selection
17958      */
17959     singleSelect:  false,
17960     
17961     /**
17962      * @cfg {Boolean} toggleSelect - selecting 
17963      */
17964     toggleSelect : false,
17965     
17966     /**
17967      * @cfg {Boolean} tickable - selecting 
17968      */
17969     tickable : false,
17970     
17971     /**
17972      * Returns the element this view is bound to.
17973      * @return {Roo.Element}
17974      */
17975     getEl : function(){
17976         return this.wrapEl;
17977     },
17978     
17979     
17980
17981     /**
17982      * Refreshes the view. - called by datachanged on the store. - do not call directly.
17983      */
17984     refresh : function(){
17985         //Roo.log('refresh');
17986         var t = this.tpl;
17987         
17988         // if we are using something like 'domtemplate', then
17989         // the what gets used is:
17990         // t.applySubtemplate(NAME, data, wrapping data..)
17991         // the outer template then get' applied with
17992         //     the store 'extra data'
17993         // and the body get's added to the
17994         //      roo-name="data" node?
17995         //      <span class='roo-tpl-{name}'></span> ?????
17996         
17997         
17998         
17999         this.clearSelections();
18000         this.el.update("");
18001         var html = [];
18002         var records = this.store.getRange();
18003         if(records.length < 1) {
18004             
18005             // is this valid??  = should it render a template??
18006             
18007             this.el.update(this.emptyText);
18008             return;
18009         }
18010         var el = this.el;
18011         if (this.dataName) {
18012             this.el.update(t.apply(this.store.meta)); //????
18013             el = this.el.child('.roo-tpl-' + this.dataName);
18014         }
18015         
18016         for(var i = 0, len = records.length; i < len; i++){
18017             var data = this.prepareData(records[i].data, i, records[i]);
18018             this.fireEvent("preparedata", this, data, i, records[i]);
18019             
18020             var d = Roo.apply({}, data);
18021             
18022             if(this.tickable){
18023                 Roo.apply(d, {'roo-id' : Roo.id()});
18024                 
18025                 var _this = this;
18026             
18027                 Roo.each(this.parent.item, function(item){
18028                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18029                         return;
18030                     }
18031                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18032                 });
18033             }
18034             
18035             html[html.length] = Roo.util.Format.trim(
18036                 this.dataName ?
18037                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18038                     t.apply(d)
18039             );
18040         }
18041         
18042         
18043         
18044         el.update(html.join(""));
18045         this.nodes = el.dom.childNodes;
18046         this.updateIndexes(0);
18047     },
18048     
18049
18050     /**
18051      * Function to override to reformat the data that is sent to
18052      * the template for each node.
18053      * DEPRICATED - use the preparedata event handler.
18054      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18055      * a JSON object for an UpdateManager bound view).
18056      */
18057     prepareData : function(data, index, record)
18058     {
18059         this.fireEvent("preparedata", this, data, index, record);
18060         return data;
18061     },
18062
18063     onUpdate : function(ds, record){
18064         // Roo.log('on update');   
18065         this.clearSelections();
18066         var index = this.store.indexOf(record);
18067         var n = this.nodes[index];
18068         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18069         n.parentNode.removeChild(n);
18070         this.updateIndexes(index, index);
18071     },
18072
18073     
18074     
18075 // --------- FIXME     
18076     onAdd : function(ds, records, index)
18077     {
18078         //Roo.log(['on Add', ds, records, index] );        
18079         this.clearSelections();
18080         if(this.nodes.length == 0){
18081             this.refresh();
18082             return;
18083         }
18084         var n = this.nodes[index];
18085         for(var i = 0, len = records.length; i < len; i++){
18086             var d = this.prepareData(records[i].data, i, records[i]);
18087             if(n){
18088                 this.tpl.insertBefore(n, d);
18089             }else{
18090                 
18091                 this.tpl.append(this.el, d);
18092             }
18093         }
18094         this.updateIndexes(index);
18095     },
18096
18097     onRemove : function(ds, record, index){
18098        // Roo.log('onRemove');
18099         this.clearSelections();
18100         var el = this.dataName  ?
18101             this.el.child('.roo-tpl-' + this.dataName) :
18102             this.el; 
18103         
18104         el.dom.removeChild(this.nodes[index]);
18105         this.updateIndexes(index);
18106     },
18107
18108     /**
18109      * Refresh an individual node.
18110      * @param {Number} index
18111      */
18112     refreshNode : function(index){
18113         this.onUpdate(this.store, this.store.getAt(index));
18114     },
18115
18116     updateIndexes : function(startIndex, endIndex){
18117         var ns = this.nodes;
18118         startIndex = startIndex || 0;
18119         endIndex = endIndex || ns.length - 1;
18120         for(var i = startIndex; i <= endIndex; i++){
18121             ns[i].nodeIndex = i;
18122         }
18123     },
18124
18125     /**
18126      * Changes the data store this view uses and refresh the view.
18127      * @param {Store} store
18128      */
18129     setStore : function(store, initial){
18130         if(!initial && this.store){
18131             this.store.un("datachanged", this.refresh);
18132             this.store.un("add", this.onAdd);
18133             this.store.un("remove", this.onRemove);
18134             this.store.un("update", this.onUpdate);
18135             this.store.un("clear", this.refresh);
18136             this.store.un("beforeload", this.onBeforeLoad);
18137             this.store.un("load", this.onLoad);
18138             this.store.un("loadexception", this.onLoad);
18139         }
18140         if(store){
18141           
18142             store.on("datachanged", this.refresh, this);
18143             store.on("add", this.onAdd, this);
18144             store.on("remove", this.onRemove, this);
18145             store.on("update", this.onUpdate, this);
18146             store.on("clear", this.refresh, this);
18147             store.on("beforeload", this.onBeforeLoad, this);
18148             store.on("load", this.onLoad, this);
18149             store.on("loadexception", this.onLoad, this);
18150         }
18151         
18152         if(store){
18153             this.refresh();
18154         }
18155     },
18156     /**
18157      * onbeforeLoad - masks the loading area.
18158      *
18159      */
18160     onBeforeLoad : function(store,opts)
18161     {
18162          //Roo.log('onBeforeLoad');   
18163         if (!opts.add) {
18164             this.el.update("");
18165         }
18166         this.el.mask(this.mask ? this.mask : "Loading" ); 
18167     },
18168     onLoad : function ()
18169     {
18170         this.el.unmask();
18171     },
18172     
18173
18174     /**
18175      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18176      * @param {HTMLElement} node
18177      * @return {HTMLElement} The template node
18178      */
18179     findItemFromChild : function(node){
18180         var el = this.dataName  ?
18181             this.el.child('.roo-tpl-' + this.dataName,true) :
18182             this.el.dom; 
18183         
18184         if(!node || node.parentNode == el){
18185                     return node;
18186             }
18187             var p = node.parentNode;
18188             while(p && p != el){
18189             if(p.parentNode == el){
18190                 return p;
18191             }
18192             p = p.parentNode;
18193         }
18194             return null;
18195     },
18196
18197     /** @ignore */
18198     onClick : function(e){
18199         var item = this.findItemFromChild(e.getTarget());
18200         if(item){
18201             var index = this.indexOf(item);
18202             if(this.onItemClick(item, index, e) !== false){
18203                 this.fireEvent("click", this, index, item, e);
18204             }
18205         }else{
18206             this.clearSelections();
18207         }
18208     },
18209
18210     /** @ignore */
18211     onContextMenu : function(e){
18212         var item = this.findItemFromChild(e.getTarget());
18213         if(item){
18214             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18215         }
18216     },
18217
18218     /** @ignore */
18219     onDblClick : function(e){
18220         var item = this.findItemFromChild(e.getTarget());
18221         if(item){
18222             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18223         }
18224     },
18225
18226     onItemClick : function(item, index, e)
18227     {
18228         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18229             return false;
18230         }
18231         if (this.toggleSelect) {
18232             var m = this.isSelected(item) ? 'unselect' : 'select';
18233             //Roo.log(m);
18234             var _t = this;
18235             _t[m](item, true, false);
18236             return true;
18237         }
18238         if(this.multiSelect || this.singleSelect){
18239             if(this.multiSelect && e.shiftKey && this.lastSelection){
18240                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18241             }else{
18242                 this.select(item, this.multiSelect && e.ctrlKey);
18243                 this.lastSelection = item;
18244             }
18245             
18246             if(!this.tickable){
18247                 e.preventDefault();
18248             }
18249             
18250         }
18251         return true;
18252     },
18253
18254     /**
18255      * Get the number of selected nodes.
18256      * @return {Number}
18257      */
18258     getSelectionCount : function(){
18259         return this.selections.length;
18260     },
18261
18262     /**
18263      * Get the currently selected nodes.
18264      * @return {Array} An array of HTMLElements
18265      */
18266     getSelectedNodes : function(){
18267         return this.selections;
18268     },
18269
18270     /**
18271      * Get the indexes of the selected nodes.
18272      * @return {Array}
18273      */
18274     getSelectedIndexes : function(){
18275         var indexes = [], s = this.selections;
18276         for(var i = 0, len = s.length; i < len; i++){
18277             indexes.push(s[i].nodeIndex);
18278         }
18279         return indexes;
18280     },
18281
18282     /**
18283      * Clear all selections
18284      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18285      */
18286     clearSelections : function(suppressEvent){
18287         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18288             this.cmp.elements = this.selections;
18289             this.cmp.removeClass(this.selectedClass);
18290             this.selections = [];
18291             if(!suppressEvent){
18292                 this.fireEvent("selectionchange", this, this.selections);
18293             }
18294         }
18295     },
18296
18297     /**
18298      * Returns true if the passed node is selected
18299      * @param {HTMLElement/Number} node The node or node index
18300      * @return {Boolean}
18301      */
18302     isSelected : function(node){
18303         var s = this.selections;
18304         if(s.length < 1){
18305             return false;
18306         }
18307         node = this.getNode(node);
18308         return s.indexOf(node) !== -1;
18309     },
18310
18311     /**
18312      * Selects nodes.
18313      * @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
18314      * @param {Boolean} keepExisting (optional) true to keep existing selections
18315      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18316      */
18317     select : function(nodeInfo, keepExisting, suppressEvent){
18318         if(nodeInfo instanceof Array){
18319             if(!keepExisting){
18320                 this.clearSelections(true);
18321             }
18322             for(var i = 0, len = nodeInfo.length; i < len; i++){
18323                 this.select(nodeInfo[i], true, true);
18324             }
18325             return;
18326         } 
18327         var node = this.getNode(nodeInfo);
18328         if(!node || this.isSelected(node)){
18329             return; // already selected.
18330         }
18331         if(!keepExisting){
18332             this.clearSelections(true);
18333         }
18334         
18335         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18336             Roo.fly(node).addClass(this.selectedClass);
18337             this.selections.push(node);
18338             if(!suppressEvent){
18339                 this.fireEvent("selectionchange", this, this.selections);
18340             }
18341         }
18342         
18343         
18344     },
18345       /**
18346      * Unselects nodes.
18347      * @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
18348      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18349      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18350      */
18351     unselect : function(nodeInfo, keepExisting, suppressEvent)
18352     {
18353         if(nodeInfo instanceof Array){
18354             Roo.each(this.selections, function(s) {
18355                 this.unselect(s, nodeInfo);
18356             }, this);
18357             return;
18358         }
18359         var node = this.getNode(nodeInfo);
18360         if(!node || !this.isSelected(node)){
18361             //Roo.log("not selected");
18362             return; // not selected.
18363         }
18364         // fireevent???
18365         var ns = [];
18366         Roo.each(this.selections, function(s) {
18367             if (s == node ) {
18368                 Roo.fly(node).removeClass(this.selectedClass);
18369
18370                 return;
18371             }
18372             ns.push(s);
18373         },this);
18374         
18375         this.selections= ns;
18376         this.fireEvent("selectionchange", this, this.selections);
18377     },
18378
18379     /**
18380      * Gets a template node.
18381      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18382      * @return {HTMLElement} The node or null if it wasn't found
18383      */
18384     getNode : function(nodeInfo){
18385         if(typeof nodeInfo == "string"){
18386             return document.getElementById(nodeInfo);
18387         }else if(typeof nodeInfo == "number"){
18388             return this.nodes[nodeInfo];
18389         }
18390         return nodeInfo;
18391     },
18392
18393     /**
18394      * Gets a range template nodes.
18395      * @param {Number} startIndex
18396      * @param {Number} endIndex
18397      * @return {Array} An array of nodes
18398      */
18399     getNodes : function(start, end){
18400         var ns = this.nodes;
18401         start = start || 0;
18402         end = typeof end == "undefined" ? ns.length - 1 : end;
18403         var nodes = [];
18404         if(start <= end){
18405             for(var i = start; i <= end; i++){
18406                 nodes.push(ns[i]);
18407             }
18408         } else{
18409             for(var i = start; i >= end; i--){
18410                 nodes.push(ns[i]);
18411             }
18412         }
18413         return nodes;
18414     },
18415
18416     /**
18417      * Finds the index of the passed node
18418      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18419      * @return {Number} The index of the node or -1
18420      */
18421     indexOf : function(node){
18422         node = this.getNode(node);
18423         if(typeof node.nodeIndex == "number"){
18424             return node.nodeIndex;
18425         }
18426         var ns = this.nodes;
18427         for(var i = 0, len = ns.length; i < len; i++){
18428             if(ns[i] == node){
18429                 return i;
18430             }
18431         }
18432         return -1;
18433     }
18434 });
18435 /*
18436  * - LGPL
18437  *
18438  * based on jquery fullcalendar
18439  * 
18440  */
18441
18442 Roo.bootstrap = Roo.bootstrap || {};
18443 /**
18444  * @class Roo.bootstrap.Calendar
18445  * @extends Roo.bootstrap.Component
18446  * Bootstrap Calendar class
18447  * @cfg {Boolean} loadMask (true|false) default false
18448  * @cfg {Object} header generate the user specific header of the calendar, default false
18449
18450  * @constructor
18451  * Create a new Container
18452  * @param {Object} config The config object
18453  */
18454
18455
18456
18457 Roo.bootstrap.Calendar = function(config){
18458     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18459      this.addEvents({
18460         /**
18461              * @event select
18462              * Fires when a date is selected
18463              * @param {DatePicker} this
18464              * @param {Date} date The selected date
18465              */
18466         'select': true,
18467         /**
18468              * @event monthchange
18469              * Fires when the displayed month changes 
18470              * @param {DatePicker} this
18471              * @param {Date} date The selected month
18472              */
18473         'monthchange': true,
18474         /**
18475              * @event evententer
18476              * Fires when mouse over an event
18477              * @param {Calendar} this
18478              * @param {event} Event
18479              */
18480         'evententer': true,
18481         /**
18482              * @event eventleave
18483              * Fires when the mouse leaves an
18484              * @param {Calendar} this
18485              * @param {event}
18486              */
18487         'eventleave': true,
18488         /**
18489              * @event eventclick
18490              * Fires when the mouse click an
18491              * @param {Calendar} this
18492              * @param {event}
18493              */
18494         'eventclick': true
18495         
18496     });
18497
18498 };
18499
18500 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
18501     
18502      /**
18503      * @cfg {Number} startDay
18504      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18505      */
18506     startDay : 0,
18507     
18508     loadMask : false,
18509     
18510     header : false,
18511       
18512     getAutoCreate : function(){
18513         
18514         
18515         var fc_button = function(name, corner, style, content ) {
18516             return Roo.apply({},{
18517                 tag : 'span',
18518                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
18519                          (corner.length ?
18520                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18521                             ''
18522                         ),
18523                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18524                 unselectable: 'on'
18525             });
18526         };
18527         
18528         var header = {};
18529         
18530         if(!this.header){
18531             header = {
18532                 tag : 'table',
18533                 cls : 'fc-header',
18534                 style : 'width:100%',
18535                 cn : [
18536                     {
18537                         tag: 'tr',
18538                         cn : [
18539                             {
18540                                 tag : 'td',
18541                                 cls : 'fc-header-left',
18542                                 cn : [
18543                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
18544                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
18545                                     { tag: 'span', cls: 'fc-header-space' },
18546                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
18547
18548
18549                                 ]
18550                             },
18551
18552                             {
18553                                 tag : 'td',
18554                                 cls : 'fc-header-center',
18555                                 cn : [
18556                                     {
18557                                         tag: 'span',
18558                                         cls: 'fc-header-title',
18559                                         cn : {
18560                                             tag: 'H2',
18561                                             html : 'month / year'
18562                                         }
18563                                     }
18564
18565                                 ]
18566                             },
18567                             {
18568                                 tag : 'td',
18569                                 cls : 'fc-header-right',
18570                                 cn : [
18571                               /*      fc_button('month', 'left', '', 'month' ),
18572                                     fc_button('week', '', '', 'week' ),
18573                                     fc_button('day', 'right', '', 'day' )
18574                                 */    
18575
18576                                 ]
18577                             }
18578
18579                         ]
18580                     }
18581                 ]
18582             };
18583         }
18584         
18585         header = this.header;
18586         
18587        
18588         var cal_heads = function() {
18589             var ret = [];
18590             // fixme - handle this.
18591             
18592             for (var i =0; i < Date.dayNames.length; i++) {
18593                 var d = Date.dayNames[i];
18594                 ret.push({
18595                     tag: 'th',
18596                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18597                     html : d.substring(0,3)
18598                 });
18599                 
18600             }
18601             ret[0].cls += ' fc-first';
18602             ret[6].cls += ' fc-last';
18603             return ret;
18604         };
18605         var cal_cell = function(n) {
18606             return  {
18607                 tag: 'td',
18608                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18609                 cn : [
18610                     {
18611                         cn : [
18612                             {
18613                                 cls: 'fc-day-number',
18614                                 html: 'D'
18615                             },
18616                             {
18617                                 cls: 'fc-day-content',
18618                              
18619                                 cn : [
18620                                      {
18621                                         style: 'position: relative;' // height: 17px;
18622                                     }
18623                                 ]
18624                             }
18625                             
18626                             
18627                         ]
18628                     }
18629                 ]
18630                 
18631             }
18632         };
18633         var cal_rows = function() {
18634             
18635             var ret = [];
18636             for (var r = 0; r < 6; r++) {
18637                 var row= {
18638                     tag : 'tr',
18639                     cls : 'fc-week',
18640                     cn : []
18641                 };
18642                 
18643                 for (var i =0; i < Date.dayNames.length; i++) {
18644                     var d = Date.dayNames[i];
18645                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18646
18647                 }
18648                 row.cn[0].cls+=' fc-first';
18649                 row.cn[0].cn[0].style = 'min-height:90px';
18650                 row.cn[6].cls+=' fc-last';
18651                 ret.push(row);
18652                 
18653             }
18654             ret[0].cls += ' fc-first';
18655             ret[4].cls += ' fc-prev-last';
18656             ret[5].cls += ' fc-last';
18657             return ret;
18658             
18659         };
18660         
18661         var cal_table = {
18662             tag: 'table',
18663             cls: 'fc-border-separate',
18664             style : 'width:100%',
18665             cellspacing  : 0,
18666             cn : [
18667                 { 
18668                     tag: 'thead',
18669                     cn : [
18670                         { 
18671                             tag: 'tr',
18672                             cls : 'fc-first fc-last',
18673                             cn : cal_heads()
18674                         }
18675                     ]
18676                 },
18677                 { 
18678                     tag: 'tbody',
18679                     cn : cal_rows()
18680                 }
18681                   
18682             ]
18683         };
18684          
18685          var cfg = {
18686             cls : 'fc fc-ltr',
18687             cn : [
18688                 header,
18689                 {
18690                     cls : 'fc-content',
18691                     style : "position: relative;",
18692                     cn : [
18693                         {
18694                             cls : 'fc-view fc-view-month fc-grid',
18695                             style : 'position: relative',
18696                             unselectable : 'on',
18697                             cn : [
18698                                 {
18699                                     cls : 'fc-event-container',
18700                                     style : 'position:absolute;z-index:8;top:0;left:0;'
18701                                 },
18702                                 cal_table
18703                             ]
18704                         }
18705                     ]
18706     
18707                 }
18708            ] 
18709             
18710         };
18711         
18712          
18713         
18714         return cfg;
18715     },
18716     
18717     
18718     initEvents : function()
18719     {
18720         if(!this.store){
18721             throw "can not find store for calendar";
18722         }
18723         
18724         var mark = {
18725             tag: "div",
18726             cls:"x-dlg-mask",
18727             style: "text-align:center",
18728             cn: [
18729                 {
18730                     tag: "div",
18731                     style: "background-color:white;width:50%;margin:250 auto",
18732                     cn: [
18733                         {
18734                             tag: "img",
18735                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
18736                         },
18737                         {
18738                             tag: "span",
18739                             html: "Loading"
18740                         }
18741                         
18742                     ]
18743                 }
18744             ]
18745         };
18746         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
18747         
18748         var size = this.el.select('.fc-content', true).first().getSize();
18749         this.maskEl.setSize(size.width, size.height);
18750         this.maskEl.enableDisplayMode("block");
18751         if(!this.loadMask){
18752             this.maskEl.hide();
18753         }
18754         
18755         this.store = Roo.factory(this.store, Roo.data);
18756         this.store.on('load', this.onLoad, this);
18757         this.store.on('beforeload', this.onBeforeLoad, this);
18758         
18759         this.resize();
18760         
18761         this.cells = this.el.select('.fc-day',true);
18762         //Roo.log(this.cells);
18763         this.textNodes = this.el.query('.fc-day-number');
18764         this.cells.addClassOnOver('fc-state-hover');
18765         
18766         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
18767         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
18768         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
18769         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
18770         
18771         this.on('monthchange', this.onMonthChange, this);
18772         
18773         this.update(new Date().clearTime());
18774     },
18775     
18776     resize : function() {
18777         var sz  = this.el.getSize();
18778         
18779         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
18780         this.el.select('.fc-day-content div',true).setHeight(34);
18781     },
18782     
18783     
18784     // private
18785     showPrevMonth : function(e){
18786         this.update(this.activeDate.add("mo", -1));
18787     },
18788     showToday : function(e){
18789         this.update(new Date().clearTime());
18790     },
18791     // private
18792     showNextMonth : function(e){
18793         this.update(this.activeDate.add("mo", 1));
18794     },
18795
18796     // private
18797     showPrevYear : function(){
18798         this.update(this.activeDate.add("y", -1));
18799     },
18800
18801     // private
18802     showNextYear : function(){
18803         this.update(this.activeDate.add("y", 1));
18804     },
18805
18806     
18807    // private
18808     update : function(date)
18809     {
18810         var vd = this.activeDate;
18811         this.activeDate = date;
18812 //        if(vd && this.el){
18813 //            var t = date.getTime();
18814 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
18815 //                Roo.log('using add remove');
18816 //                
18817 //                this.fireEvent('monthchange', this, date);
18818 //                
18819 //                this.cells.removeClass("fc-state-highlight");
18820 //                this.cells.each(function(c){
18821 //                   if(c.dateValue == t){
18822 //                       c.addClass("fc-state-highlight");
18823 //                       setTimeout(function(){
18824 //                            try{c.dom.firstChild.focus();}catch(e){}
18825 //                       }, 50);
18826 //                       return false;
18827 //                   }
18828 //                   return true;
18829 //                });
18830 //                return;
18831 //            }
18832 //        }
18833         
18834         var days = date.getDaysInMonth();
18835         
18836         var firstOfMonth = date.getFirstDateOfMonth();
18837         var startingPos = firstOfMonth.getDay()-this.startDay;
18838         
18839         if(startingPos < this.startDay){
18840             startingPos += 7;
18841         }
18842         
18843         var pm = date.add(Date.MONTH, -1);
18844         var prevStart = pm.getDaysInMonth()-startingPos;
18845 //        
18846         this.cells = this.el.select('.fc-day',true);
18847         this.textNodes = this.el.query('.fc-day-number');
18848         this.cells.addClassOnOver('fc-state-hover');
18849         
18850         var cells = this.cells.elements;
18851         var textEls = this.textNodes;
18852         
18853         Roo.each(cells, function(cell){
18854             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
18855         });
18856         
18857         days += startingPos;
18858
18859         // convert everything to numbers so it's fast
18860         var day = 86400000;
18861         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
18862         //Roo.log(d);
18863         //Roo.log(pm);
18864         //Roo.log(prevStart);
18865         
18866         var today = new Date().clearTime().getTime();
18867         var sel = date.clearTime().getTime();
18868         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
18869         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
18870         var ddMatch = this.disabledDatesRE;
18871         var ddText = this.disabledDatesText;
18872         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
18873         var ddaysText = this.disabledDaysText;
18874         var format = this.format;
18875         
18876         var setCellClass = function(cal, cell){
18877             cell.row = 0;
18878             cell.events = [];
18879             cell.more = [];
18880             //Roo.log('set Cell Class');
18881             cell.title = "";
18882             var t = d.getTime();
18883             
18884             //Roo.log(d);
18885             
18886             cell.dateValue = t;
18887             if(t == today){
18888                 cell.className += " fc-today";
18889                 cell.className += " fc-state-highlight";
18890                 cell.title = cal.todayText;
18891             }
18892             if(t == sel){
18893                 // disable highlight in other month..
18894                 //cell.className += " fc-state-highlight";
18895                 
18896             }
18897             // disabling
18898             if(t < min) {
18899                 cell.className = " fc-state-disabled";
18900                 cell.title = cal.minText;
18901                 return;
18902             }
18903             if(t > max) {
18904                 cell.className = " fc-state-disabled";
18905                 cell.title = cal.maxText;
18906                 return;
18907             }
18908             if(ddays){
18909                 if(ddays.indexOf(d.getDay()) != -1){
18910                     cell.title = ddaysText;
18911                     cell.className = " fc-state-disabled";
18912                 }
18913             }
18914             if(ddMatch && format){
18915                 var fvalue = d.dateFormat(format);
18916                 if(ddMatch.test(fvalue)){
18917                     cell.title = ddText.replace("%0", fvalue);
18918                     cell.className = " fc-state-disabled";
18919                 }
18920             }
18921             
18922             if (!cell.initialClassName) {
18923                 cell.initialClassName = cell.dom.className;
18924             }
18925             
18926             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
18927         };
18928
18929         var i = 0;
18930         
18931         for(; i < startingPos; i++) {
18932             textEls[i].innerHTML = (++prevStart);
18933             d.setDate(d.getDate()+1);
18934             
18935             cells[i].className = "fc-past fc-other-month";
18936             setCellClass(this, cells[i]);
18937         }
18938         
18939         var intDay = 0;
18940         
18941         for(; i < days; i++){
18942             intDay = i - startingPos + 1;
18943             textEls[i].innerHTML = (intDay);
18944             d.setDate(d.getDate()+1);
18945             
18946             cells[i].className = ''; // "x-date-active";
18947             setCellClass(this, cells[i]);
18948         }
18949         var extraDays = 0;
18950         
18951         for(; i < 42; i++) {
18952             textEls[i].innerHTML = (++extraDays);
18953             d.setDate(d.getDate()+1);
18954             
18955             cells[i].className = "fc-future fc-other-month";
18956             setCellClass(this, cells[i]);
18957         }
18958         
18959         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
18960         
18961         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
18962         
18963         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
18964         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
18965         
18966         if(totalRows != 6){
18967             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
18968             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
18969         }
18970         
18971         this.fireEvent('monthchange', this, date);
18972         
18973         
18974         /*
18975         if(!this.internalRender){
18976             var main = this.el.dom.firstChild;
18977             var w = main.offsetWidth;
18978             this.el.setWidth(w + this.el.getBorderWidth("lr"));
18979             Roo.fly(main).setWidth(w);
18980             this.internalRender = true;
18981             // opera does not respect the auto grow header center column
18982             // then, after it gets a width opera refuses to recalculate
18983             // without a second pass
18984             if(Roo.isOpera && !this.secondPass){
18985                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
18986                 this.secondPass = true;
18987                 this.update.defer(10, this, [date]);
18988             }
18989         }
18990         */
18991         
18992     },
18993     
18994     findCell : function(dt) {
18995         dt = dt.clearTime().getTime();
18996         var ret = false;
18997         this.cells.each(function(c){
18998             //Roo.log("check " +c.dateValue + '?=' + dt);
18999             if(c.dateValue == dt){
19000                 ret = c;
19001                 return false;
19002             }
19003             return true;
19004         });
19005         
19006         return ret;
19007     },
19008     
19009     findCells : function(ev) {
19010         var s = ev.start.clone().clearTime().getTime();
19011        // Roo.log(s);
19012         var e= ev.end.clone().clearTime().getTime();
19013        // Roo.log(e);
19014         var ret = [];
19015         this.cells.each(function(c){
19016              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19017             
19018             if(c.dateValue > e){
19019                 return ;
19020             }
19021             if(c.dateValue < s){
19022                 return ;
19023             }
19024             ret.push(c);
19025         });
19026         
19027         return ret;    
19028     },
19029     
19030 //    findBestRow: function(cells)
19031 //    {
19032 //        var ret = 0;
19033 //        
19034 //        for (var i =0 ; i < cells.length;i++) {
19035 //            ret  = Math.max(cells[i].rows || 0,ret);
19036 //        }
19037 //        return ret;
19038 //        
19039 //    },
19040     
19041     
19042     addItem : function(ev)
19043     {
19044         // look for vertical location slot in
19045         var cells = this.findCells(ev);
19046         
19047 //        ev.row = this.findBestRow(cells);
19048         
19049         // work out the location.
19050         
19051         var crow = false;
19052         var rows = [];
19053         for(var i =0; i < cells.length; i++) {
19054             
19055             cells[i].row = cells[0].row;
19056             
19057             if(i == 0){
19058                 cells[i].row = cells[i].row + 1;
19059             }
19060             
19061             if (!crow) {
19062                 crow = {
19063                     start : cells[i],
19064                     end :  cells[i]
19065                 };
19066                 continue;
19067             }
19068             if (crow.start.getY() == cells[i].getY()) {
19069                 // on same row.
19070                 crow.end = cells[i];
19071                 continue;
19072             }
19073             // different row.
19074             rows.push(crow);
19075             crow = {
19076                 start: cells[i],
19077                 end : cells[i]
19078             };
19079             
19080         }
19081         
19082         rows.push(crow);
19083         ev.els = [];
19084         ev.rows = rows;
19085         ev.cells = cells;
19086         
19087         cells[0].events.push(ev);
19088         
19089         this.calevents.push(ev);
19090     },
19091     
19092     clearEvents: function() {
19093         
19094         if(!this.calevents){
19095             return;
19096         }
19097         
19098         Roo.each(this.cells.elements, function(c){
19099             c.row = 0;
19100             c.events = [];
19101             c.more = [];
19102         });
19103         
19104         Roo.each(this.calevents, function(e) {
19105             Roo.each(e.els, function(el) {
19106                 el.un('mouseenter' ,this.onEventEnter, this);
19107                 el.un('mouseleave' ,this.onEventLeave, this);
19108                 el.remove();
19109             },this);
19110         },this);
19111         
19112         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19113             e.remove();
19114         });
19115         
19116     },
19117     
19118     renderEvents: function()
19119     {   
19120         var _this = this;
19121         
19122         this.cells.each(function(c) {
19123             
19124             if(c.row < 5){
19125                 return;
19126             }
19127             
19128             var ev = c.events;
19129             
19130             var r = 4;
19131             if(c.row != c.events.length){
19132                 r = 4 - (4 - (c.row - c.events.length));
19133             }
19134             
19135             c.events = ev.slice(0, r);
19136             c.more = ev.slice(r);
19137             
19138             if(c.more.length && c.more.length == 1){
19139                 c.events.push(c.more.pop());
19140             }
19141             
19142             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19143             
19144         });
19145             
19146         this.cells.each(function(c) {
19147             
19148             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19149             
19150             
19151             for (var e = 0; e < c.events.length; e++){
19152                 var ev = c.events[e];
19153                 var rows = ev.rows;
19154                 
19155                 for(var i = 0; i < rows.length; i++) {
19156                 
19157                     // how many rows should it span..
19158
19159                     var  cfg = {
19160                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19161                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19162
19163                         unselectable : "on",
19164                         cn : [
19165                             {
19166                                 cls: 'fc-event-inner',
19167                                 cn : [
19168     //                                {
19169     //                                  tag:'span',
19170     //                                  cls: 'fc-event-time',
19171     //                                  html : cells.length > 1 ? '' : ev.time
19172     //                                },
19173                                     {
19174                                       tag:'span',
19175                                       cls: 'fc-event-title',
19176                                       html : String.format('{0}', ev.title)
19177                                     }
19178
19179
19180                                 ]
19181                             },
19182                             {
19183                                 cls: 'ui-resizable-handle ui-resizable-e',
19184                                 html : '&nbsp;&nbsp;&nbsp'
19185                             }
19186
19187                         ]
19188                     };
19189
19190                     if (i == 0) {
19191                         cfg.cls += ' fc-event-start';
19192                     }
19193                     if ((i+1) == rows.length) {
19194                         cfg.cls += ' fc-event-end';
19195                     }
19196
19197                     var ctr = _this.el.select('.fc-event-container',true).first();
19198                     var cg = ctr.createChild(cfg);
19199
19200                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19201                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19202
19203                     var r = (c.more.length) ? 1 : 0;
19204                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19205                     cg.setWidth(ebox.right - sbox.x -2);
19206
19207                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19208                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19209                     cg.on('click', _this.onEventClick, _this, ev);
19210
19211                     ev.els.push(cg);
19212                     
19213                 }
19214                 
19215             }
19216             
19217             
19218             if(c.more.length){
19219                 var  cfg = {
19220                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19221                     style : 'position: absolute',
19222                     unselectable : "on",
19223                     cn : [
19224                         {
19225                             cls: 'fc-event-inner',
19226                             cn : [
19227                                 {
19228                                   tag:'span',
19229                                   cls: 'fc-event-title',
19230                                   html : 'More'
19231                                 }
19232
19233
19234                             ]
19235                         },
19236                         {
19237                             cls: 'ui-resizable-handle ui-resizable-e',
19238                             html : '&nbsp;&nbsp;&nbsp'
19239                         }
19240
19241                     ]
19242                 };
19243
19244                 var ctr = _this.el.select('.fc-event-container',true).first();
19245                 var cg = ctr.createChild(cfg);
19246
19247                 var sbox = c.select('.fc-day-content',true).first().getBox();
19248                 var ebox = c.select('.fc-day-content',true).first().getBox();
19249                 //Roo.log(cg);
19250                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19251                 cg.setWidth(ebox.right - sbox.x -2);
19252
19253                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19254                 
19255             }
19256             
19257         });
19258         
19259         
19260         
19261     },
19262     
19263     onEventEnter: function (e, el,event,d) {
19264         this.fireEvent('evententer', this, el, event);
19265     },
19266     
19267     onEventLeave: function (e, el,event,d) {
19268         this.fireEvent('eventleave', this, el, event);
19269     },
19270     
19271     onEventClick: function (e, el,event,d) {
19272         this.fireEvent('eventclick', this, el, event);
19273     },
19274     
19275     onMonthChange: function () {
19276         this.store.load();
19277     },
19278     
19279     onMoreEventClick: function(e, el, more)
19280     {
19281         var _this = this;
19282         
19283         this.calpopover.placement = 'right';
19284         this.calpopover.setTitle('More');
19285         
19286         this.calpopover.setContent('');
19287         
19288         var ctr = this.calpopover.el.select('.popover-content', true).first();
19289         
19290         Roo.each(more, function(m){
19291             var cfg = {
19292                 cls : 'fc-event-hori fc-event-draggable',
19293                 html : m.title
19294             };
19295             var cg = ctr.createChild(cfg);
19296             
19297             cg.on('click', _this.onEventClick, _this, m);
19298         });
19299         
19300         this.calpopover.show(el);
19301         
19302         
19303     },
19304     
19305     onLoad: function () 
19306     {   
19307         this.calevents = [];
19308         var cal = this;
19309         
19310         if(this.store.getCount() > 0){
19311             this.store.data.each(function(d){
19312                cal.addItem({
19313                     id : d.data.id,
19314                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19315                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19316                     time : d.data.start_time,
19317                     title : d.data.title,
19318                     description : d.data.description,
19319                     venue : d.data.venue
19320                 });
19321             });
19322         }
19323         
19324         this.renderEvents();
19325         
19326         if(this.calevents.length && this.loadMask){
19327             this.maskEl.hide();
19328         }
19329     },
19330     
19331     onBeforeLoad: function()
19332     {
19333         this.clearEvents();
19334         if(this.loadMask){
19335             this.maskEl.show();
19336         }
19337     }
19338 });
19339
19340  
19341  /*
19342  * - LGPL
19343  *
19344  * element
19345  * 
19346  */
19347
19348 /**
19349  * @class Roo.bootstrap.Popover
19350  * @extends Roo.bootstrap.Component
19351  * Bootstrap Popover class
19352  * @cfg {String} html contents of the popover   (or false to use children..)
19353  * @cfg {String} title of popover (or false to hide)
19354  * @cfg {String} placement how it is placed
19355  * @cfg {String} trigger click || hover (or false to trigger manually)
19356  * @cfg {String} over what (parent or false to trigger manually.)
19357  * @cfg {Number} delay - delay before showing
19358  
19359  * @constructor
19360  * Create a new Popover
19361  * @param {Object} config The config object
19362  */
19363
19364 Roo.bootstrap.Popover = function(config){
19365     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19366     
19367     this.addEvents({
19368         // raw events
19369          /**
19370          * @event show
19371          * After the popover show
19372          * 
19373          * @param {Roo.bootstrap.Popover} this
19374          */
19375         "show" : true,
19376         /**
19377          * @event hide
19378          * After the popover hide
19379          * 
19380          * @param {Roo.bootstrap.Popover} this
19381          */
19382         "hide" : true
19383     });
19384 };
19385
19386 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
19387     
19388     title: 'Fill in a title',
19389     html: false,
19390     
19391     placement : 'right',
19392     trigger : 'hover', // hover
19393     
19394     delay : 0,
19395     
19396     over: 'parent',
19397     
19398     can_build_overlaid : false,
19399     
19400     getChildContainer : function()
19401     {
19402         return this.el.select('.popover-content',true).first();
19403     },
19404     
19405     getAutoCreate : function(){
19406          
19407         var cfg = {
19408            cls : 'popover roo-dynamic',
19409            style: 'display:block',
19410            cn : [
19411                 {
19412                     cls : 'arrow'
19413                 },
19414                 {
19415                     cls : 'popover-inner',
19416                     cn : [
19417                         {
19418                             tag: 'h3',
19419                             cls: 'popover-title popover-header',
19420                             html : this.title
19421                         },
19422                         {
19423                             cls : 'popover-content popover-body',
19424                             html : this.html
19425                         }
19426                     ]
19427                     
19428                 }
19429            ]
19430         };
19431         
19432         return cfg;
19433     },
19434     setTitle: function(str)
19435     {
19436         this.title = str;
19437         this.el.select('.popover-title',true).first().dom.innerHTML = str;
19438     },
19439     setContent: function(str)
19440     {
19441         this.html = str;
19442         this.el.select('.popover-content',true).first().dom.innerHTML = str;
19443     },
19444     // as it get's added to the bottom of the page.
19445     onRender : function(ct, position)
19446     {
19447         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19448         if(!this.el){
19449             var cfg = Roo.apply({},  this.getAutoCreate());
19450             cfg.id = Roo.id();
19451             
19452             if (this.cls) {
19453                 cfg.cls += ' ' + this.cls;
19454             }
19455             if (this.style) {
19456                 cfg.style = this.style;
19457             }
19458             //Roo.log("adding to ");
19459             this.el = Roo.get(document.body).createChild(cfg, position);
19460 //            Roo.log(this.el);
19461         }
19462         this.initEvents();
19463     },
19464     
19465     initEvents : function()
19466     {
19467         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
19468         this.el.enableDisplayMode('block');
19469         this.el.hide();
19470         if (this.over === false) {
19471             return; 
19472         }
19473         if (this.triggers === false) {
19474             return;
19475         }
19476         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
19477         var triggers = this.trigger ? this.trigger.split(' ') : [];
19478         Roo.each(triggers, function(trigger) {
19479         
19480             if (trigger == 'click') {
19481                 on_el.on('click', this.toggle, this);
19482             } else if (trigger != 'manual') {
19483                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
19484                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19485       
19486                 on_el.on(eventIn  ,this.enter, this);
19487                 on_el.on(eventOut, this.leave, this);
19488             }
19489         }, this);
19490         
19491     },
19492     
19493     
19494     // private
19495     timeout : null,
19496     hoverState : null,
19497     
19498     toggle : function () {
19499         this.hoverState == 'in' ? this.leave() : this.enter();
19500     },
19501     
19502     enter : function () {
19503         
19504         clearTimeout(this.timeout);
19505     
19506         this.hoverState = 'in';
19507     
19508         if (!this.delay || !this.delay.show) {
19509             this.show();
19510             return;
19511         }
19512         var _t = this;
19513         this.timeout = setTimeout(function () {
19514             if (_t.hoverState == 'in') {
19515                 _t.show();
19516             }
19517         }, this.delay.show)
19518     },
19519     
19520     leave : function() {
19521         clearTimeout(this.timeout);
19522     
19523         this.hoverState = 'out';
19524     
19525         if (!this.delay || !this.delay.hide) {
19526             this.hide();
19527             return;
19528         }
19529         var _t = this;
19530         this.timeout = setTimeout(function () {
19531             if (_t.hoverState == 'out') {
19532                 _t.hide();
19533             }
19534         }, this.delay.hide)
19535     },
19536     
19537     show : function (on_el)
19538     {
19539         if (!on_el) {
19540             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
19541         }
19542         
19543         // set content.
19544         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
19545         if (this.html !== false) {
19546             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
19547         }
19548         this.el.removeClass([
19549             'fade','top','bottom', 'left', 'right','in',
19550             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19551         ]);
19552         if (!this.title.length) {
19553             this.el.select('.popover-title',true).hide();
19554         }
19555         
19556         var placement = typeof this.placement == 'function' ?
19557             this.placement.call(this, this.el, on_el) :
19558             this.placement;
19559             
19560         var autoToken = /\s?auto?\s?/i;
19561         var autoPlace = autoToken.test(placement);
19562         if (autoPlace) {
19563             placement = placement.replace(autoToken, '') || 'top';
19564         }
19565         
19566         //this.el.detach()
19567         //this.el.setXY([0,0]);
19568         this.el.show();
19569         this.el.dom.style.display='block';
19570         this.el.addClass(placement);
19571         
19572         //this.el.appendTo(on_el);
19573         
19574         var p = this.getPosition();
19575         var box = this.el.getBox();
19576         
19577         if (autoPlace) {
19578             // fixme..
19579         }
19580         var align = Roo.bootstrap.Popover.alignment[placement];
19581         
19582 //        Roo.log(align);
19583         this.el.alignTo(on_el, align[0],align[1]);
19584         //var arrow = this.el.select('.arrow',true).first();
19585         //arrow.set(align[2], 
19586         
19587         this.el.addClass('in');
19588         
19589         
19590         if (this.el.hasClass('fade')) {
19591             // fade it?
19592         }
19593         
19594         this.hoverState = 'in';
19595         
19596         this.fireEvent('show', this);
19597         
19598     },
19599     hide : function()
19600     {
19601         this.el.setXY([0,0]);
19602         this.el.removeClass('in');
19603         this.el.hide();
19604         this.hoverState = null;
19605         
19606         this.fireEvent('hide', this);
19607     }
19608     
19609 });
19610
19611 Roo.bootstrap.Popover.alignment = {
19612     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
19613     'right' : ['l-r', [10,0], 'left bs-popover-left'],
19614     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
19615     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
19616 };
19617
19618  /*
19619  * - LGPL
19620  *
19621  * Progress
19622  * 
19623  */
19624
19625 /**
19626  * @class Roo.bootstrap.Progress
19627  * @extends Roo.bootstrap.Component
19628  * Bootstrap Progress class
19629  * @cfg {Boolean} striped striped of the progress bar
19630  * @cfg {Boolean} active animated of the progress bar
19631  * 
19632  * 
19633  * @constructor
19634  * Create a new Progress
19635  * @param {Object} config The config object
19636  */
19637
19638 Roo.bootstrap.Progress = function(config){
19639     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
19640 };
19641
19642 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
19643     
19644     striped : false,
19645     active: false,
19646     
19647     getAutoCreate : function(){
19648         var cfg = {
19649             tag: 'div',
19650             cls: 'progress'
19651         };
19652         
19653         
19654         if(this.striped){
19655             cfg.cls += ' progress-striped';
19656         }
19657       
19658         if(this.active){
19659             cfg.cls += ' active';
19660         }
19661         
19662         
19663         return cfg;
19664     }
19665    
19666 });
19667
19668  
19669
19670  /*
19671  * - LGPL
19672  *
19673  * ProgressBar
19674  * 
19675  */
19676
19677 /**
19678  * @class Roo.bootstrap.ProgressBar
19679  * @extends Roo.bootstrap.Component
19680  * Bootstrap ProgressBar class
19681  * @cfg {Number} aria_valuenow aria-value now
19682  * @cfg {Number} aria_valuemin aria-value min
19683  * @cfg {Number} aria_valuemax aria-value max
19684  * @cfg {String} label label for the progress bar
19685  * @cfg {String} panel (success | info | warning | danger )
19686  * @cfg {String} role role of the progress bar
19687  * @cfg {String} sr_only text
19688  * 
19689  * 
19690  * @constructor
19691  * Create a new ProgressBar
19692  * @param {Object} config The config object
19693  */
19694
19695 Roo.bootstrap.ProgressBar = function(config){
19696     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
19697 };
19698
19699 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
19700     
19701     aria_valuenow : 0,
19702     aria_valuemin : 0,
19703     aria_valuemax : 100,
19704     label : false,
19705     panel : false,
19706     role : false,
19707     sr_only: false,
19708     
19709     getAutoCreate : function()
19710     {
19711         
19712         var cfg = {
19713             tag: 'div',
19714             cls: 'progress-bar',
19715             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
19716         };
19717         
19718         if(this.sr_only){
19719             cfg.cn = {
19720                 tag: 'span',
19721                 cls: 'sr-only',
19722                 html: this.sr_only
19723             }
19724         }
19725         
19726         if(this.role){
19727             cfg.role = this.role;
19728         }
19729         
19730         if(this.aria_valuenow){
19731             cfg['aria-valuenow'] = this.aria_valuenow;
19732         }
19733         
19734         if(this.aria_valuemin){
19735             cfg['aria-valuemin'] = this.aria_valuemin;
19736         }
19737         
19738         if(this.aria_valuemax){
19739             cfg['aria-valuemax'] = this.aria_valuemax;
19740         }
19741         
19742         if(this.label && !this.sr_only){
19743             cfg.html = this.label;
19744         }
19745         
19746         if(this.panel){
19747             cfg.cls += ' progress-bar-' + this.panel;
19748         }
19749         
19750         return cfg;
19751     },
19752     
19753     update : function(aria_valuenow)
19754     {
19755         this.aria_valuenow = aria_valuenow;
19756         
19757         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
19758     }
19759    
19760 });
19761
19762  
19763
19764  /*
19765  * - LGPL
19766  *
19767  * column
19768  * 
19769  */
19770
19771 /**
19772  * @class Roo.bootstrap.TabGroup
19773  * @extends Roo.bootstrap.Column
19774  * Bootstrap Column class
19775  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
19776  * @cfg {Boolean} carousel true to make the group behave like a carousel
19777  * @cfg {Boolean} bullets show bullets for the panels
19778  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
19779  * @cfg {Number} timer auto slide timer .. default 0 millisecond
19780  * @cfg {Boolean} showarrow (true|false) show arrow default true
19781  * 
19782  * @constructor
19783  * Create a new TabGroup
19784  * @param {Object} config The config object
19785  */
19786
19787 Roo.bootstrap.TabGroup = function(config){
19788     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
19789     if (!this.navId) {
19790         this.navId = Roo.id();
19791     }
19792     this.tabs = [];
19793     Roo.bootstrap.TabGroup.register(this);
19794     
19795 };
19796
19797 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
19798     
19799     carousel : false,
19800     transition : false,
19801     bullets : 0,
19802     timer : 0,
19803     autoslide : false,
19804     slideFn : false,
19805     slideOnTouch : false,
19806     showarrow : true,
19807     
19808     getAutoCreate : function()
19809     {
19810         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
19811         
19812         cfg.cls += ' tab-content';
19813         
19814         if (this.carousel) {
19815             cfg.cls += ' carousel slide';
19816             
19817             cfg.cn = [{
19818                cls : 'carousel-inner',
19819                cn : []
19820             }];
19821         
19822             if(this.bullets  && !Roo.isTouch){
19823                 
19824                 var bullets = {
19825                     cls : 'carousel-bullets',
19826                     cn : []
19827                 };
19828                
19829                 if(this.bullets_cls){
19830                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
19831                 }
19832                 
19833                 bullets.cn.push({
19834                     cls : 'clear'
19835                 });
19836                 
19837                 cfg.cn[0].cn.push(bullets);
19838             }
19839             
19840             if(this.showarrow){
19841                 cfg.cn[0].cn.push({
19842                     tag : 'div',
19843                     class : 'carousel-arrow',
19844                     cn : [
19845                         {
19846                             tag : 'div',
19847                             class : 'carousel-prev',
19848                             cn : [
19849                                 {
19850                                     tag : 'i',
19851                                     class : 'fa fa-chevron-left'
19852                                 }
19853                             ]
19854                         },
19855                         {
19856                             tag : 'div',
19857                             class : 'carousel-next',
19858                             cn : [
19859                                 {
19860                                     tag : 'i',
19861                                     class : 'fa fa-chevron-right'
19862                                 }
19863                             ]
19864                         }
19865                     ]
19866                 });
19867             }
19868             
19869         }
19870         
19871         return cfg;
19872     },
19873     
19874     initEvents:  function()
19875     {
19876 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
19877 //            this.el.on("touchstart", this.onTouchStart, this);
19878 //        }
19879         
19880         if(this.autoslide){
19881             var _this = this;
19882             
19883             this.slideFn = window.setInterval(function() {
19884                 _this.showPanelNext();
19885             }, this.timer);
19886         }
19887         
19888         if(this.showarrow){
19889             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
19890             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
19891         }
19892         
19893         
19894     },
19895     
19896 //    onTouchStart : function(e, el, o)
19897 //    {
19898 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
19899 //            return;
19900 //        }
19901 //        
19902 //        this.showPanelNext();
19903 //    },
19904     
19905     
19906     getChildContainer : function()
19907     {
19908         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
19909     },
19910     
19911     /**
19912     * register a Navigation item
19913     * @param {Roo.bootstrap.NavItem} the navitem to add
19914     */
19915     register : function(item)
19916     {
19917         this.tabs.push( item);
19918         item.navId = this.navId; // not really needed..
19919         this.addBullet();
19920     
19921     },
19922     
19923     getActivePanel : function()
19924     {
19925         var r = false;
19926         Roo.each(this.tabs, function(t) {
19927             if (t.active) {
19928                 r = t;
19929                 return false;
19930             }
19931             return null;
19932         });
19933         return r;
19934         
19935     },
19936     getPanelByName : function(n)
19937     {
19938         var r = false;
19939         Roo.each(this.tabs, function(t) {
19940             if (t.tabId == n) {
19941                 r = t;
19942                 return false;
19943             }
19944             return null;
19945         });
19946         return r;
19947     },
19948     indexOfPanel : function(p)
19949     {
19950         var r = false;
19951         Roo.each(this.tabs, function(t,i) {
19952             if (t.tabId == p.tabId) {
19953                 r = i;
19954                 return false;
19955             }
19956             return null;
19957         });
19958         return r;
19959     },
19960     /**
19961      * show a specific panel
19962      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
19963      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
19964      */
19965     showPanel : function (pan)
19966     {
19967         if(this.transition || typeof(pan) == 'undefined'){
19968             Roo.log("waiting for the transitionend");
19969             return false;
19970         }
19971         
19972         if (typeof(pan) == 'number') {
19973             pan = this.tabs[pan];
19974         }
19975         
19976         if (typeof(pan) == 'string') {
19977             pan = this.getPanelByName(pan);
19978         }
19979         
19980         var cur = this.getActivePanel();
19981         
19982         if(!pan || !cur){
19983             Roo.log('pan or acitve pan is undefined');
19984             return false;
19985         }
19986         
19987         if (pan.tabId == this.getActivePanel().tabId) {
19988             return true;
19989         }
19990         
19991         if (false === cur.fireEvent('beforedeactivate')) {
19992             return false;
19993         }
19994         
19995         if(this.bullets > 0 && !Roo.isTouch){
19996             this.setActiveBullet(this.indexOfPanel(pan));
19997         }
19998         
19999         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20000             
20001             //class="carousel-item carousel-item-next carousel-item-left"
20002             
20003             this.transition = true;
20004             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20005             var lr = dir == 'next' ? 'left' : 'right';
20006             pan.el.addClass(dir); // or prev
20007             pan.el.addClass('carousel-item-' + dir); // or prev
20008             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20009             cur.el.addClass(lr); // or right
20010             pan.el.addClass(lr);
20011             cur.el.addClass('carousel-item-' +lr); // or right
20012             pan.el.addClass('carousel-item-' +lr);
20013             
20014             
20015             var _this = this;
20016             cur.el.on('transitionend', function() {
20017                 Roo.log("trans end?");
20018                 
20019                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20020                 pan.setActive(true);
20021                 
20022                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20023                 cur.setActive(false);
20024                 
20025                 _this.transition = false;
20026                 
20027             }, this, { single:  true } );
20028             
20029             return true;
20030         }
20031         
20032         cur.setActive(false);
20033         pan.setActive(true);
20034         
20035         return true;
20036         
20037     },
20038     showPanelNext : function()
20039     {
20040         var i = this.indexOfPanel(this.getActivePanel());
20041         
20042         if (i >= this.tabs.length - 1 && !this.autoslide) {
20043             return;
20044         }
20045         
20046         if (i >= this.tabs.length - 1 && this.autoslide) {
20047             i = -1;
20048         }
20049         
20050         this.showPanel(this.tabs[i+1]);
20051     },
20052     
20053     showPanelPrev : function()
20054     {
20055         var i = this.indexOfPanel(this.getActivePanel());
20056         
20057         if (i  < 1 && !this.autoslide) {
20058             return;
20059         }
20060         
20061         if (i < 1 && this.autoslide) {
20062             i = this.tabs.length;
20063         }
20064         
20065         this.showPanel(this.tabs[i-1]);
20066     },
20067     
20068     
20069     addBullet: function()
20070     {
20071         if(!this.bullets || Roo.isTouch){
20072             return;
20073         }
20074         var ctr = this.el.select('.carousel-bullets',true).first();
20075         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20076         var bullet = ctr.createChild({
20077             cls : 'bullet bullet-' + i
20078         },ctr.dom.lastChild);
20079         
20080         
20081         var _this = this;
20082         
20083         bullet.on('click', (function(e, el, o, ii, t){
20084
20085             e.preventDefault();
20086
20087             this.showPanel(ii);
20088
20089             if(this.autoslide && this.slideFn){
20090                 clearInterval(this.slideFn);
20091                 this.slideFn = window.setInterval(function() {
20092                     _this.showPanelNext();
20093                 }, this.timer);
20094             }
20095
20096         }).createDelegate(this, [i, bullet], true));
20097                 
20098         
20099     },
20100      
20101     setActiveBullet : function(i)
20102     {
20103         if(Roo.isTouch){
20104             return;
20105         }
20106         
20107         Roo.each(this.el.select('.bullet', true).elements, function(el){
20108             el.removeClass('selected');
20109         });
20110
20111         var bullet = this.el.select('.bullet-' + i, true).first();
20112         
20113         if(!bullet){
20114             return;
20115         }
20116         
20117         bullet.addClass('selected');
20118     }
20119     
20120     
20121   
20122 });
20123
20124  
20125
20126  
20127  
20128 Roo.apply(Roo.bootstrap.TabGroup, {
20129     
20130     groups: {},
20131      /**
20132     * register a Navigation Group
20133     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20134     */
20135     register : function(navgrp)
20136     {
20137         this.groups[navgrp.navId] = navgrp;
20138         
20139     },
20140     /**
20141     * fetch a Navigation Group based on the navigation ID
20142     * if one does not exist , it will get created.
20143     * @param {string} the navgroup to add
20144     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20145     */
20146     get: function(navId) {
20147         if (typeof(this.groups[navId]) == 'undefined') {
20148             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20149         }
20150         return this.groups[navId] ;
20151     }
20152     
20153     
20154     
20155 });
20156
20157  /*
20158  * - LGPL
20159  *
20160  * TabPanel
20161  * 
20162  */
20163
20164 /**
20165  * @class Roo.bootstrap.TabPanel
20166  * @extends Roo.bootstrap.Component
20167  * Bootstrap TabPanel class
20168  * @cfg {Boolean} active panel active
20169  * @cfg {String} html panel content
20170  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20171  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20172  * @cfg {String} href click to link..
20173  * 
20174  * 
20175  * @constructor
20176  * Create a new TabPanel
20177  * @param {Object} config The config object
20178  */
20179
20180 Roo.bootstrap.TabPanel = function(config){
20181     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20182     this.addEvents({
20183         /**
20184              * @event changed
20185              * Fires when the active status changes
20186              * @param {Roo.bootstrap.TabPanel} this
20187              * @param {Boolean} state the new state
20188             
20189          */
20190         'changed': true,
20191         /**
20192              * @event beforedeactivate
20193              * Fires before a tab is de-activated - can be used to do validation on a form.
20194              * @param {Roo.bootstrap.TabPanel} this
20195              * @return {Boolean} false if there is an error
20196             
20197          */
20198         'beforedeactivate': true
20199      });
20200     
20201     this.tabId = this.tabId || Roo.id();
20202   
20203 };
20204
20205 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
20206     
20207     active: false,
20208     html: false,
20209     tabId: false,
20210     navId : false,
20211     href : '',
20212     
20213     getAutoCreate : function(){
20214         
20215         
20216         var cfg = {
20217             tag: 'div',
20218             // item is needed for carousel - not sure if it has any effect otherwise
20219             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20220             html: this.html || ''
20221         };
20222         
20223         if(this.active){
20224             cfg.cls += ' active';
20225         }
20226         
20227         if(this.tabId){
20228             cfg.tabId = this.tabId;
20229         }
20230         
20231         
20232         
20233         return cfg;
20234     },
20235     
20236     initEvents:  function()
20237     {
20238         var p = this.parent();
20239         
20240         this.navId = this.navId || p.navId;
20241         
20242         if (typeof(this.navId) != 'undefined') {
20243             // not really needed.. but just in case.. parent should be a NavGroup.
20244             var tg = Roo.bootstrap.TabGroup.get(this.navId);
20245             
20246             tg.register(this);
20247             
20248             var i = tg.tabs.length - 1;
20249             
20250             if(this.active && tg.bullets > 0 && i < tg.bullets){
20251                 tg.setActiveBullet(i);
20252             }
20253         }
20254         
20255         this.el.on('click', this.onClick, this);
20256         
20257         if(Roo.isTouch){
20258             this.el.on("touchstart", this.onTouchStart, this);
20259             this.el.on("touchmove", this.onTouchMove, this);
20260             this.el.on("touchend", this.onTouchEnd, this);
20261         }
20262         
20263     },
20264     
20265     onRender : function(ct, position)
20266     {
20267         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20268     },
20269     
20270     setActive : function(state)
20271     {
20272         Roo.log("panel - set active " + this.tabId + "=" + state);
20273         
20274         this.active = state;
20275         if (!state) {
20276             this.el.removeClass('active');
20277             
20278         } else  if (!this.el.hasClass('active')) {
20279             this.el.addClass('active');
20280         }
20281         
20282         this.fireEvent('changed', this, state);
20283     },
20284     
20285     onClick : function(e)
20286     {
20287         e.preventDefault();
20288         
20289         if(!this.href.length){
20290             return;
20291         }
20292         
20293         window.location.href = this.href;
20294     },
20295     
20296     startX : 0,
20297     startY : 0,
20298     endX : 0,
20299     endY : 0,
20300     swiping : false,
20301     
20302     onTouchStart : function(e)
20303     {
20304         this.swiping = false;
20305         
20306         this.startX = e.browserEvent.touches[0].clientX;
20307         this.startY = e.browserEvent.touches[0].clientY;
20308     },
20309     
20310     onTouchMove : function(e)
20311     {
20312         this.swiping = true;
20313         
20314         this.endX = e.browserEvent.touches[0].clientX;
20315         this.endY = e.browserEvent.touches[0].clientY;
20316     },
20317     
20318     onTouchEnd : function(e)
20319     {
20320         if(!this.swiping){
20321             this.onClick(e);
20322             return;
20323         }
20324         
20325         var tabGroup = this.parent();
20326         
20327         if(this.endX > this.startX){ // swiping right
20328             tabGroup.showPanelPrev();
20329             return;
20330         }
20331         
20332         if(this.startX > this.endX){ // swiping left
20333             tabGroup.showPanelNext();
20334             return;
20335         }
20336     }
20337     
20338     
20339 });
20340  
20341
20342  
20343
20344  /*
20345  * - LGPL
20346  *
20347  * DateField
20348  * 
20349  */
20350
20351 /**
20352  * @class Roo.bootstrap.DateField
20353  * @extends Roo.bootstrap.Input
20354  * Bootstrap DateField class
20355  * @cfg {Number} weekStart default 0
20356  * @cfg {String} viewMode default empty, (months|years)
20357  * @cfg {String} minViewMode default empty, (months|years)
20358  * @cfg {Number} startDate default -Infinity
20359  * @cfg {Number} endDate default Infinity
20360  * @cfg {Boolean} todayHighlight default false
20361  * @cfg {Boolean} todayBtn default false
20362  * @cfg {Boolean} calendarWeeks default false
20363  * @cfg {Object} daysOfWeekDisabled default empty
20364  * @cfg {Boolean} singleMode default false (true | false)
20365  * 
20366  * @cfg {Boolean} keyboardNavigation default true
20367  * @cfg {String} language default en
20368  * 
20369  * @constructor
20370  * Create a new DateField
20371  * @param {Object} config The config object
20372  */
20373
20374 Roo.bootstrap.DateField = function(config){
20375     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20376      this.addEvents({
20377             /**
20378              * @event show
20379              * Fires when this field show.
20380              * @param {Roo.bootstrap.DateField} this
20381              * @param {Mixed} date The date value
20382              */
20383             show : true,
20384             /**
20385              * @event show
20386              * Fires when this field hide.
20387              * @param {Roo.bootstrap.DateField} this
20388              * @param {Mixed} date The date value
20389              */
20390             hide : true,
20391             /**
20392              * @event select
20393              * Fires when select a date.
20394              * @param {Roo.bootstrap.DateField} this
20395              * @param {Mixed} date The date value
20396              */
20397             select : true,
20398             /**
20399              * @event beforeselect
20400              * Fires when before select a date.
20401              * @param {Roo.bootstrap.DateField} this
20402              * @param {Mixed} date The date value
20403              */
20404             beforeselect : true
20405         });
20406 };
20407
20408 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
20409     
20410     /**
20411      * @cfg {String} format
20412      * The default date format string which can be overriden for localization support.  The format must be
20413      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20414      */
20415     format : "m/d/y",
20416     /**
20417      * @cfg {String} altFormats
20418      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20419      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20420      */
20421     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20422     
20423     weekStart : 0,
20424     
20425     viewMode : '',
20426     
20427     minViewMode : '',
20428     
20429     todayHighlight : false,
20430     
20431     todayBtn: false,
20432     
20433     language: 'en',
20434     
20435     keyboardNavigation: true,
20436     
20437     calendarWeeks: false,
20438     
20439     startDate: -Infinity,
20440     
20441     endDate: Infinity,
20442     
20443     daysOfWeekDisabled: [],
20444     
20445     _events: [],
20446     
20447     singleMode : false,
20448     
20449     UTCDate: function()
20450     {
20451         return new Date(Date.UTC.apply(Date, arguments));
20452     },
20453     
20454     UTCToday: function()
20455     {
20456         var today = new Date();
20457         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20458     },
20459     
20460     getDate: function() {
20461             var d = this.getUTCDate();
20462             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20463     },
20464     
20465     getUTCDate: function() {
20466             return this.date;
20467     },
20468     
20469     setDate: function(d) {
20470             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20471     },
20472     
20473     setUTCDate: function(d) {
20474             this.date = d;
20475             this.setValue(this.formatDate(this.date));
20476     },
20477         
20478     onRender: function(ct, position)
20479     {
20480         
20481         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20482         
20483         this.language = this.language || 'en';
20484         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20485         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20486         
20487         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
20488         this.format = this.format || 'm/d/y';
20489         this.isInline = false;
20490         this.isInput = true;
20491         this.component = this.el.select('.add-on', true).first() || false;
20492         this.component = (this.component && this.component.length === 0) ? false : this.component;
20493         this.hasInput = this.component && this.inputEl().length;
20494         
20495         if (typeof(this.minViewMode === 'string')) {
20496             switch (this.minViewMode) {
20497                 case 'months':
20498                     this.minViewMode = 1;
20499                     break;
20500                 case 'years':
20501                     this.minViewMode = 2;
20502                     break;
20503                 default:
20504                     this.minViewMode = 0;
20505                     break;
20506             }
20507         }
20508         
20509         if (typeof(this.viewMode === 'string')) {
20510             switch (this.viewMode) {
20511                 case 'months':
20512                     this.viewMode = 1;
20513                     break;
20514                 case 'years':
20515                     this.viewMode = 2;
20516                     break;
20517                 default:
20518                     this.viewMode = 0;
20519                     break;
20520             }
20521         }
20522                 
20523         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
20524         
20525 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
20526         
20527         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20528         
20529         this.picker().on('mousedown', this.onMousedown, this);
20530         this.picker().on('click', this.onClick, this);
20531         
20532         this.picker().addClass('datepicker-dropdown');
20533         
20534         this.startViewMode = this.viewMode;
20535         
20536         if(this.singleMode){
20537             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
20538                 v.setVisibilityMode(Roo.Element.DISPLAY);
20539                 v.hide();
20540             });
20541             
20542             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20543                 v.setStyle('width', '189px');
20544             });
20545         }
20546         
20547         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
20548             if(!this.calendarWeeks){
20549                 v.remove();
20550                 return;
20551             }
20552             
20553             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20554             v.attr('colspan', function(i, val){
20555                 return parseInt(val) + 1;
20556             });
20557         });
20558                         
20559         
20560         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
20561         
20562         this.setStartDate(this.startDate);
20563         this.setEndDate(this.endDate);
20564         
20565         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
20566         
20567         this.fillDow();
20568         this.fillMonths();
20569         this.update();
20570         this.showMode();
20571         
20572         if(this.isInline) {
20573             this.showPopup();
20574         }
20575     },
20576     
20577     picker : function()
20578     {
20579         return this.pickerEl;
20580 //        return this.el.select('.datepicker', true).first();
20581     },
20582     
20583     fillDow: function()
20584     {
20585         var dowCnt = this.weekStart;
20586         
20587         var dow = {
20588             tag: 'tr',
20589             cn: [
20590                 
20591             ]
20592         };
20593         
20594         if(this.calendarWeeks){
20595             dow.cn.push({
20596                 tag: 'th',
20597                 cls: 'cw',
20598                 html: '&nbsp;'
20599             })
20600         }
20601         
20602         while (dowCnt < this.weekStart + 7) {
20603             dow.cn.push({
20604                 tag: 'th',
20605                 cls: 'dow',
20606                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
20607             });
20608         }
20609         
20610         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
20611     },
20612     
20613     fillMonths: function()
20614     {    
20615         var i = 0;
20616         var months = this.picker().select('>.datepicker-months td', true).first();
20617         
20618         months.dom.innerHTML = '';
20619         
20620         while (i < 12) {
20621             var month = {
20622                 tag: 'span',
20623                 cls: 'month',
20624                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
20625             };
20626             
20627             months.createChild(month);
20628         }
20629         
20630     },
20631     
20632     update: function()
20633     {
20634         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;
20635         
20636         if (this.date < this.startDate) {
20637             this.viewDate = new Date(this.startDate);
20638         } else if (this.date > this.endDate) {
20639             this.viewDate = new Date(this.endDate);
20640         } else {
20641             this.viewDate = new Date(this.date);
20642         }
20643         
20644         this.fill();
20645     },
20646     
20647     fill: function() 
20648     {
20649         var d = new Date(this.viewDate),
20650                 year = d.getUTCFullYear(),
20651                 month = d.getUTCMonth(),
20652                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
20653                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
20654                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
20655                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
20656                 currentDate = this.date && this.date.valueOf(),
20657                 today = this.UTCToday();
20658         
20659         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
20660         
20661 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20662         
20663 //        this.picker.select('>tfoot th.today').
20664 //                                              .text(dates[this.language].today)
20665 //                                              .toggle(this.todayBtn !== false);
20666     
20667         this.updateNavArrows();
20668         this.fillMonths();
20669                                                 
20670         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
20671         
20672         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
20673          
20674         prevMonth.setUTCDate(day);
20675         
20676         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
20677         
20678         var nextMonth = new Date(prevMonth);
20679         
20680         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
20681         
20682         nextMonth = nextMonth.valueOf();
20683         
20684         var fillMonths = false;
20685         
20686         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
20687         
20688         while(prevMonth.valueOf() <= nextMonth) {
20689             var clsName = '';
20690             
20691             if (prevMonth.getUTCDay() === this.weekStart) {
20692                 if(fillMonths){
20693                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
20694                 }
20695                     
20696                 fillMonths = {
20697                     tag: 'tr',
20698                     cn: []
20699                 };
20700                 
20701                 if(this.calendarWeeks){
20702                     // ISO 8601: First week contains first thursday.
20703                     // ISO also states week starts on Monday, but we can be more abstract here.
20704                     var
20705                     // Start of current week: based on weekstart/current date
20706                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
20707                     // Thursday of this week
20708                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
20709                     // First Thursday of year, year from thursday
20710                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
20711                     // Calendar week: ms between thursdays, div ms per day, div 7 days
20712                     calWeek =  (th - yth) / 864e5 / 7 + 1;
20713                     
20714                     fillMonths.cn.push({
20715                         tag: 'td',
20716                         cls: 'cw',
20717                         html: calWeek
20718                     });
20719                 }
20720             }
20721             
20722             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
20723                 clsName += ' old';
20724             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
20725                 clsName += ' new';
20726             }
20727             if (this.todayHighlight &&
20728                 prevMonth.getUTCFullYear() == today.getFullYear() &&
20729                 prevMonth.getUTCMonth() == today.getMonth() &&
20730                 prevMonth.getUTCDate() == today.getDate()) {
20731                 clsName += ' today';
20732             }
20733             
20734             if (currentDate && prevMonth.valueOf() === currentDate) {
20735                 clsName += ' active';
20736             }
20737             
20738             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
20739                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
20740                     clsName += ' disabled';
20741             }
20742             
20743             fillMonths.cn.push({
20744                 tag: 'td',
20745                 cls: 'day ' + clsName,
20746                 html: prevMonth.getDate()
20747             });
20748             
20749             prevMonth.setDate(prevMonth.getDate()+1);
20750         }
20751           
20752         var currentYear = this.date && this.date.getUTCFullYear();
20753         var currentMonth = this.date && this.date.getUTCMonth();
20754         
20755         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
20756         
20757         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
20758             v.removeClass('active');
20759             
20760             if(currentYear === year && k === currentMonth){
20761                 v.addClass('active');
20762             }
20763             
20764             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
20765                 v.addClass('disabled');
20766             }
20767             
20768         });
20769         
20770         
20771         year = parseInt(year/10, 10) * 10;
20772         
20773         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
20774         
20775         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
20776         
20777         year -= 1;
20778         for (var i = -1; i < 11; i++) {
20779             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
20780                 tag: 'span',
20781                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
20782                 html: year
20783             });
20784             
20785             year += 1;
20786         }
20787     },
20788     
20789     showMode: function(dir) 
20790     {
20791         if (dir) {
20792             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
20793         }
20794         
20795         Roo.each(this.picker().select('>div',true).elements, function(v){
20796             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20797             v.hide();
20798         });
20799         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
20800     },
20801     
20802     place: function()
20803     {
20804         if(this.isInline) {
20805             return;
20806         }
20807         
20808         this.picker().removeClass(['bottom', 'top']);
20809         
20810         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20811             /*
20812              * place to the top of element!
20813              *
20814              */
20815             
20816             this.picker().addClass('top');
20817             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20818             
20819             return;
20820         }
20821         
20822         this.picker().addClass('bottom');
20823         
20824         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20825     },
20826     
20827     parseDate : function(value)
20828     {
20829         if(!value || value instanceof Date){
20830             return value;
20831         }
20832         var v = Date.parseDate(value, this.format);
20833         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
20834             v = Date.parseDate(value, 'Y-m-d');
20835         }
20836         if(!v && this.altFormats){
20837             if(!this.altFormatsArray){
20838                 this.altFormatsArray = this.altFormats.split("|");
20839             }
20840             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
20841                 v = Date.parseDate(value, this.altFormatsArray[i]);
20842             }
20843         }
20844         return v;
20845     },
20846     
20847     formatDate : function(date, fmt)
20848     {   
20849         return (!date || !(date instanceof Date)) ?
20850         date : date.dateFormat(fmt || this.format);
20851     },
20852     
20853     onFocus : function()
20854     {
20855         Roo.bootstrap.DateField.superclass.onFocus.call(this);
20856         this.showPopup();
20857     },
20858     
20859     onBlur : function()
20860     {
20861         Roo.bootstrap.DateField.superclass.onBlur.call(this);
20862         
20863         var d = this.inputEl().getValue();
20864         
20865         this.setValue(d);
20866                 
20867         this.hidePopup();
20868     },
20869     
20870     showPopup : function()
20871     {
20872         this.picker().show();
20873         this.update();
20874         this.place();
20875         
20876         this.fireEvent('showpopup', this, this.date);
20877     },
20878     
20879     hidePopup : function()
20880     {
20881         if(this.isInline) {
20882             return;
20883         }
20884         this.picker().hide();
20885         this.viewMode = this.startViewMode;
20886         this.showMode();
20887         
20888         this.fireEvent('hidepopup', this, this.date);
20889         
20890     },
20891     
20892     onMousedown: function(e)
20893     {
20894         e.stopPropagation();
20895         e.preventDefault();
20896     },
20897     
20898     keyup: function(e)
20899     {
20900         Roo.bootstrap.DateField.superclass.keyup.call(this);
20901         this.update();
20902     },
20903
20904     setValue: function(v)
20905     {
20906         if(this.fireEvent('beforeselect', this, v) !== false){
20907             var d = new Date(this.parseDate(v) ).clearTime();
20908         
20909             if(isNaN(d.getTime())){
20910                 this.date = this.viewDate = '';
20911                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
20912                 return;
20913             }
20914
20915             v = this.formatDate(d);
20916
20917             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
20918
20919             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
20920
20921             this.update();
20922
20923             this.fireEvent('select', this, this.date);
20924         }
20925     },
20926     
20927     getValue: function()
20928     {
20929         return this.formatDate(this.date);
20930     },
20931     
20932     fireKey: function(e)
20933     {
20934         if (!this.picker().isVisible()){
20935             if (e.keyCode == 27) { // allow escape to hide and re-show picker
20936                 this.showPopup();
20937             }
20938             return;
20939         }
20940         
20941         var dateChanged = false,
20942         dir, day, month,
20943         newDate, newViewDate;
20944         
20945         switch(e.keyCode){
20946             case 27: // escape
20947                 this.hidePopup();
20948                 e.preventDefault();
20949                 break;
20950             case 37: // left
20951             case 39: // right
20952                 if (!this.keyboardNavigation) {
20953                     break;
20954                 }
20955                 dir = e.keyCode == 37 ? -1 : 1;
20956                 
20957                 if (e.ctrlKey){
20958                     newDate = this.moveYear(this.date, dir);
20959                     newViewDate = this.moveYear(this.viewDate, dir);
20960                 } else if (e.shiftKey){
20961                     newDate = this.moveMonth(this.date, dir);
20962                     newViewDate = this.moveMonth(this.viewDate, dir);
20963                 } else {
20964                     newDate = new Date(this.date);
20965                     newDate.setUTCDate(this.date.getUTCDate() + dir);
20966                     newViewDate = new Date(this.viewDate);
20967                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
20968                 }
20969                 if (this.dateWithinRange(newDate)){
20970                     this.date = newDate;
20971                     this.viewDate = newViewDate;
20972                     this.setValue(this.formatDate(this.date));
20973 //                    this.update();
20974                     e.preventDefault();
20975                     dateChanged = true;
20976                 }
20977                 break;
20978             case 38: // up
20979             case 40: // down
20980                 if (!this.keyboardNavigation) {
20981                     break;
20982                 }
20983                 dir = e.keyCode == 38 ? -1 : 1;
20984                 if (e.ctrlKey){
20985                     newDate = this.moveYear(this.date, dir);
20986                     newViewDate = this.moveYear(this.viewDate, dir);
20987                 } else if (e.shiftKey){
20988                     newDate = this.moveMonth(this.date, dir);
20989                     newViewDate = this.moveMonth(this.viewDate, dir);
20990                 } else {
20991                     newDate = new Date(this.date);
20992                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
20993                     newViewDate = new Date(this.viewDate);
20994                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
20995                 }
20996                 if (this.dateWithinRange(newDate)){
20997                     this.date = newDate;
20998                     this.viewDate = newViewDate;
20999                     this.setValue(this.formatDate(this.date));
21000 //                    this.update();
21001                     e.preventDefault();
21002                     dateChanged = true;
21003                 }
21004                 break;
21005             case 13: // enter
21006                 this.setValue(this.formatDate(this.date));
21007                 this.hidePopup();
21008                 e.preventDefault();
21009                 break;
21010             case 9: // tab
21011                 this.setValue(this.formatDate(this.date));
21012                 this.hidePopup();
21013                 break;
21014             case 16: // shift
21015             case 17: // ctrl
21016             case 18: // alt
21017                 break;
21018             default :
21019                 this.hidePopup();
21020                 
21021         }
21022     },
21023     
21024     
21025     onClick: function(e) 
21026     {
21027         e.stopPropagation();
21028         e.preventDefault();
21029         
21030         var target = e.getTarget();
21031         
21032         if(target.nodeName.toLowerCase() === 'i'){
21033             target = Roo.get(target).dom.parentNode;
21034         }
21035         
21036         var nodeName = target.nodeName;
21037         var className = target.className;
21038         var html = target.innerHTML;
21039         //Roo.log(nodeName);
21040         
21041         switch(nodeName.toLowerCase()) {
21042             case 'th':
21043                 switch(className) {
21044                     case 'switch':
21045                         this.showMode(1);
21046                         break;
21047                     case 'prev':
21048                     case 'next':
21049                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21050                         switch(this.viewMode){
21051                                 case 0:
21052                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21053                                         break;
21054                                 case 1:
21055                                 case 2:
21056                                         this.viewDate = this.moveYear(this.viewDate, dir);
21057                                         break;
21058                         }
21059                         this.fill();
21060                         break;
21061                     case 'today':
21062                         var date = new Date();
21063                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21064 //                        this.fill()
21065                         this.setValue(this.formatDate(this.date));
21066                         
21067                         this.hidePopup();
21068                         break;
21069                 }
21070                 break;
21071             case 'span':
21072                 if (className.indexOf('disabled') < 0) {
21073                     this.viewDate.setUTCDate(1);
21074                     if (className.indexOf('month') > -1) {
21075                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21076                     } else {
21077                         var year = parseInt(html, 10) || 0;
21078                         this.viewDate.setUTCFullYear(year);
21079                         
21080                     }
21081                     
21082                     if(this.singleMode){
21083                         this.setValue(this.formatDate(this.viewDate));
21084                         this.hidePopup();
21085                         return;
21086                     }
21087                     
21088                     this.showMode(-1);
21089                     this.fill();
21090                 }
21091                 break;
21092                 
21093             case 'td':
21094                 //Roo.log(className);
21095                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21096                     var day = parseInt(html, 10) || 1;
21097                     var year = this.viewDate.getUTCFullYear(),
21098                         month = this.viewDate.getUTCMonth();
21099
21100                     if (className.indexOf('old') > -1) {
21101                         if(month === 0 ){
21102                             month = 11;
21103                             year -= 1;
21104                         }else{
21105                             month -= 1;
21106                         }
21107                     } else if (className.indexOf('new') > -1) {
21108                         if (month == 11) {
21109                             month = 0;
21110                             year += 1;
21111                         } else {
21112                             month += 1;
21113                         }
21114                     }
21115                     //Roo.log([year,month,day]);
21116                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21117                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21118 //                    this.fill();
21119                     //Roo.log(this.formatDate(this.date));
21120                     this.setValue(this.formatDate(this.date));
21121                     this.hidePopup();
21122                 }
21123                 break;
21124         }
21125     },
21126     
21127     setStartDate: function(startDate)
21128     {
21129         this.startDate = startDate || -Infinity;
21130         if (this.startDate !== -Infinity) {
21131             this.startDate = this.parseDate(this.startDate);
21132         }
21133         this.update();
21134         this.updateNavArrows();
21135     },
21136
21137     setEndDate: function(endDate)
21138     {
21139         this.endDate = endDate || Infinity;
21140         if (this.endDate !== Infinity) {
21141             this.endDate = this.parseDate(this.endDate);
21142         }
21143         this.update();
21144         this.updateNavArrows();
21145     },
21146     
21147     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21148     {
21149         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21150         if (typeof(this.daysOfWeekDisabled) !== 'object') {
21151             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21152         }
21153         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21154             return parseInt(d, 10);
21155         });
21156         this.update();
21157         this.updateNavArrows();
21158     },
21159     
21160     updateNavArrows: function() 
21161     {
21162         if(this.singleMode){
21163             return;
21164         }
21165         
21166         var d = new Date(this.viewDate),
21167         year = d.getUTCFullYear(),
21168         month = d.getUTCMonth();
21169         
21170         Roo.each(this.picker().select('.prev', true).elements, function(v){
21171             v.show();
21172             switch (this.viewMode) {
21173                 case 0:
21174
21175                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21176                         v.hide();
21177                     }
21178                     break;
21179                 case 1:
21180                 case 2:
21181                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21182                         v.hide();
21183                     }
21184                     break;
21185             }
21186         });
21187         
21188         Roo.each(this.picker().select('.next', true).elements, function(v){
21189             v.show();
21190             switch (this.viewMode) {
21191                 case 0:
21192
21193                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21194                         v.hide();
21195                     }
21196                     break;
21197                 case 1:
21198                 case 2:
21199                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21200                         v.hide();
21201                     }
21202                     break;
21203             }
21204         })
21205     },
21206     
21207     moveMonth: function(date, dir)
21208     {
21209         if (!dir) {
21210             return date;
21211         }
21212         var new_date = new Date(date.valueOf()),
21213         day = new_date.getUTCDate(),
21214         month = new_date.getUTCMonth(),
21215         mag = Math.abs(dir),
21216         new_month, test;
21217         dir = dir > 0 ? 1 : -1;
21218         if (mag == 1){
21219             test = dir == -1
21220             // If going back one month, make sure month is not current month
21221             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21222             ? function(){
21223                 return new_date.getUTCMonth() == month;
21224             }
21225             // If going forward one month, make sure month is as expected
21226             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21227             : function(){
21228                 return new_date.getUTCMonth() != new_month;
21229             };
21230             new_month = month + dir;
21231             new_date.setUTCMonth(new_month);
21232             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21233             if (new_month < 0 || new_month > 11) {
21234                 new_month = (new_month + 12) % 12;
21235             }
21236         } else {
21237             // For magnitudes >1, move one month at a time...
21238             for (var i=0; i<mag; i++) {
21239                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21240                 new_date = this.moveMonth(new_date, dir);
21241             }
21242             // ...then reset the day, keeping it in the new month
21243             new_month = new_date.getUTCMonth();
21244             new_date.setUTCDate(day);
21245             test = function(){
21246                 return new_month != new_date.getUTCMonth();
21247             };
21248         }
21249         // Common date-resetting loop -- if date is beyond end of month, make it
21250         // end of month
21251         while (test()){
21252             new_date.setUTCDate(--day);
21253             new_date.setUTCMonth(new_month);
21254         }
21255         return new_date;
21256     },
21257
21258     moveYear: function(date, dir)
21259     {
21260         return this.moveMonth(date, dir*12);
21261     },
21262
21263     dateWithinRange: function(date)
21264     {
21265         return date >= this.startDate && date <= this.endDate;
21266     },
21267
21268     
21269     remove: function() 
21270     {
21271         this.picker().remove();
21272     },
21273     
21274     validateValue : function(value)
21275     {
21276         if(this.getVisibilityEl().hasClass('hidden')){
21277             return true;
21278         }
21279         
21280         if(value.length < 1)  {
21281             if(this.allowBlank){
21282                 return true;
21283             }
21284             return false;
21285         }
21286         
21287         if(value.length < this.minLength){
21288             return false;
21289         }
21290         if(value.length > this.maxLength){
21291             return false;
21292         }
21293         if(this.vtype){
21294             var vt = Roo.form.VTypes;
21295             if(!vt[this.vtype](value, this)){
21296                 return false;
21297             }
21298         }
21299         if(typeof this.validator == "function"){
21300             var msg = this.validator(value);
21301             if(msg !== true){
21302                 return false;
21303             }
21304         }
21305         
21306         if(this.regex && !this.regex.test(value)){
21307             return false;
21308         }
21309         
21310         if(typeof(this.parseDate(value)) == 'undefined'){
21311             return false;
21312         }
21313         
21314         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21315             return false;
21316         }      
21317         
21318         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21319             return false;
21320         } 
21321         
21322         
21323         return true;
21324     },
21325     
21326     reset : function()
21327     {
21328         this.date = this.viewDate = '';
21329         
21330         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21331     }
21332    
21333 });
21334
21335 Roo.apply(Roo.bootstrap.DateField,  {
21336     
21337     head : {
21338         tag: 'thead',
21339         cn: [
21340         {
21341             tag: 'tr',
21342             cn: [
21343             {
21344                 tag: 'th',
21345                 cls: 'prev',
21346                 html: '<i class="fa fa-arrow-left"/>'
21347             },
21348             {
21349                 tag: 'th',
21350                 cls: 'switch',
21351                 colspan: '5'
21352             },
21353             {
21354                 tag: 'th',
21355                 cls: 'next',
21356                 html: '<i class="fa fa-arrow-right"/>'
21357             }
21358
21359             ]
21360         }
21361         ]
21362     },
21363     
21364     content : {
21365         tag: 'tbody',
21366         cn: [
21367         {
21368             tag: 'tr',
21369             cn: [
21370             {
21371                 tag: 'td',
21372                 colspan: '7'
21373             }
21374             ]
21375         }
21376         ]
21377     },
21378     
21379     footer : {
21380         tag: 'tfoot',
21381         cn: [
21382         {
21383             tag: 'tr',
21384             cn: [
21385             {
21386                 tag: 'th',
21387                 colspan: '7',
21388                 cls: 'today'
21389             }
21390                     
21391             ]
21392         }
21393         ]
21394     },
21395     
21396     dates:{
21397         en: {
21398             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21399             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21400             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21401             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21402             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21403             today: "Today"
21404         }
21405     },
21406     
21407     modes: [
21408     {
21409         clsName: 'days',
21410         navFnc: 'Month',
21411         navStep: 1
21412     },
21413     {
21414         clsName: 'months',
21415         navFnc: 'FullYear',
21416         navStep: 1
21417     },
21418     {
21419         clsName: 'years',
21420         navFnc: 'FullYear',
21421         navStep: 10
21422     }]
21423 });
21424
21425 Roo.apply(Roo.bootstrap.DateField,  {
21426   
21427     template : {
21428         tag: 'div',
21429         cls: 'datepicker dropdown-menu roo-dynamic',
21430         cn: [
21431         {
21432             tag: 'div',
21433             cls: 'datepicker-days',
21434             cn: [
21435             {
21436                 tag: 'table',
21437                 cls: 'table-condensed',
21438                 cn:[
21439                 Roo.bootstrap.DateField.head,
21440                 {
21441                     tag: 'tbody'
21442                 },
21443                 Roo.bootstrap.DateField.footer
21444                 ]
21445             }
21446             ]
21447         },
21448         {
21449             tag: 'div',
21450             cls: 'datepicker-months',
21451             cn: [
21452             {
21453                 tag: 'table',
21454                 cls: 'table-condensed',
21455                 cn:[
21456                 Roo.bootstrap.DateField.head,
21457                 Roo.bootstrap.DateField.content,
21458                 Roo.bootstrap.DateField.footer
21459                 ]
21460             }
21461             ]
21462         },
21463         {
21464             tag: 'div',
21465             cls: 'datepicker-years',
21466             cn: [
21467             {
21468                 tag: 'table',
21469                 cls: 'table-condensed',
21470                 cn:[
21471                 Roo.bootstrap.DateField.head,
21472                 Roo.bootstrap.DateField.content,
21473                 Roo.bootstrap.DateField.footer
21474                 ]
21475             }
21476             ]
21477         }
21478         ]
21479     }
21480 });
21481
21482  
21483
21484  /*
21485  * - LGPL
21486  *
21487  * TimeField
21488  * 
21489  */
21490
21491 /**
21492  * @class Roo.bootstrap.TimeField
21493  * @extends Roo.bootstrap.Input
21494  * Bootstrap DateField class
21495  * 
21496  * 
21497  * @constructor
21498  * Create a new TimeField
21499  * @param {Object} config The config object
21500  */
21501
21502 Roo.bootstrap.TimeField = function(config){
21503     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
21504     this.addEvents({
21505             /**
21506              * @event show
21507              * Fires when this field show.
21508              * @param {Roo.bootstrap.DateField} thisthis
21509              * @param {Mixed} date The date value
21510              */
21511             show : true,
21512             /**
21513              * @event show
21514              * Fires when this field hide.
21515              * @param {Roo.bootstrap.DateField} this
21516              * @param {Mixed} date The date value
21517              */
21518             hide : true,
21519             /**
21520              * @event select
21521              * Fires when select a date.
21522              * @param {Roo.bootstrap.DateField} this
21523              * @param {Mixed} date The date value
21524              */
21525             select : true
21526         });
21527 };
21528
21529 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
21530     
21531     /**
21532      * @cfg {String} format
21533      * The default time format string which can be overriden for localization support.  The format must be
21534      * valid according to {@link Date#parseDate} (defaults to 'H:i').
21535      */
21536     format : "H:i",
21537        
21538     onRender: function(ct, position)
21539     {
21540         
21541         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
21542                 
21543         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
21544         
21545         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21546         
21547         this.pop = this.picker().select('>.datepicker-time',true).first();
21548         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21549         
21550         this.picker().on('mousedown', this.onMousedown, this);
21551         this.picker().on('click', this.onClick, this);
21552         
21553         this.picker().addClass('datepicker-dropdown');
21554     
21555         this.fillTime();
21556         this.update();
21557             
21558         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
21559         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
21560         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
21561         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
21562         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
21563         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
21564
21565     },
21566     
21567     fireKey: function(e){
21568         if (!this.picker().isVisible()){
21569             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21570                 this.show();
21571             }
21572             return;
21573         }
21574
21575         e.preventDefault();
21576         
21577         switch(e.keyCode){
21578             case 27: // escape
21579                 this.hide();
21580                 break;
21581             case 37: // left
21582             case 39: // right
21583                 this.onTogglePeriod();
21584                 break;
21585             case 38: // up
21586                 this.onIncrementMinutes();
21587                 break;
21588             case 40: // down
21589                 this.onDecrementMinutes();
21590                 break;
21591             case 13: // enter
21592             case 9: // tab
21593                 this.setTime();
21594                 break;
21595         }
21596     },
21597     
21598     onClick: function(e) {
21599         e.stopPropagation();
21600         e.preventDefault();
21601     },
21602     
21603     picker : function()
21604     {
21605         return this.el.select('.datepicker', true).first();
21606     },
21607     
21608     fillTime: function()
21609     {    
21610         var time = this.pop.select('tbody', true).first();
21611         
21612         time.dom.innerHTML = '';
21613         
21614         time.createChild({
21615             tag: 'tr',
21616             cn: [
21617                 {
21618                     tag: 'td',
21619                     cn: [
21620                         {
21621                             tag: 'a',
21622                             href: '#',
21623                             cls: 'btn',
21624                             cn: [
21625                                 {
21626                                     tag: 'span',
21627                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
21628                                 }
21629                             ]
21630                         } 
21631                     ]
21632                 },
21633                 {
21634                     tag: 'td',
21635                     cls: 'separator'
21636                 },
21637                 {
21638                     tag: 'td',
21639                     cn: [
21640                         {
21641                             tag: 'a',
21642                             href: '#',
21643                             cls: 'btn',
21644                             cn: [
21645                                 {
21646                                     tag: 'span',
21647                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
21648                                 }
21649                             ]
21650                         }
21651                     ]
21652                 },
21653                 {
21654                     tag: 'td',
21655                     cls: 'separator'
21656                 }
21657             ]
21658         });
21659         
21660         time.createChild({
21661             tag: 'tr',
21662             cn: [
21663                 {
21664                     tag: 'td',
21665                     cn: [
21666                         {
21667                             tag: 'span',
21668                             cls: 'timepicker-hour',
21669                             html: '00'
21670                         }  
21671                     ]
21672                 },
21673                 {
21674                     tag: 'td',
21675                     cls: 'separator',
21676                     html: ':'
21677                 },
21678                 {
21679                     tag: 'td',
21680                     cn: [
21681                         {
21682                             tag: 'span',
21683                             cls: 'timepicker-minute',
21684                             html: '00'
21685                         }  
21686                     ]
21687                 },
21688                 {
21689                     tag: 'td',
21690                     cls: 'separator'
21691                 },
21692                 {
21693                     tag: 'td',
21694                     cn: [
21695                         {
21696                             tag: 'button',
21697                             type: 'button',
21698                             cls: 'btn btn-primary period',
21699                             html: 'AM'
21700                             
21701                         }
21702                     ]
21703                 }
21704             ]
21705         });
21706         
21707         time.createChild({
21708             tag: 'tr',
21709             cn: [
21710                 {
21711                     tag: 'td',
21712                     cn: [
21713                         {
21714                             tag: 'a',
21715                             href: '#',
21716                             cls: 'btn',
21717                             cn: [
21718                                 {
21719                                     tag: 'span',
21720                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
21721                                 }
21722                             ]
21723                         }
21724                     ]
21725                 },
21726                 {
21727                     tag: 'td',
21728                     cls: 'separator'
21729                 },
21730                 {
21731                     tag: 'td',
21732                     cn: [
21733                         {
21734                             tag: 'a',
21735                             href: '#',
21736                             cls: 'btn',
21737                             cn: [
21738                                 {
21739                                     tag: 'span',
21740                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
21741                                 }
21742                             ]
21743                         }
21744                     ]
21745                 },
21746                 {
21747                     tag: 'td',
21748                     cls: 'separator'
21749                 }
21750             ]
21751         });
21752         
21753     },
21754     
21755     update: function()
21756     {
21757         
21758         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
21759         
21760         this.fill();
21761     },
21762     
21763     fill: function() 
21764     {
21765         var hours = this.time.getHours();
21766         var minutes = this.time.getMinutes();
21767         var period = 'AM';
21768         
21769         if(hours > 11){
21770             period = 'PM';
21771         }
21772         
21773         if(hours == 0){
21774             hours = 12;
21775         }
21776         
21777         
21778         if(hours > 12){
21779             hours = hours - 12;
21780         }
21781         
21782         if(hours < 10){
21783             hours = '0' + hours;
21784         }
21785         
21786         if(minutes < 10){
21787             minutes = '0' + minutes;
21788         }
21789         
21790         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
21791         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
21792         this.pop.select('button', true).first().dom.innerHTML = period;
21793         
21794     },
21795     
21796     place: function()
21797     {   
21798         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
21799         
21800         var cls = ['bottom'];
21801         
21802         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
21803             cls.pop();
21804             cls.push('top');
21805         }
21806         
21807         cls.push('right');
21808         
21809         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
21810             cls.pop();
21811             cls.push('left');
21812         }
21813         
21814         this.picker().addClass(cls.join('-'));
21815         
21816         var _this = this;
21817         
21818         Roo.each(cls, function(c){
21819             if(c == 'bottom'){
21820                 _this.picker().setTop(_this.inputEl().getHeight());
21821                 return;
21822             }
21823             if(c == 'top'){
21824                 _this.picker().setTop(0 - _this.picker().getHeight());
21825                 return;
21826             }
21827             
21828             if(c == 'left'){
21829                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
21830                 return;
21831             }
21832             if(c == 'right'){
21833                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
21834                 return;
21835             }
21836         });
21837         
21838     },
21839   
21840     onFocus : function()
21841     {
21842         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
21843         this.show();
21844     },
21845     
21846     onBlur : function()
21847     {
21848         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
21849         this.hide();
21850     },
21851     
21852     show : function()
21853     {
21854         this.picker().show();
21855         this.pop.show();
21856         this.update();
21857         this.place();
21858         
21859         this.fireEvent('show', this, this.date);
21860     },
21861     
21862     hide : function()
21863     {
21864         this.picker().hide();
21865         this.pop.hide();
21866         
21867         this.fireEvent('hide', this, this.date);
21868     },
21869     
21870     setTime : function()
21871     {
21872         this.hide();
21873         this.setValue(this.time.format(this.format));
21874         
21875         this.fireEvent('select', this, this.date);
21876         
21877         
21878     },
21879     
21880     onMousedown: function(e){
21881         e.stopPropagation();
21882         e.preventDefault();
21883     },
21884     
21885     onIncrementHours: function()
21886     {
21887         Roo.log('onIncrementHours');
21888         this.time = this.time.add(Date.HOUR, 1);
21889         this.update();
21890         
21891     },
21892     
21893     onDecrementHours: function()
21894     {
21895         Roo.log('onDecrementHours');
21896         this.time = this.time.add(Date.HOUR, -1);
21897         this.update();
21898     },
21899     
21900     onIncrementMinutes: function()
21901     {
21902         Roo.log('onIncrementMinutes');
21903         this.time = this.time.add(Date.MINUTE, 1);
21904         this.update();
21905     },
21906     
21907     onDecrementMinutes: function()
21908     {
21909         Roo.log('onDecrementMinutes');
21910         this.time = this.time.add(Date.MINUTE, -1);
21911         this.update();
21912     },
21913     
21914     onTogglePeriod: function()
21915     {
21916         Roo.log('onTogglePeriod');
21917         this.time = this.time.add(Date.HOUR, 12);
21918         this.update();
21919     }
21920     
21921    
21922 });
21923
21924 Roo.apply(Roo.bootstrap.TimeField,  {
21925     
21926     content : {
21927         tag: 'tbody',
21928         cn: [
21929             {
21930                 tag: 'tr',
21931                 cn: [
21932                 {
21933                     tag: 'td',
21934                     colspan: '7'
21935                 }
21936                 ]
21937             }
21938         ]
21939     },
21940     
21941     footer : {
21942         tag: 'tfoot',
21943         cn: [
21944             {
21945                 tag: 'tr',
21946                 cn: [
21947                 {
21948                     tag: 'th',
21949                     colspan: '7',
21950                     cls: '',
21951                     cn: [
21952                         {
21953                             tag: 'button',
21954                             cls: 'btn btn-info ok',
21955                             html: 'OK'
21956                         }
21957                     ]
21958                 }
21959
21960                 ]
21961             }
21962         ]
21963     }
21964 });
21965
21966 Roo.apply(Roo.bootstrap.TimeField,  {
21967   
21968     template : {
21969         tag: 'div',
21970         cls: 'datepicker dropdown-menu',
21971         cn: [
21972             {
21973                 tag: 'div',
21974                 cls: 'datepicker-time',
21975                 cn: [
21976                 {
21977                     tag: 'table',
21978                     cls: 'table-condensed',
21979                     cn:[
21980                     Roo.bootstrap.TimeField.content,
21981                     Roo.bootstrap.TimeField.footer
21982                     ]
21983                 }
21984                 ]
21985             }
21986         ]
21987     }
21988 });
21989
21990  
21991
21992  /*
21993  * - LGPL
21994  *
21995  * MonthField
21996  * 
21997  */
21998
21999 /**
22000  * @class Roo.bootstrap.MonthField
22001  * @extends Roo.bootstrap.Input
22002  * Bootstrap MonthField class
22003  * 
22004  * @cfg {String} language default en
22005  * 
22006  * @constructor
22007  * Create a new MonthField
22008  * @param {Object} config The config object
22009  */
22010
22011 Roo.bootstrap.MonthField = function(config){
22012     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22013     
22014     this.addEvents({
22015         /**
22016          * @event show
22017          * Fires when this field show.
22018          * @param {Roo.bootstrap.MonthField} this
22019          * @param {Mixed} date The date value
22020          */
22021         show : true,
22022         /**
22023          * @event show
22024          * Fires when this field hide.
22025          * @param {Roo.bootstrap.MonthField} this
22026          * @param {Mixed} date The date value
22027          */
22028         hide : true,
22029         /**
22030          * @event select
22031          * Fires when select a date.
22032          * @param {Roo.bootstrap.MonthField} this
22033          * @param {String} oldvalue The old value
22034          * @param {String} newvalue The new value
22035          */
22036         select : true
22037     });
22038 };
22039
22040 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22041     
22042     onRender: function(ct, position)
22043     {
22044         
22045         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22046         
22047         this.language = this.language || 'en';
22048         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22049         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22050         
22051         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22052         this.isInline = false;
22053         this.isInput = true;
22054         this.component = this.el.select('.add-on', true).first() || false;
22055         this.component = (this.component && this.component.length === 0) ? false : this.component;
22056         this.hasInput = this.component && this.inputEL().length;
22057         
22058         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22059         
22060         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22061         
22062         this.picker().on('mousedown', this.onMousedown, this);
22063         this.picker().on('click', this.onClick, this);
22064         
22065         this.picker().addClass('datepicker-dropdown');
22066         
22067         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22068             v.setStyle('width', '189px');
22069         });
22070         
22071         this.fillMonths();
22072         
22073         this.update();
22074         
22075         if(this.isInline) {
22076             this.show();
22077         }
22078         
22079     },
22080     
22081     setValue: function(v, suppressEvent)
22082     {   
22083         var o = this.getValue();
22084         
22085         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22086         
22087         this.update();
22088
22089         if(suppressEvent !== true){
22090             this.fireEvent('select', this, o, v);
22091         }
22092         
22093     },
22094     
22095     getValue: function()
22096     {
22097         return this.value;
22098     },
22099     
22100     onClick: function(e) 
22101     {
22102         e.stopPropagation();
22103         e.preventDefault();
22104         
22105         var target = e.getTarget();
22106         
22107         if(target.nodeName.toLowerCase() === 'i'){
22108             target = Roo.get(target).dom.parentNode;
22109         }
22110         
22111         var nodeName = target.nodeName;
22112         var className = target.className;
22113         var html = target.innerHTML;
22114         
22115         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22116             return;
22117         }
22118         
22119         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22120         
22121         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22122         
22123         this.hide();
22124                         
22125     },
22126     
22127     picker : function()
22128     {
22129         return this.pickerEl;
22130     },
22131     
22132     fillMonths: function()
22133     {    
22134         var i = 0;
22135         var months = this.picker().select('>.datepicker-months td', true).first();
22136         
22137         months.dom.innerHTML = '';
22138         
22139         while (i < 12) {
22140             var month = {
22141                 tag: 'span',
22142                 cls: 'month',
22143                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22144             };
22145             
22146             months.createChild(month);
22147         }
22148         
22149     },
22150     
22151     update: function()
22152     {
22153         var _this = this;
22154         
22155         if(typeof(this.vIndex) == 'undefined' && this.value.length){
22156             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22157         }
22158         
22159         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22160             e.removeClass('active');
22161             
22162             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22163                 e.addClass('active');
22164             }
22165         })
22166     },
22167     
22168     place: function()
22169     {
22170         if(this.isInline) {
22171             return;
22172         }
22173         
22174         this.picker().removeClass(['bottom', 'top']);
22175         
22176         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22177             /*
22178              * place to the top of element!
22179              *
22180              */
22181             
22182             this.picker().addClass('top');
22183             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22184             
22185             return;
22186         }
22187         
22188         this.picker().addClass('bottom');
22189         
22190         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22191     },
22192     
22193     onFocus : function()
22194     {
22195         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22196         this.show();
22197     },
22198     
22199     onBlur : function()
22200     {
22201         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22202         
22203         var d = this.inputEl().getValue();
22204         
22205         this.setValue(d);
22206                 
22207         this.hide();
22208     },
22209     
22210     show : function()
22211     {
22212         this.picker().show();
22213         this.picker().select('>.datepicker-months', true).first().show();
22214         this.update();
22215         this.place();
22216         
22217         this.fireEvent('show', this, this.date);
22218     },
22219     
22220     hide : function()
22221     {
22222         if(this.isInline) {
22223             return;
22224         }
22225         this.picker().hide();
22226         this.fireEvent('hide', this, this.date);
22227         
22228     },
22229     
22230     onMousedown: function(e)
22231     {
22232         e.stopPropagation();
22233         e.preventDefault();
22234     },
22235     
22236     keyup: function(e)
22237     {
22238         Roo.bootstrap.MonthField.superclass.keyup.call(this);
22239         this.update();
22240     },
22241
22242     fireKey: function(e)
22243     {
22244         if (!this.picker().isVisible()){
22245             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
22246                 this.show();
22247             }
22248             return;
22249         }
22250         
22251         var dir;
22252         
22253         switch(e.keyCode){
22254             case 27: // escape
22255                 this.hide();
22256                 e.preventDefault();
22257                 break;
22258             case 37: // left
22259             case 39: // right
22260                 dir = e.keyCode == 37 ? -1 : 1;
22261                 
22262                 this.vIndex = this.vIndex + dir;
22263                 
22264                 if(this.vIndex < 0){
22265                     this.vIndex = 0;
22266                 }
22267                 
22268                 if(this.vIndex > 11){
22269                     this.vIndex = 11;
22270                 }
22271                 
22272                 if(isNaN(this.vIndex)){
22273                     this.vIndex = 0;
22274                 }
22275                 
22276                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22277                 
22278                 break;
22279             case 38: // up
22280             case 40: // down
22281                 
22282                 dir = e.keyCode == 38 ? -1 : 1;
22283                 
22284                 this.vIndex = this.vIndex + dir * 4;
22285                 
22286                 if(this.vIndex < 0){
22287                     this.vIndex = 0;
22288                 }
22289                 
22290                 if(this.vIndex > 11){
22291                     this.vIndex = 11;
22292                 }
22293                 
22294                 if(isNaN(this.vIndex)){
22295                     this.vIndex = 0;
22296                 }
22297                 
22298                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22299                 break;
22300                 
22301             case 13: // enter
22302                 
22303                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22304                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22305                 }
22306                 
22307                 this.hide();
22308                 e.preventDefault();
22309                 break;
22310             case 9: // tab
22311                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22312                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22313                 }
22314                 this.hide();
22315                 break;
22316             case 16: // shift
22317             case 17: // ctrl
22318             case 18: // alt
22319                 break;
22320             default :
22321                 this.hide();
22322                 
22323         }
22324     },
22325     
22326     remove: function() 
22327     {
22328         this.picker().remove();
22329     }
22330    
22331 });
22332
22333 Roo.apply(Roo.bootstrap.MonthField,  {
22334     
22335     content : {
22336         tag: 'tbody',
22337         cn: [
22338         {
22339             tag: 'tr',
22340             cn: [
22341             {
22342                 tag: 'td',
22343                 colspan: '7'
22344             }
22345             ]
22346         }
22347         ]
22348     },
22349     
22350     dates:{
22351         en: {
22352             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22353             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22354         }
22355     }
22356 });
22357
22358 Roo.apply(Roo.bootstrap.MonthField,  {
22359   
22360     template : {
22361         tag: 'div',
22362         cls: 'datepicker dropdown-menu roo-dynamic',
22363         cn: [
22364             {
22365                 tag: 'div',
22366                 cls: 'datepicker-months',
22367                 cn: [
22368                 {
22369                     tag: 'table',
22370                     cls: 'table-condensed',
22371                     cn:[
22372                         Roo.bootstrap.DateField.content
22373                     ]
22374                 }
22375                 ]
22376             }
22377         ]
22378     }
22379 });
22380
22381  
22382
22383  
22384  /*
22385  * - LGPL
22386  *
22387  * CheckBox
22388  * 
22389  */
22390
22391 /**
22392  * @class Roo.bootstrap.CheckBox
22393  * @extends Roo.bootstrap.Input
22394  * Bootstrap CheckBox class
22395  * 
22396  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22397  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22398  * @cfg {String} boxLabel The text that appears beside the checkbox
22399  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22400  * @cfg {Boolean} checked initnal the element
22401  * @cfg {Boolean} inline inline the element (default false)
22402  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22403  * @cfg {String} tooltip label tooltip
22404  * 
22405  * @constructor
22406  * Create a new CheckBox
22407  * @param {Object} config The config object
22408  */
22409
22410 Roo.bootstrap.CheckBox = function(config){
22411     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22412    
22413     this.addEvents({
22414         /**
22415         * @event check
22416         * Fires when the element is checked or unchecked.
22417         * @param {Roo.bootstrap.CheckBox} this This input
22418         * @param {Boolean} checked The new checked value
22419         */
22420        check : true,
22421        /**
22422         * @event click
22423         * Fires when the element is click.
22424         * @param {Roo.bootstrap.CheckBox} this This input
22425         */
22426        click : true
22427     });
22428     
22429 };
22430
22431 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
22432   
22433     inputType: 'checkbox',
22434     inputValue: 1,
22435     valueOff: 0,
22436     boxLabel: false,
22437     checked: false,
22438     weight : false,
22439     inline: false,
22440     tooltip : '',
22441     
22442     // checkbox success does not make any sense really.. 
22443     invalidClass : "",
22444     validClass : "",
22445     
22446     
22447     getAutoCreate : function()
22448     {
22449         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22450         
22451         var id = Roo.id();
22452         
22453         var cfg = {};
22454         
22455         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22456         
22457         if(this.inline){
22458             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
22459         }
22460         
22461         var input =  {
22462             tag: 'input',
22463             id : id,
22464             type : this.inputType,
22465             value : this.inputValue,
22466             cls : 'roo-' + this.inputType, //'form-box',
22467             placeholder : this.placeholder || ''
22468             
22469         };
22470         
22471         if(this.inputType != 'radio'){
22472             var hidden =  {
22473                 tag: 'input',
22474                 type : 'hidden',
22475                 cls : 'roo-hidden-value',
22476                 value : this.checked ? this.inputValue : this.valueOff
22477             };
22478         }
22479         
22480             
22481         if (this.weight) { // Validity check?
22482             cfg.cls += " " + this.inputType + "-" + this.weight;
22483         }
22484         
22485         if (this.disabled) {
22486             input.disabled=true;
22487         }
22488         
22489         if(this.checked){
22490             input.checked = this.checked;
22491         }
22492         
22493         if (this.name) {
22494             
22495             input.name = this.name;
22496             
22497             if(this.inputType != 'radio'){
22498                 hidden.name = this.name;
22499                 input.name = '_hidden_' + this.name;
22500             }
22501         }
22502         
22503         if (this.size) {
22504             input.cls += ' input-' + this.size;
22505         }
22506         
22507         var settings=this;
22508         
22509         ['xs','sm','md','lg'].map(function(size){
22510             if (settings[size]) {
22511                 cfg.cls += ' col-' + size + '-' + settings[size];
22512             }
22513         });
22514         
22515         var inputblock = input;
22516          
22517         if (this.before || this.after) {
22518             
22519             inputblock = {
22520                 cls : 'input-group',
22521                 cn :  [] 
22522             };
22523             
22524             if (this.before) {
22525                 inputblock.cn.push({
22526                     tag :'span',
22527                     cls : 'input-group-addon',
22528                     html : this.before
22529                 });
22530             }
22531             
22532             inputblock.cn.push(input);
22533             
22534             if(this.inputType != 'radio'){
22535                 inputblock.cn.push(hidden);
22536             }
22537             
22538             if (this.after) {
22539                 inputblock.cn.push({
22540                     tag :'span',
22541                     cls : 'input-group-addon',
22542                     html : this.after
22543                 });
22544             }
22545             
22546         }
22547         var boxLabelCfg = false;
22548         
22549         if(this.boxLabel){
22550            
22551             boxLabelCfg = {
22552                 tag: 'label',
22553                 //'for': id, // box label is handled by onclick - so no for...
22554                 cls: 'box-label',
22555                 html: this.boxLabel
22556             };
22557             if(this.tooltip){
22558                 boxLabelCfg.tooltip = this.tooltip;
22559             }
22560              
22561         }
22562         
22563         
22564         if (align ==='left' && this.fieldLabel.length) {
22565 //                Roo.log("left and has label");
22566             cfg.cn = [
22567                 {
22568                     tag: 'label',
22569                     'for' :  id,
22570                     cls : 'control-label',
22571                     html : this.fieldLabel
22572                 },
22573                 {
22574                     cls : "", 
22575                     cn: [
22576                         inputblock
22577                     ]
22578                 }
22579             ];
22580             
22581             if (boxLabelCfg) {
22582                 cfg.cn[1].cn.push(boxLabelCfg);
22583             }
22584             
22585             if(this.labelWidth > 12){
22586                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
22587             }
22588             
22589             if(this.labelWidth < 13 && this.labelmd == 0){
22590                 this.labelmd = this.labelWidth;
22591             }
22592             
22593             if(this.labellg > 0){
22594                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
22595                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
22596             }
22597             
22598             if(this.labelmd > 0){
22599                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
22600                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
22601             }
22602             
22603             if(this.labelsm > 0){
22604                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
22605                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
22606             }
22607             
22608             if(this.labelxs > 0){
22609                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
22610                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
22611             }
22612             
22613         } else if ( this.fieldLabel.length) {
22614 //                Roo.log(" label");
22615                 cfg.cn = [
22616                    
22617                     {
22618                         tag: this.boxLabel ? 'span' : 'label',
22619                         'for': id,
22620                         cls: 'control-label box-input-label',
22621                         //cls : 'input-group-addon',
22622                         html : this.fieldLabel
22623                     },
22624                     
22625                     inputblock
22626                     
22627                 ];
22628                 if (boxLabelCfg) {
22629                     cfg.cn.push(boxLabelCfg);
22630                 }
22631
22632         } else {
22633             
22634 //                Roo.log(" no label && no align");
22635                 cfg.cn = [  inputblock ] ;
22636                 if (boxLabelCfg) {
22637                     cfg.cn.push(boxLabelCfg);
22638                 }
22639
22640                 
22641         }
22642         
22643        
22644         
22645         if(this.inputType != 'radio'){
22646             cfg.cn.push(hidden);
22647         }
22648         
22649         return cfg;
22650         
22651     },
22652     
22653     /**
22654      * return the real input element.
22655      */
22656     inputEl: function ()
22657     {
22658         return this.el.select('input.roo-' + this.inputType,true).first();
22659     },
22660     hiddenEl: function ()
22661     {
22662         return this.el.select('input.roo-hidden-value',true).first();
22663     },
22664     
22665     labelEl: function()
22666     {
22667         return this.el.select('label.control-label',true).first();
22668     },
22669     /* depricated... */
22670     
22671     label: function()
22672     {
22673         return this.labelEl();
22674     },
22675     
22676     boxLabelEl: function()
22677     {
22678         return this.el.select('label.box-label',true).first();
22679     },
22680     
22681     initEvents : function()
22682     {
22683 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
22684         
22685         this.inputEl().on('click', this.onClick,  this);
22686         
22687         if (this.boxLabel) { 
22688             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
22689         }
22690         
22691         this.startValue = this.getValue();
22692         
22693         if(this.groupId){
22694             Roo.bootstrap.CheckBox.register(this);
22695         }
22696     },
22697     
22698     onClick : function(e)
22699     {   
22700         if(this.fireEvent('click', this, e) !== false){
22701             this.setChecked(!this.checked);
22702         }
22703         
22704     },
22705     
22706     setChecked : function(state,suppressEvent)
22707     {
22708         this.startValue = this.getValue();
22709
22710         if(this.inputType == 'radio'){
22711             
22712             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22713                 e.dom.checked = false;
22714             });
22715             
22716             this.inputEl().dom.checked = true;
22717             
22718             this.inputEl().dom.value = this.inputValue;
22719             
22720             if(suppressEvent !== true){
22721                 this.fireEvent('check', this, true);
22722             }
22723             
22724             this.validate();
22725             
22726             return;
22727         }
22728         
22729         this.checked = state;
22730         
22731         this.inputEl().dom.checked = state;
22732         
22733         
22734         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
22735         
22736         if(suppressEvent !== true){
22737             this.fireEvent('check', this, state);
22738         }
22739         
22740         this.validate();
22741     },
22742     
22743     getValue : function()
22744     {
22745         if(this.inputType == 'radio'){
22746             return this.getGroupValue();
22747         }
22748         
22749         return this.hiddenEl().dom.value;
22750         
22751     },
22752     
22753     getGroupValue : function()
22754     {
22755         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
22756             return '';
22757         }
22758         
22759         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
22760     },
22761     
22762     setValue : function(v,suppressEvent)
22763     {
22764         if(this.inputType == 'radio'){
22765             this.setGroupValue(v, suppressEvent);
22766             return;
22767         }
22768         
22769         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
22770         
22771         this.validate();
22772     },
22773     
22774     setGroupValue : function(v, suppressEvent)
22775     {
22776         this.startValue = this.getValue();
22777         
22778         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22779             e.dom.checked = false;
22780             
22781             if(e.dom.value == v){
22782                 e.dom.checked = true;
22783             }
22784         });
22785         
22786         if(suppressEvent !== true){
22787             this.fireEvent('check', this, true);
22788         }
22789
22790         this.validate();
22791         
22792         return;
22793     },
22794     
22795     validate : function()
22796     {
22797         if(this.getVisibilityEl().hasClass('hidden')){
22798             return true;
22799         }
22800         
22801         if(
22802                 this.disabled || 
22803                 (this.inputType == 'radio' && this.validateRadio()) ||
22804                 (this.inputType == 'checkbox' && this.validateCheckbox())
22805         ){
22806             this.markValid();
22807             return true;
22808         }
22809         
22810         this.markInvalid();
22811         return false;
22812     },
22813     
22814     validateRadio : function()
22815     {
22816         if(this.getVisibilityEl().hasClass('hidden')){
22817             return true;
22818         }
22819         
22820         if(this.allowBlank){
22821             return true;
22822         }
22823         
22824         var valid = false;
22825         
22826         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22827             if(!e.dom.checked){
22828                 return;
22829             }
22830             
22831             valid = true;
22832             
22833             return false;
22834         });
22835         
22836         return valid;
22837     },
22838     
22839     validateCheckbox : function()
22840     {
22841         if(!this.groupId){
22842             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
22843             //return (this.getValue() == this.inputValue) ? true : false;
22844         }
22845         
22846         var group = Roo.bootstrap.CheckBox.get(this.groupId);
22847         
22848         if(!group){
22849             return false;
22850         }
22851         
22852         var r = false;
22853         
22854         for(var i in group){
22855             if(group[i].el.isVisible(true)){
22856                 r = false;
22857                 break;
22858             }
22859             
22860             r = true;
22861         }
22862         
22863         for(var i in group){
22864             if(r){
22865                 break;
22866             }
22867             
22868             r = (group[i].getValue() == group[i].inputValue) ? true : false;
22869         }
22870         
22871         return r;
22872     },
22873     
22874     /**
22875      * Mark this field as valid
22876      */
22877     markValid : function()
22878     {
22879         var _this = this;
22880         
22881         this.fireEvent('valid', this);
22882         
22883         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
22884         
22885         if(this.groupId){
22886             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
22887         }
22888         
22889         if(label){
22890             label.markValid();
22891         }
22892
22893         if(this.inputType == 'radio'){
22894             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22895                 var fg = e.findParent('.form-group', false, true);
22896                 if (Roo.bootstrap.version == 3) {
22897                     fg.removeClass([_this.invalidClass, _this.validClass]);
22898                     fg.addClass(_this.validClass);
22899                 } else {
22900                     fg.removeClass(['is-valid', 'is-invalid']);
22901                     fg.addClass('is-valid');
22902                 }
22903             });
22904             
22905             return;
22906         }
22907
22908         if(!this.groupId){
22909             var fg = this.el.findParent('.form-group', false, true);
22910             if (Roo.bootstrap.version == 3) {
22911                 fg.removeClass([this.invalidClass, this.validClass]);
22912                 fg.addClass(this.validClass);
22913             } else {
22914                 fg.removeClass(['is-valid', 'is-invalid']);
22915                 fg.addClass('is-valid');
22916             }
22917             return;
22918         }
22919         
22920         var group = Roo.bootstrap.CheckBox.get(this.groupId);
22921         
22922         if(!group){
22923             return;
22924         }
22925         
22926         for(var i in group){
22927             var fg = group[i].el.findParent('.form-group', false, true);
22928             if (Roo.bootstrap.version == 3) {
22929                 fg.removeClass([this.invalidClass, this.validClass]);
22930                 fg.addClass(this.validClass);
22931             } else {
22932                 fg.removeClass(['is-valid', 'is-invalid']);
22933                 fg.addClass('is-valid');
22934             }
22935         }
22936     },
22937     
22938      /**
22939      * Mark this field as invalid
22940      * @param {String} msg The validation message
22941      */
22942     markInvalid : function(msg)
22943     {
22944         if(this.allowBlank){
22945             return;
22946         }
22947         
22948         var _this = this;
22949         
22950         this.fireEvent('invalid', this, msg);
22951         
22952         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
22953         
22954         if(this.groupId){
22955             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
22956         }
22957         
22958         if(label){
22959             label.markInvalid();
22960         }
22961             
22962         if(this.inputType == 'radio'){
22963             
22964             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22965                 var fg = e.findParent('.form-group', false, true);
22966                 if (Roo.bootstrap.version == 3) {
22967                     fg.removeClass([_this.invalidClass, _this.validClass]);
22968                     fg.addClass(_this.invalidClass);
22969                 } else {
22970                     fg.removeClass(['is-invalid', 'is-valid']);
22971                     fg.addClass('is-invalid');
22972                 }
22973             });
22974             
22975             return;
22976         }
22977         
22978         if(!this.groupId){
22979             var fg = this.el.findParent('.form-group', false, true);
22980             if (Roo.bootstrap.version == 3) {
22981                 fg.removeClass([_this.invalidClass, _this.validClass]);
22982                 fg.addClass(_this.invalidClass);
22983             } else {
22984                 fg.removeClass(['is-invalid', 'is-valid']);
22985                 fg.addClass('is-invalid');
22986             }
22987             return;
22988         }
22989         
22990         var group = Roo.bootstrap.CheckBox.get(this.groupId);
22991         
22992         if(!group){
22993             return;
22994         }
22995         
22996         for(var i in group){
22997             var fg = group[i].el.findParent('.form-group', false, true);
22998             if (Roo.bootstrap.version == 3) {
22999                 fg.removeClass([_this.invalidClass, _this.validClass]);
23000                 fg.addClass(_this.invalidClass);
23001             } else {
23002                 fg.removeClass(['is-invalid', 'is-valid']);
23003                 fg.addClass('is-invalid');
23004             }
23005         }
23006         
23007     },
23008     
23009     clearInvalid : function()
23010     {
23011         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23012         
23013         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23014         
23015         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23016         
23017         if (label && label.iconEl) {
23018             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23019             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23020         }
23021     },
23022     
23023     disable : function()
23024     {
23025         if(this.inputType != 'radio'){
23026             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23027             return;
23028         }
23029         
23030         var _this = this;
23031         
23032         if(this.rendered){
23033             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23034                 _this.getActionEl().addClass(this.disabledClass);
23035                 e.dom.disabled = true;
23036             });
23037         }
23038         
23039         this.disabled = true;
23040         this.fireEvent("disable", this);
23041         return this;
23042     },
23043
23044     enable : function()
23045     {
23046         if(this.inputType != 'radio'){
23047             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23048             return;
23049         }
23050         
23051         var _this = this;
23052         
23053         if(this.rendered){
23054             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23055                 _this.getActionEl().removeClass(this.disabledClass);
23056                 e.dom.disabled = false;
23057             });
23058         }
23059         
23060         this.disabled = false;
23061         this.fireEvent("enable", this);
23062         return this;
23063     },
23064     
23065     setBoxLabel : function(v)
23066     {
23067         this.boxLabel = v;
23068         
23069         if(this.rendered){
23070             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23071         }
23072     }
23073
23074 });
23075
23076 Roo.apply(Roo.bootstrap.CheckBox, {
23077     
23078     groups: {},
23079     
23080      /**
23081     * register a CheckBox Group
23082     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23083     */
23084     register : function(checkbox)
23085     {
23086         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23087             this.groups[checkbox.groupId] = {};
23088         }
23089         
23090         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23091             return;
23092         }
23093         
23094         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23095         
23096     },
23097     /**
23098     * fetch a CheckBox Group based on the group ID
23099     * @param {string} the group ID
23100     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23101     */
23102     get: function(groupId) {
23103         if (typeof(this.groups[groupId]) == 'undefined') {
23104             return false;
23105         }
23106         
23107         return this.groups[groupId] ;
23108     }
23109     
23110     
23111 });
23112 /*
23113  * - LGPL
23114  *
23115  * RadioItem
23116  * 
23117  */
23118
23119 /**
23120  * @class Roo.bootstrap.Radio
23121  * @extends Roo.bootstrap.Component
23122  * Bootstrap Radio class
23123  * @cfg {String} boxLabel - the label associated
23124  * @cfg {String} value - the value of radio
23125  * 
23126  * @constructor
23127  * Create a new Radio
23128  * @param {Object} config The config object
23129  */
23130 Roo.bootstrap.Radio = function(config){
23131     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23132     
23133 };
23134
23135 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23136     
23137     boxLabel : '',
23138     
23139     value : '',
23140     
23141     getAutoCreate : function()
23142     {
23143         var cfg = {
23144             tag : 'div',
23145             cls : 'form-group radio',
23146             cn : [
23147                 {
23148                     tag : 'label',
23149                     cls : 'box-label',
23150                     html : this.boxLabel
23151                 }
23152             ]
23153         };
23154         
23155         return cfg;
23156     },
23157     
23158     initEvents : function() 
23159     {
23160         this.parent().register(this);
23161         
23162         this.el.on('click', this.onClick, this);
23163         
23164     },
23165     
23166     onClick : function(e)
23167     {
23168         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23169             this.setChecked(true);
23170         }
23171     },
23172     
23173     setChecked : function(state, suppressEvent)
23174     {
23175         this.parent().setValue(this.value, suppressEvent);
23176         
23177     },
23178     
23179     setBoxLabel : function(v)
23180     {
23181         this.boxLabel = v;
23182         
23183         if(this.rendered){
23184             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23185         }
23186     }
23187     
23188 });
23189  
23190
23191  /*
23192  * - LGPL
23193  *
23194  * Input
23195  * 
23196  */
23197
23198 /**
23199  * @class Roo.bootstrap.SecurePass
23200  * @extends Roo.bootstrap.Input
23201  * Bootstrap SecurePass class
23202  *
23203  * 
23204  * @constructor
23205  * Create a new SecurePass
23206  * @param {Object} config The config object
23207  */
23208  
23209 Roo.bootstrap.SecurePass = function (config) {
23210     // these go here, so the translation tool can replace them..
23211     this.errors = {
23212         PwdEmpty: "Please type a password, and then retype it to confirm.",
23213         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23214         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23215         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23216         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23217         FNInPwd: "Your password can't contain your first name. Please type a different password.",
23218         LNInPwd: "Your password can't contain your last name. Please type a different password.",
23219         TooWeak: "Your password is Too Weak."
23220     },
23221     this.meterLabel = "Password strength:";
23222     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23223     this.meterClass = [
23224         "roo-password-meter-tooweak", 
23225         "roo-password-meter-weak", 
23226         "roo-password-meter-medium", 
23227         "roo-password-meter-strong", 
23228         "roo-password-meter-grey"
23229     ];
23230     
23231     this.errors = {};
23232     
23233     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23234 }
23235
23236 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23237     /**
23238      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23239      * {
23240      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
23241      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23242      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23243      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23244      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23245      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
23246      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
23247      * })
23248      */
23249     // private
23250     
23251     meterWidth: 300,
23252     errorMsg :'',    
23253     errors: false,
23254     imageRoot: '/',
23255     /**
23256      * @cfg {String/Object} Label for the strength meter (defaults to
23257      * 'Password strength:')
23258      */
23259     // private
23260     meterLabel: '',
23261     /**
23262      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23263      * ['Weak', 'Medium', 'Strong'])
23264      */
23265     // private    
23266     pwdStrengths: false,    
23267     // private
23268     strength: 0,
23269     // private
23270     _lastPwd: null,
23271     // private
23272     kCapitalLetter: 0,
23273     kSmallLetter: 1,
23274     kDigit: 2,
23275     kPunctuation: 3,
23276     
23277     insecure: false,
23278     // private
23279     initEvents: function ()
23280     {
23281         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23282
23283         if (this.el.is('input[type=password]') && Roo.isSafari) {
23284             this.el.on('keydown', this.SafariOnKeyDown, this);
23285         }
23286
23287         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23288     },
23289     // private
23290     onRender: function (ct, position)
23291     {
23292         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23293         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23294         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23295
23296         this.trigger.createChild({
23297                    cn: [
23298                     {
23299                     //id: 'PwdMeter',
23300                     tag: 'div',
23301                     cls: 'roo-password-meter-grey col-xs-12',
23302                     style: {
23303                         //width: 0,
23304                         //width: this.meterWidth + 'px'                                                
23305                         }
23306                     },
23307                     {                            
23308                          cls: 'roo-password-meter-text'                          
23309                     }
23310                 ]            
23311         });
23312
23313          
23314         if (this.hideTrigger) {
23315             this.trigger.setDisplayed(false);
23316         }
23317         this.setSize(this.width || '', this.height || '');
23318     },
23319     // private
23320     onDestroy: function ()
23321     {
23322         if (this.trigger) {
23323             this.trigger.removeAllListeners();
23324             this.trigger.remove();
23325         }
23326         if (this.wrap) {
23327             this.wrap.remove();
23328         }
23329         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23330     },
23331     // private
23332     checkStrength: function ()
23333     {
23334         var pwd = this.inputEl().getValue();
23335         if (pwd == this._lastPwd) {
23336             return;
23337         }
23338
23339         var strength;
23340         if (this.ClientSideStrongPassword(pwd)) {
23341             strength = 3;
23342         } else if (this.ClientSideMediumPassword(pwd)) {
23343             strength = 2;
23344         } else if (this.ClientSideWeakPassword(pwd)) {
23345             strength = 1;
23346         } else {
23347             strength = 0;
23348         }
23349         
23350         Roo.log('strength1: ' + strength);
23351         
23352         //var pm = this.trigger.child('div/div/div').dom;
23353         var pm = this.trigger.child('div/div');
23354         pm.removeClass(this.meterClass);
23355         pm.addClass(this.meterClass[strength]);
23356                 
23357         
23358         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23359                 
23360         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23361         
23362         this._lastPwd = pwd;
23363     },
23364     reset: function ()
23365     {
23366         Roo.bootstrap.SecurePass.superclass.reset.call(this);
23367         
23368         this._lastPwd = '';
23369         
23370         var pm = this.trigger.child('div/div');
23371         pm.removeClass(this.meterClass);
23372         pm.addClass('roo-password-meter-grey');        
23373         
23374         
23375         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23376         
23377         pt.innerHTML = '';
23378         this.inputEl().dom.type='password';
23379     },
23380     // private
23381     validateValue: function (value)
23382     {
23383         
23384         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23385             return false;
23386         }
23387         if (value.length == 0) {
23388             if (this.allowBlank) {
23389                 this.clearInvalid();
23390                 return true;
23391             }
23392
23393             this.markInvalid(this.errors.PwdEmpty);
23394             this.errorMsg = this.errors.PwdEmpty;
23395             return false;
23396         }
23397         
23398         if(this.insecure){
23399             return true;
23400         }
23401         
23402         if ('[\x21-\x7e]*'.match(value)) {
23403             this.markInvalid(this.errors.PwdBadChar);
23404             this.errorMsg = this.errors.PwdBadChar;
23405             return false;
23406         }
23407         if (value.length < 6) {
23408             this.markInvalid(this.errors.PwdShort);
23409             this.errorMsg = this.errors.PwdShort;
23410             return false;
23411         }
23412         if (value.length > 16) {
23413             this.markInvalid(this.errors.PwdLong);
23414             this.errorMsg = this.errors.PwdLong;
23415             return false;
23416         }
23417         var strength;
23418         if (this.ClientSideStrongPassword(value)) {
23419             strength = 3;
23420         } else if (this.ClientSideMediumPassword(value)) {
23421             strength = 2;
23422         } else if (this.ClientSideWeakPassword(value)) {
23423             strength = 1;
23424         } else {
23425             strength = 0;
23426         }
23427
23428         
23429         if (strength < 2) {
23430             //this.markInvalid(this.errors.TooWeak);
23431             this.errorMsg = this.errors.TooWeak;
23432             //return false;
23433         }
23434         
23435         
23436         console.log('strength2: ' + strength);
23437         
23438         //var pm = this.trigger.child('div/div/div').dom;
23439         
23440         var pm = this.trigger.child('div/div');
23441         pm.removeClass(this.meterClass);
23442         pm.addClass(this.meterClass[strength]);
23443                 
23444         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23445                 
23446         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23447         
23448         this.errorMsg = ''; 
23449         return true;
23450     },
23451     // private
23452     CharacterSetChecks: function (type)
23453     {
23454         this.type = type;
23455         this.fResult = false;
23456     },
23457     // private
23458     isctype: function (character, type)
23459     {
23460         switch (type) {  
23461             case this.kCapitalLetter:
23462                 if (character >= 'A' && character <= 'Z') {
23463                     return true;
23464                 }
23465                 break;
23466             
23467             case this.kSmallLetter:
23468                 if (character >= 'a' && character <= 'z') {
23469                     return true;
23470                 }
23471                 break;
23472             
23473             case this.kDigit:
23474                 if (character >= '0' && character <= '9') {
23475                     return true;
23476                 }
23477                 break;
23478             
23479             case this.kPunctuation:
23480                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
23481                     return true;
23482                 }
23483                 break;
23484             
23485             default:
23486                 return false;
23487         }
23488
23489     },
23490     // private
23491     IsLongEnough: function (pwd, size)
23492     {
23493         return !(pwd == null || isNaN(size) || pwd.length < size);
23494     },
23495     // private
23496     SpansEnoughCharacterSets: function (word, nb)
23497     {
23498         if (!this.IsLongEnough(word, nb))
23499         {
23500             return false;
23501         }
23502
23503         var characterSetChecks = new Array(
23504             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
23505             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
23506         );
23507         
23508         for (var index = 0; index < word.length; ++index) {
23509             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23510                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
23511                     characterSetChecks[nCharSet].fResult = true;
23512                     break;
23513                 }
23514             }
23515         }
23516
23517         var nCharSets = 0;
23518         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23519             if (characterSetChecks[nCharSet].fResult) {
23520                 ++nCharSets;
23521             }
23522         }
23523
23524         if (nCharSets < nb) {
23525             return false;
23526         }
23527         return true;
23528     },
23529     // private
23530     ClientSideStrongPassword: function (pwd)
23531     {
23532         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
23533     },
23534     // private
23535     ClientSideMediumPassword: function (pwd)
23536     {
23537         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
23538     },
23539     // private
23540     ClientSideWeakPassword: function (pwd)
23541     {
23542         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
23543     }
23544           
23545 })//<script type="text/javascript">
23546
23547 /*
23548  * Based  Ext JS Library 1.1.1
23549  * Copyright(c) 2006-2007, Ext JS, LLC.
23550  * LGPL
23551  *
23552  */
23553  
23554 /**
23555  * @class Roo.HtmlEditorCore
23556  * @extends Roo.Component
23557  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
23558  *
23559  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23560  */
23561
23562 Roo.HtmlEditorCore = function(config){
23563     
23564     
23565     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
23566     
23567     
23568     this.addEvents({
23569         /**
23570          * @event initialize
23571          * Fires when the editor is fully initialized (including the iframe)
23572          * @param {Roo.HtmlEditorCore} this
23573          */
23574         initialize: true,
23575         /**
23576          * @event activate
23577          * Fires when the editor is first receives the focus. Any insertion must wait
23578          * until after this event.
23579          * @param {Roo.HtmlEditorCore} this
23580          */
23581         activate: true,
23582          /**
23583          * @event beforesync
23584          * Fires before the textarea is updated with content from the editor iframe. Return false
23585          * to cancel the sync.
23586          * @param {Roo.HtmlEditorCore} this
23587          * @param {String} html
23588          */
23589         beforesync: true,
23590          /**
23591          * @event beforepush
23592          * Fires before the iframe editor is updated with content from the textarea. Return false
23593          * to cancel the push.
23594          * @param {Roo.HtmlEditorCore} this
23595          * @param {String} html
23596          */
23597         beforepush: true,
23598          /**
23599          * @event sync
23600          * Fires when the textarea is updated with content from the editor iframe.
23601          * @param {Roo.HtmlEditorCore} this
23602          * @param {String} html
23603          */
23604         sync: true,
23605          /**
23606          * @event push
23607          * Fires when the iframe editor is updated with content from the textarea.
23608          * @param {Roo.HtmlEditorCore} this
23609          * @param {String} html
23610          */
23611         push: true,
23612         
23613         /**
23614          * @event editorevent
23615          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23616          * @param {Roo.HtmlEditorCore} this
23617          */
23618         editorevent: true
23619         
23620     });
23621     
23622     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
23623     
23624     // defaults : white / black...
23625     this.applyBlacklists();
23626     
23627     
23628     
23629 };
23630
23631
23632 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
23633
23634
23635      /**
23636      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
23637      */
23638     
23639     owner : false,
23640     
23641      /**
23642      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23643      *                        Roo.resizable.
23644      */
23645     resizable : false,
23646      /**
23647      * @cfg {Number} height (in pixels)
23648      */   
23649     height: 300,
23650    /**
23651      * @cfg {Number} width (in pixels)
23652      */   
23653     width: 500,
23654     
23655     /**
23656      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23657      * 
23658      */
23659     stylesheets: false,
23660     
23661     // id of frame..
23662     frameId: false,
23663     
23664     // private properties
23665     validationEvent : false,
23666     deferHeight: true,
23667     initialized : false,
23668     activated : false,
23669     sourceEditMode : false,
23670     onFocus : Roo.emptyFn,
23671     iframePad:3,
23672     hideMode:'offsets',
23673     
23674     clearUp: true,
23675     
23676     // blacklist + whitelisted elements..
23677     black: false,
23678     white: false,
23679      
23680     bodyCls : '',
23681
23682     /**
23683      * Protected method that will not generally be called directly. It
23684      * is called when the editor initializes the iframe with HTML contents. Override this method if you
23685      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
23686      */
23687     getDocMarkup : function(){
23688         // body styles..
23689         var st = '';
23690         
23691         // inherit styels from page...?? 
23692         if (this.stylesheets === false) {
23693             
23694             Roo.get(document.head).select('style').each(function(node) {
23695                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23696             });
23697             
23698             Roo.get(document.head).select('link').each(function(node) { 
23699                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23700             });
23701             
23702         } else if (!this.stylesheets.length) {
23703                 // simple..
23704                 st = '<style type="text/css">' +
23705                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23706                    '</style>';
23707         } else { 
23708             st = '<style type="text/css">' +
23709                     this.stylesheets +
23710                 '</style>';
23711         }
23712         
23713         st +=  '<style type="text/css">' +
23714             'IMG { cursor: pointer } ' +
23715         '</style>';
23716
23717         var cls = 'roo-htmleditor-body';
23718         
23719         if(this.bodyCls.length){
23720             cls += ' ' + this.bodyCls;
23721         }
23722         
23723         return '<html><head>' + st  +
23724             //<style type="text/css">' +
23725             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23726             //'</style>' +
23727             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
23728     },
23729
23730     // private
23731     onRender : function(ct, position)
23732     {
23733         var _t = this;
23734         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
23735         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
23736         
23737         
23738         this.el.dom.style.border = '0 none';
23739         this.el.dom.setAttribute('tabIndex', -1);
23740         this.el.addClass('x-hidden hide');
23741         
23742         
23743         
23744         if(Roo.isIE){ // fix IE 1px bogus margin
23745             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
23746         }
23747        
23748         
23749         this.frameId = Roo.id();
23750         
23751          
23752         
23753         var iframe = this.owner.wrap.createChild({
23754             tag: 'iframe',
23755             cls: 'form-control', // bootstrap..
23756             id: this.frameId,
23757             name: this.frameId,
23758             frameBorder : 'no',
23759             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
23760         }, this.el
23761         );
23762         
23763         
23764         this.iframe = iframe.dom;
23765
23766          this.assignDocWin();
23767         
23768         this.doc.designMode = 'on';
23769        
23770         this.doc.open();
23771         this.doc.write(this.getDocMarkup());
23772         this.doc.close();
23773
23774         
23775         var task = { // must defer to wait for browser to be ready
23776             run : function(){
23777                 //console.log("run task?" + this.doc.readyState);
23778                 this.assignDocWin();
23779                 if(this.doc.body || this.doc.readyState == 'complete'){
23780                     try {
23781                         this.doc.designMode="on";
23782                     } catch (e) {
23783                         return;
23784                     }
23785                     Roo.TaskMgr.stop(task);
23786                     this.initEditor.defer(10, this);
23787                 }
23788             },
23789             interval : 10,
23790             duration: 10000,
23791             scope: this
23792         };
23793         Roo.TaskMgr.start(task);
23794
23795     },
23796
23797     // private
23798     onResize : function(w, h)
23799     {
23800          Roo.log('resize: ' +w + ',' + h );
23801         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
23802         if(!this.iframe){
23803             return;
23804         }
23805         if(typeof w == 'number'){
23806             
23807             this.iframe.style.width = w + 'px';
23808         }
23809         if(typeof h == 'number'){
23810             
23811             this.iframe.style.height = h + 'px';
23812             if(this.doc){
23813                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
23814             }
23815         }
23816         
23817     },
23818
23819     /**
23820      * Toggles the editor between standard and source edit mode.
23821      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23822      */
23823     toggleSourceEdit : function(sourceEditMode){
23824         
23825         this.sourceEditMode = sourceEditMode === true;
23826         
23827         if(this.sourceEditMode){
23828  
23829             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
23830             
23831         }else{
23832             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
23833             //this.iframe.className = '';
23834             this.deferFocus();
23835         }
23836         //this.setSize(this.owner.wrap.getSize());
23837         //this.fireEvent('editmodechange', this, this.sourceEditMode);
23838     },
23839
23840     
23841   
23842
23843     /**
23844      * Protected method that will not generally be called directly. If you need/want
23845      * custom HTML cleanup, this is the method you should override.
23846      * @param {String} html The HTML to be cleaned
23847      * return {String} The cleaned HTML
23848      */
23849     cleanHtml : function(html){
23850         html = String(html);
23851         if(html.length > 5){
23852             if(Roo.isSafari){ // strip safari nonsense
23853                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
23854             }
23855         }
23856         if(html == '&nbsp;'){
23857             html = '';
23858         }
23859         return html;
23860     },
23861
23862     /**
23863      * HTML Editor -> Textarea
23864      * Protected method that will not generally be called directly. Syncs the contents
23865      * of the editor iframe with the textarea.
23866      */
23867     syncValue : function(){
23868         if(this.initialized){
23869             var bd = (this.doc.body || this.doc.documentElement);
23870             //this.cleanUpPaste(); -- this is done else where and causes havoc..
23871             var html = bd.innerHTML;
23872             if(Roo.isSafari){
23873                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
23874                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
23875                 if(m && m[1]){
23876                     html = '<div style="'+m[0]+'">' + html + '</div>';
23877                 }
23878             }
23879             html = this.cleanHtml(html);
23880             // fix up the special chars.. normaly like back quotes in word...
23881             // however we do not want to do this with chinese..
23882             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
23883                 
23884                 var cc = match.charCodeAt();
23885
23886                 // Get the character value, handling surrogate pairs
23887                 if (match.length == 2) {
23888                     // It's a surrogate pair, calculate the Unicode code point
23889                     var high = match.charCodeAt(0) - 0xD800;
23890                     var low  = match.charCodeAt(1) - 0xDC00;
23891                     cc = (high * 0x400) + low + 0x10000;
23892                 }  else if (
23893                     (cc >= 0x4E00 && cc < 0xA000 ) ||
23894                     (cc >= 0x3400 && cc < 0x4E00 ) ||
23895                     (cc >= 0xf900 && cc < 0xfb00 )
23896                 ) {
23897                         return match;
23898                 }  
23899          
23900                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
23901                 return "&#" + cc + ";";
23902                 
23903                 
23904             });
23905             
23906             
23907              
23908             if(this.owner.fireEvent('beforesync', this, html) !== false){
23909                 this.el.dom.value = html;
23910                 this.owner.fireEvent('sync', this, html);
23911             }
23912         }
23913     },
23914
23915     /**
23916      * Protected method that will not generally be called directly. Pushes the value of the textarea
23917      * into the iframe editor.
23918      */
23919     pushValue : function(){
23920         if(this.initialized){
23921             var v = this.el.dom.value.trim();
23922             
23923 //            if(v.length < 1){
23924 //                v = '&#160;';
23925 //            }
23926             
23927             if(this.owner.fireEvent('beforepush', this, v) !== false){
23928                 var d = (this.doc.body || this.doc.documentElement);
23929                 d.innerHTML = v;
23930                 this.cleanUpPaste();
23931                 this.el.dom.value = d.innerHTML;
23932                 this.owner.fireEvent('push', this, v);
23933             }
23934         }
23935     },
23936
23937     // private
23938     deferFocus : function(){
23939         this.focus.defer(10, this);
23940     },
23941
23942     // doc'ed in Field
23943     focus : function(){
23944         if(this.win && !this.sourceEditMode){
23945             this.win.focus();
23946         }else{
23947             this.el.focus();
23948         }
23949     },
23950     
23951     assignDocWin: function()
23952     {
23953         var iframe = this.iframe;
23954         
23955          if(Roo.isIE){
23956             this.doc = iframe.contentWindow.document;
23957             this.win = iframe.contentWindow;
23958         } else {
23959 //            if (!Roo.get(this.frameId)) {
23960 //                return;
23961 //            }
23962 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
23963 //            this.win = Roo.get(this.frameId).dom.contentWindow;
23964             
23965             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
23966                 return;
23967             }
23968             
23969             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
23970             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
23971         }
23972     },
23973     
23974     // private
23975     initEditor : function(){
23976         //console.log("INIT EDITOR");
23977         this.assignDocWin();
23978         
23979         
23980         
23981         this.doc.designMode="on";
23982         this.doc.open();
23983         this.doc.write(this.getDocMarkup());
23984         this.doc.close();
23985         
23986         var dbody = (this.doc.body || this.doc.documentElement);
23987         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
23988         // this copies styles from the containing element into thsi one..
23989         // not sure why we need all of this..
23990         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
23991         
23992         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
23993         //ss['background-attachment'] = 'fixed'; // w3c
23994         dbody.bgProperties = 'fixed'; // ie
23995         //Roo.DomHelper.applyStyles(dbody, ss);
23996         Roo.EventManager.on(this.doc, {
23997             //'mousedown': this.onEditorEvent,
23998             'mouseup': this.onEditorEvent,
23999             'dblclick': this.onEditorEvent,
24000             'click': this.onEditorEvent,
24001             'keyup': this.onEditorEvent,
24002             buffer:100,
24003             scope: this
24004         });
24005         if(Roo.isGecko){
24006             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24007         }
24008         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24009             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24010         }
24011         this.initialized = true;
24012
24013         this.owner.fireEvent('initialize', this);
24014         this.pushValue();
24015     },
24016
24017     // private
24018     onDestroy : function(){
24019         
24020         
24021         
24022         if(this.rendered){
24023             
24024             //for (var i =0; i < this.toolbars.length;i++) {
24025             //    // fixme - ask toolbars for heights?
24026             //    this.toolbars[i].onDestroy();
24027            // }
24028             
24029             //this.wrap.dom.innerHTML = '';
24030             //this.wrap.remove();
24031         }
24032     },
24033
24034     // private
24035     onFirstFocus : function(){
24036         
24037         this.assignDocWin();
24038         
24039         
24040         this.activated = true;
24041          
24042     
24043         if(Roo.isGecko){ // prevent silly gecko errors
24044             this.win.focus();
24045             var s = this.win.getSelection();
24046             if(!s.focusNode || s.focusNode.nodeType != 3){
24047                 var r = s.getRangeAt(0);
24048                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24049                 r.collapse(true);
24050                 this.deferFocus();
24051             }
24052             try{
24053                 this.execCmd('useCSS', true);
24054                 this.execCmd('styleWithCSS', false);
24055             }catch(e){}
24056         }
24057         this.owner.fireEvent('activate', this);
24058     },
24059
24060     // private
24061     adjustFont: function(btn){
24062         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24063         //if(Roo.isSafari){ // safari
24064         //    adjust *= 2;
24065        // }
24066         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24067         if(Roo.isSafari){ // safari
24068             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24069             v =  (v < 10) ? 10 : v;
24070             v =  (v > 48) ? 48 : v;
24071             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24072             
24073         }
24074         
24075         
24076         v = Math.max(1, v+adjust);
24077         
24078         this.execCmd('FontSize', v  );
24079     },
24080
24081     onEditorEvent : function(e)
24082     {
24083         this.owner.fireEvent('editorevent', this, e);
24084       //  this.updateToolbar();
24085         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24086     },
24087
24088     insertTag : function(tg)
24089     {
24090         // could be a bit smarter... -> wrap the current selected tRoo..
24091         if (tg.toLowerCase() == 'span' ||
24092             tg.toLowerCase() == 'code' ||
24093             tg.toLowerCase() == 'sup' ||
24094             tg.toLowerCase() == 'sub' 
24095             ) {
24096             
24097             range = this.createRange(this.getSelection());
24098             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24099             wrappingNode.appendChild(range.extractContents());
24100             range.insertNode(wrappingNode);
24101
24102             return;
24103             
24104             
24105             
24106         }
24107         this.execCmd("formatblock",   tg);
24108         
24109     },
24110     
24111     insertText : function(txt)
24112     {
24113         
24114         
24115         var range = this.createRange();
24116         range.deleteContents();
24117                //alert(Sender.getAttribute('label'));
24118                
24119         range.insertNode(this.doc.createTextNode(txt));
24120     } ,
24121     
24122      
24123
24124     /**
24125      * Executes a Midas editor command on the editor document and performs necessary focus and
24126      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24127      * @param {String} cmd The Midas command
24128      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24129      */
24130     relayCmd : function(cmd, value){
24131         this.win.focus();
24132         this.execCmd(cmd, value);
24133         this.owner.fireEvent('editorevent', this);
24134         //this.updateToolbar();
24135         this.owner.deferFocus();
24136     },
24137
24138     /**
24139      * Executes a Midas editor command directly on the editor document.
24140      * For visual commands, you should use {@link #relayCmd} instead.
24141      * <b>This should only be called after the editor is initialized.</b>
24142      * @param {String} cmd The Midas command
24143      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24144      */
24145     execCmd : function(cmd, value){
24146         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24147         this.syncValue();
24148     },
24149  
24150  
24151    
24152     /**
24153      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24154      * to insert tRoo.
24155      * @param {String} text | dom node.. 
24156      */
24157     insertAtCursor : function(text)
24158     {
24159         
24160         if(!this.activated){
24161             return;
24162         }
24163         /*
24164         if(Roo.isIE){
24165             this.win.focus();
24166             var r = this.doc.selection.createRange();
24167             if(r){
24168                 r.collapse(true);
24169                 r.pasteHTML(text);
24170                 this.syncValue();
24171                 this.deferFocus();
24172             
24173             }
24174             return;
24175         }
24176         */
24177         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24178             this.win.focus();
24179             
24180             
24181             // from jquery ui (MIT licenced)
24182             var range, node;
24183             var win = this.win;
24184             
24185             if (win.getSelection && win.getSelection().getRangeAt) {
24186                 range = win.getSelection().getRangeAt(0);
24187                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24188                 range.insertNode(node);
24189             } else if (win.document.selection && win.document.selection.createRange) {
24190                 // no firefox support
24191                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24192                 win.document.selection.createRange().pasteHTML(txt);
24193             } else {
24194                 // no firefox support
24195                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24196                 this.execCmd('InsertHTML', txt);
24197             } 
24198             
24199             this.syncValue();
24200             
24201             this.deferFocus();
24202         }
24203     },
24204  // private
24205     mozKeyPress : function(e){
24206         if(e.ctrlKey){
24207             var c = e.getCharCode(), cmd;
24208           
24209             if(c > 0){
24210                 c = String.fromCharCode(c).toLowerCase();
24211                 switch(c){
24212                     case 'b':
24213                         cmd = 'bold';
24214                         break;
24215                     case 'i':
24216                         cmd = 'italic';
24217                         break;
24218                     
24219                     case 'u':
24220                         cmd = 'underline';
24221                         break;
24222                     
24223                     case 'v':
24224                         this.cleanUpPaste.defer(100, this);
24225                         return;
24226                         
24227                 }
24228                 if(cmd){
24229                     this.win.focus();
24230                     this.execCmd(cmd);
24231                     this.deferFocus();
24232                     e.preventDefault();
24233                 }
24234                 
24235             }
24236         }
24237     },
24238
24239     // private
24240     fixKeys : function(){ // load time branching for fastest keydown performance
24241         if(Roo.isIE){
24242             return function(e){
24243                 var k = e.getKey(), r;
24244                 if(k == e.TAB){
24245                     e.stopEvent();
24246                     r = this.doc.selection.createRange();
24247                     if(r){
24248                         r.collapse(true);
24249                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24250                         this.deferFocus();
24251                     }
24252                     return;
24253                 }
24254                 
24255                 if(k == e.ENTER){
24256                     r = this.doc.selection.createRange();
24257                     if(r){
24258                         var target = r.parentElement();
24259                         if(!target || target.tagName.toLowerCase() != 'li'){
24260                             e.stopEvent();
24261                             r.pasteHTML('<br />');
24262                             r.collapse(false);
24263                             r.select();
24264                         }
24265                     }
24266                 }
24267                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24268                     this.cleanUpPaste.defer(100, this);
24269                     return;
24270                 }
24271                 
24272                 
24273             };
24274         }else if(Roo.isOpera){
24275             return function(e){
24276                 var k = e.getKey();
24277                 if(k == e.TAB){
24278                     e.stopEvent();
24279                     this.win.focus();
24280                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24281                     this.deferFocus();
24282                 }
24283                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24284                     this.cleanUpPaste.defer(100, this);
24285                     return;
24286                 }
24287                 
24288             };
24289         }else if(Roo.isSafari){
24290             return function(e){
24291                 var k = e.getKey();
24292                 
24293                 if(k == e.TAB){
24294                     e.stopEvent();
24295                     this.execCmd('InsertText','\t');
24296                     this.deferFocus();
24297                     return;
24298                 }
24299                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24300                     this.cleanUpPaste.defer(100, this);
24301                     return;
24302                 }
24303                 
24304              };
24305         }
24306     }(),
24307     
24308     getAllAncestors: function()
24309     {
24310         var p = this.getSelectedNode();
24311         var a = [];
24312         if (!p) {
24313             a.push(p); // push blank onto stack..
24314             p = this.getParentElement();
24315         }
24316         
24317         
24318         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24319             a.push(p);
24320             p = p.parentNode;
24321         }
24322         a.push(this.doc.body);
24323         return a;
24324     },
24325     lastSel : false,
24326     lastSelNode : false,
24327     
24328     
24329     getSelection : function() 
24330     {
24331         this.assignDocWin();
24332         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24333     },
24334     
24335     getSelectedNode: function() 
24336     {
24337         // this may only work on Gecko!!!
24338         
24339         // should we cache this!!!!
24340         
24341         
24342         
24343          
24344         var range = this.createRange(this.getSelection()).cloneRange();
24345         
24346         if (Roo.isIE) {
24347             var parent = range.parentElement();
24348             while (true) {
24349                 var testRange = range.duplicate();
24350                 testRange.moveToElementText(parent);
24351                 if (testRange.inRange(range)) {
24352                     break;
24353                 }
24354                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24355                     break;
24356                 }
24357                 parent = parent.parentElement;
24358             }
24359             return parent;
24360         }
24361         
24362         // is ancestor a text element.
24363         var ac =  range.commonAncestorContainer;
24364         if (ac.nodeType == 3) {
24365             ac = ac.parentNode;
24366         }
24367         
24368         var ar = ac.childNodes;
24369          
24370         var nodes = [];
24371         var other_nodes = [];
24372         var has_other_nodes = false;
24373         for (var i=0;i<ar.length;i++) {
24374             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24375                 continue;
24376             }
24377             // fullly contained node.
24378             
24379             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24380                 nodes.push(ar[i]);
24381                 continue;
24382             }
24383             
24384             // probably selected..
24385             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24386                 other_nodes.push(ar[i]);
24387                 continue;
24388             }
24389             // outer..
24390             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24391                 continue;
24392             }
24393             
24394             
24395             has_other_nodes = true;
24396         }
24397         if (!nodes.length && other_nodes.length) {
24398             nodes= other_nodes;
24399         }
24400         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24401             return false;
24402         }
24403         
24404         return nodes[0];
24405     },
24406     createRange: function(sel)
24407     {
24408         // this has strange effects when using with 
24409         // top toolbar - not sure if it's a great idea.
24410         //this.editor.contentWindow.focus();
24411         if (typeof sel != "undefined") {
24412             try {
24413                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24414             } catch(e) {
24415                 return this.doc.createRange();
24416             }
24417         } else {
24418             return this.doc.createRange();
24419         }
24420     },
24421     getParentElement: function()
24422     {
24423         
24424         this.assignDocWin();
24425         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24426         
24427         var range = this.createRange(sel);
24428          
24429         try {
24430             var p = range.commonAncestorContainer;
24431             while (p.nodeType == 3) { // text node
24432                 p = p.parentNode;
24433             }
24434             return p;
24435         } catch (e) {
24436             return null;
24437         }
24438     
24439     },
24440     /***
24441      *
24442      * Range intersection.. the hard stuff...
24443      *  '-1' = before
24444      *  '0' = hits..
24445      *  '1' = after.
24446      *         [ -- selected range --- ]
24447      *   [fail]                        [fail]
24448      *
24449      *    basically..
24450      *      if end is before start or  hits it. fail.
24451      *      if start is after end or hits it fail.
24452      *
24453      *   if either hits (but other is outside. - then it's not 
24454      *   
24455      *    
24456      **/
24457     
24458     
24459     // @see http://www.thismuchiknow.co.uk/?p=64.
24460     rangeIntersectsNode : function(range, node)
24461     {
24462         var nodeRange = node.ownerDocument.createRange();
24463         try {
24464             nodeRange.selectNode(node);
24465         } catch (e) {
24466             nodeRange.selectNodeContents(node);
24467         }
24468     
24469         var rangeStartRange = range.cloneRange();
24470         rangeStartRange.collapse(true);
24471     
24472         var rangeEndRange = range.cloneRange();
24473         rangeEndRange.collapse(false);
24474     
24475         var nodeStartRange = nodeRange.cloneRange();
24476         nodeStartRange.collapse(true);
24477     
24478         var nodeEndRange = nodeRange.cloneRange();
24479         nodeEndRange.collapse(false);
24480     
24481         return rangeStartRange.compareBoundaryPoints(
24482                  Range.START_TO_START, nodeEndRange) == -1 &&
24483                rangeEndRange.compareBoundaryPoints(
24484                  Range.START_TO_START, nodeStartRange) == 1;
24485         
24486          
24487     },
24488     rangeCompareNode : function(range, node)
24489     {
24490         var nodeRange = node.ownerDocument.createRange();
24491         try {
24492             nodeRange.selectNode(node);
24493         } catch (e) {
24494             nodeRange.selectNodeContents(node);
24495         }
24496         
24497         
24498         range.collapse(true);
24499     
24500         nodeRange.collapse(true);
24501      
24502         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
24503         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
24504          
24505         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
24506         
24507         var nodeIsBefore   =  ss == 1;
24508         var nodeIsAfter    = ee == -1;
24509         
24510         if (nodeIsBefore && nodeIsAfter) {
24511             return 0; // outer
24512         }
24513         if (!nodeIsBefore && nodeIsAfter) {
24514             return 1; //right trailed.
24515         }
24516         
24517         if (nodeIsBefore && !nodeIsAfter) {
24518             return 2;  // left trailed.
24519         }
24520         // fully contined.
24521         return 3;
24522     },
24523
24524     // private? - in a new class?
24525     cleanUpPaste :  function()
24526     {
24527         // cleans up the whole document..
24528         Roo.log('cleanuppaste');
24529         
24530         this.cleanUpChildren(this.doc.body);
24531         var clean = this.cleanWordChars(this.doc.body.innerHTML);
24532         if (clean != this.doc.body.innerHTML) {
24533             this.doc.body.innerHTML = clean;
24534         }
24535         
24536     },
24537     
24538     cleanWordChars : function(input) {// change the chars to hex code
24539         var he = Roo.HtmlEditorCore;
24540         
24541         var output = input;
24542         Roo.each(he.swapCodes, function(sw) { 
24543             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
24544             
24545             output = output.replace(swapper, sw[1]);
24546         });
24547         
24548         return output;
24549     },
24550     
24551     
24552     cleanUpChildren : function (n)
24553     {
24554         if (!n.childNodes.length) {
24555             return;
24556         }
24557         for (var i = n.childNodes.length-1; i > -1 ; i--) {
24558            this.cleanUpChild(n.childNodes[i]);
24559         }
24560     },
24561     
24562     
24563         
24564     
24565     cleanUpChild : function (node)
24566     {
24567         var ed = this;
24568         //console.log(node);
24569         if (node.nodeName == "#text") {
24570             // clean up silly Windows -- stuff?
24571             return; 
24572         }
24573         if (node.nodeName == "#comment") {
24574             node.parentNode.removeChild(node);
24575             // clean up silly Windows -- stuff?
24576             return; 
24577         }
24578         var lcname = node.tagName.toLowerCase();
24579         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
24580         // whitelist of tags..
24581         
24582         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
24583             // remove node.
24584             node.parentNode.removeChild(node);
24585             return;
24586             
24587         }
24588         
24589         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
24590         
24591         // spans with no attributes - just remove them..
24592         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
24593             remove_keep_children = true;
24594         }
24595         
24596         // remove <a name=....> as rendering on yahoo mailer is borked with this.
24597         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
24598         
24599         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
24600         //    remove_keep_children = true;
24601         //}
24602         
24603         if (remove_keep_children) {
24604             this.cleanUpChildren(node);
24605             // inserts everything just before this node...
24606             while (node.childNodes.length) {
24607                 var cn = node.childNodes[0];
24608                 node.removeChild(cn);
24609                 node.parentNode.insertBefore(cn, node);
24610             }
24611             node.parentNode.removeChild(node);
24612             return;
24613         }
24614         
24615         if (!node.attributes || !node.attributes.length) {
24616             
24617           
24618             
24619             
24620             this.cleanUpChildren(node);
24621             return;
24622         }
24623         
24624         function cleanAttr(n,v)
24625         {
24626             
24627             if (v.match(/^\./) || v.match(/^\//)) {
24628                 return;
24629             }
24630             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
24631                 return;
24632             }
24633             if (v.match(/^#/)) {
24634                 return;
24635             }
24636 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
24637             node.removeAttribute(n);
24638             
24639         }
24640         
24641         var cwhite = this.cwhite;
24642         var cblack = this.cblack;
24643             
24644         function cleanStyle(n,v)
24645         {
24646             if (v.match(/expression/)) { //XSS?? should we even bother..
24647                 node.removeAttribute(n);
24648                 return;
24649             }
24650             
24651             var parts = v.split(/;/);
24652             var clean = [];
24653             
24654             Roo.each(parts, function(p) {
24655                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
24656                 if (!p.length) {
24657                     return true;
24658                 }
24659                 var l = p.split(':').shift().replace(/\s+/g,'');
24660                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
24661                 
24662                 if ( cwhite.length && cblack.indexOf(l) > -1) {
24663 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24664                     //node.removeAttribute(n);
24665                     return true;
24666                 }
24667                 //Roo.log()
24668                 // only allow 'c whitelisted system attributes'
24669                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
24670 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24671                     //node.removeAttribute(n);
24672                     return true;
24673                 }
24674                 
24675                 
24676                  
24677                 
24678                 clean.push(p);
24679                 return true;
24680             });
24681             if (clean.length) { 
24682                 node.setAttribute(n, clean.join(';'));
24683             } else {
24684                 node.removeAttribute(n);
24685             }
24686             
24687         }
24688         
24689         
24690         for (var i = node.attributes.length-1; i > -1 ; i--) {
24691             var a = node.attributes[i];
24692             //console.log(a);
24693             
24694             if (a.name.toLowerCase().substr(0,2)=='on')  {
24695                 node.removeAttribute(a.name);
24696                 continue;
24697             }
24698             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
24699                 node.removeAttribute(a.name);
24700                 continue;
24701             }
24702             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
24703                 cleanAttr(a.name,a.value); // fixme..
24704                 continue;
24705             }
24706             if (a.name == 'style') {
24707                 cleanStyle(a.name,a.value);
24708                 continue;
24709             }
24710             /// clean up MS crap..
24711             // tecnically this should be a list of valid class'es..
24712             
24713             
24714             if (a.name == 'class') {
24715                 if (a.value.match(/^Mso/)) {
24716                     node.removeAttribute('class');
24717                 }
24718                 
24719                 if (a.value.match(/^body$/)) {
24720                     node.removeAttribute('class');
24721                 }
24722                 continue;
24723             }
24724             
24725             // style cleanup!?
24726             // class cleanup?
24727             
24728         }
24729         
24730         
24731         this.cleanUpChildren(node);
24732         
24733         
24734     },
24735     
24736     /**
24737      * Clean up MS wordisms...
24738      */
24739     cleanWord : function(node)
24740     {
24741         if (!node) {
24742             this.cleanWord(this.doc.body);
24743             return;
24744         }
24745         
24746         if(
24747                 node.nodeName == 'SPAN' &&
24748                 !node.hasAttributes() &&
24749                 node.childNodes.length == 1 &&
24750                 node.firstChild.nodeName == "#text"  
24751         ) {
24752             var textNode = node.firstChild;
24753             node.removeChild(textNode);
24754             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
24755                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
24756             }
24757             node.parentNode.insertBefore(textNode, node);
24758             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
24759                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
24760             }
24761             node.parentNode.removeChild(node);
24762         }
24763         
24764         if (node.nodeName == "#text") {
24765             // clean up silly Windows -- stuff?
24766             return; 
24767         }
24768         if (node.nodeName == "#comment") {
24769             node.parentNode.removeChild(node);
24770             // clean up silly Windows -- stuff?
24771             return; 
24772         }
24773         
24774         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
24775             node.parentNode.removeChild(node);
24776             return;
24777         }
24778         //Roo.log(node.tagName);
24779         // remove - but keep children..
24780         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
24781             //Roo.log('-- removed');
24782             while (node.childNodes.length) {
24783                 var cn = node.childNodes[0];
24784                 node.removeChild(cn);
24785                 node.parentNode.insertBefore(cn, node);
24786                 // move node to parent - and clean it..
24787                 this.cleanWord(cn);
24788             }
24789             node.parentNode.removeChild(node);
24790             /// no need to iterate chidlren = it's got none..
24791             //this.iterateChildren(node, this.cleanWord);
24792             return;
24793         }
24794         // clean styles
24795         if (node.className.length) {
24796             
24797             var cn = node.className.split(/\W+/);
24798             var cna = [];
24799             Roo.each(cn, function(cls) {
24800                 if (cls.match(/Mso[a-zA-Z]+/)) {
24801                     return;
24802                 }
24803                 cna.push(cls);
24804             });
24805             node.className = cna.length ? cna.join(' ') : '';
24806             if (!cna.length) {
24807                 node.removeAttribute("class");
24808             }
24809         }
24810         
24811         if (node.hasAttribute("lang")) {
24812             node.removeAttribute("lang");
24813         }
24814         
24815         if (node.hasAttribute("style")) {
24816             
24817             var styles = node.getAttribute("style").split(";");
24818             var nstyle = [];
24819             Roo.each(styles, function(s) {
24820                 if (!s.match(/:/)) {
24821                     return;
24822                 }
24823                 var kv = s.split(":");
24824                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
24825                     return;
24826                 }
24827                 // what ever is left... we allow.
24828                 nstyle.push(s);
24829             });
24830             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
24831             if (!nstyle.length) {
24832                 node.removeAttribute('style');
24833             }
24834         }
24835         this.iterateChildren(node, this.cleanWord);
24836         
24837         
24838         
24839     },
24840     /**
24841      * iterateChildren of a Node, calling fn each time, using this as the scole..
24842      * @param {DomNode} node node to iterate children of.
24843      * @param {Function} fn method of this class to call on each item.
24844      */
24845     iterateChildren : function(node, fn)
24846     {
24847         if (!node.childNodes.length) {
24848                 return;
24849         }
24850         for (var i = node.childNodes.length-1; i > -1 ; i--) {
24851            fn.call(this, node.childNodes[i])
24852         }
24853     },
24854     
24855     
24856     /**
24857      * cleanTableWidths.
24858      *
24859      * Quite often pasting from word etc.. results in tables with column and widths.
24860      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
24861      *
24862      */
24863     cleanTableWidths : function(node)
24864     {
24865          
24866          
24867         if (!node) {
24868             this.cleanTableWidths(this.doc.body);
24869             return;
24870         }
24871         
24872         // ignore list...
24873         if (node.nodeName == "#text" || node.nodeName == "#comment") {
24874             return; 
24875         }
24876         Roo.log(node.tagName);
24877         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
24878             this.iterateChildren(node, this.cleanTableWidths);
24879             return;
24880         }
24881         if (node.hasAttribute('width')) {
24882             node.removeAttribute('width');
24883         }
24884         
24885          
24886         if (node.hasAttribute("style")) {
24887             // pretty basic...
24888             
24889             var styles = node.getAttribute("style").split(";");
24890             var nstyle = [];
24891             Roo.each(styles, function(s) {
24892                 if (!s.match(/:/)) {
24893                     return;
24894                 }
24895                 var kv = s.split(":");
24896                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
24897                     return;
24898                 }
24899                 // what ever is left... we allow.
24900                 nstyle.push(s);
24901             });
24902             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
24903             if (!nstyle.length) {
24904                 node.removeAttribute('style');
24905             }
24906         }
24907         
24908         this.iterateChildren(node, this.cleanTableWidths);
24909         
24910         
24911     },
24912     
24913     
24914     
24915     
24916     domToHTML : function(currentElement, depth, nopadtext) {
24917         
24918         depth = depth || 0;
24919         nopadtext = nopadtext || false;
24920     
24921         if (!currentElement) {
24922             return this.domToHTML(this.doc.body);
24923         }
24924         
24925         //Roo.log(currentElement);
24926         var j;
24927         var allText = false;
24928         var nodeName = currentElement.nodeName;
24929         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
24930         
24931         if  (nodeName == '#text') {
24932             
24933             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
24934         }
24935         
24936         
24937         var ret = '';
24938         if (nodeName != 'BODY') {
24939              
24940             var i = 0;
24941             // Prints the node tagName, such as <A>, <IMG>, etc
24942             if (tagName) {
24943                 var attr = [];
24944                 for(i = 0; i < currentElement.attributes.length;i++) {
24945                     // quoting?
24946                     var aname = currentElement.attributes.item(i).name;
24947                     if (!currentElement.attributes.item(i).value.length) {
24948                         continue;
24949                     }
24950                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
24951                 }
24952                 
24953                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
24954             } 
24955             else {
24956                 
24957                 // eack
24958             }
24959         } else {
24960             tagName = false;
24961         }
24962         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
24963             return ret;
24964         }
24965         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
24966             nopadtext = true;
24967         }
24968         
24969         
24970         // Traverse the tree
24971         i = 0;
24972         var currentElementChild = currentElement.childNodes.item(i);
24973         var allText = true;
24974         var innerHTML  = '';
24975         lastnode = '';
24976         while (currentElementChild) {
24977             // Formatting code (indent the tree so it looks nice on the screen)
24978             var nopad = nopadtext;
24979             if (lastnode == 'SPAN') {
24980                 nopad  = true;
24981             }
24982             // text
24983             if  (currentElementChild.nodeName == '#text') {
24984                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
24985                 toadd = nopadtext ? toadd : toadd.trim();
24986                 if (!nopad && toadd.length > 80) {
24987                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
24988                 }
24989                 innerHTML  += toadd;
24990                 
24991                 i++;
24992                 currentElementChild = currentElement.childNodes.item(i);
24993                 lastNode = '';
24994                 continue;
24995             }
24996             allText = false;
24997             
24998             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
24999                 
25000             // Recursively traverse the tree structure of the child node
25001             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25002             lastnode = currentElementChild.nodeName;
25003             i++;
25004             currentElementChild=currentElement.childNodes.item(i);
25005         }
25006         
25007         ret += innerHTML;
25008         
25009         if (!allText) {
25010                 // The remaining code is mostly for formatting the tree
25011             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25012         }
25013         
25014         
25015         if (tagName) {
25016             ret+= "</"+tagName+">";
25017         }
25018         return ret;
25019         
25020     },
25021         
25022     applyBlacklists : function()
25023     {
25024         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25025         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25026         
25027         this.white = [];
25028         this.black = [];
25029         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25030             if (b.indexOf(tag) > -1) {
25031                 return;
25032             }
25033             this.white.push(tag);
25034             
25035         }, this);
25036         
25037         Roo.each(w, function(tag) {
25038             if (b.indexOf(tag) > -1) {
25039                 return;
25040             }
25041             if (this.white.indexOf(tag) > -1) {
25042                 return;
25043             }
25044             this.white.push(tag);
25045             
25046         }, this);
25047         
25048         
25049         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25050             if (w.indexOf(tag) > -1) {
25051                 return;
25052             }
25053             this.black.push(tag);
25054             
25055         }, this);
25056         
25057         Roo.each(b, function(tag) {
25058             if (w.indexOf(tag) > -1) {
25059                 return;
25060             }
25061             if (this.black.indexOf(tag) > -1) {
25062                 return;
25063             }
25064             this.black.push(tag);
25065             
25066         }, this);
25067         
25068         
25069         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25070         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25071         
25072         this.cwhite = [];
25073         this.cblack = [];
25074         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25075             if (b.indexOf(tag) > -1) {
25076                 return;
25077             }
25078             this.cwhite.push(tag);
25079             
25080         }, this);
25081         
25082         Roo.each(w, function(tag) {
25083             if (b.indexOf(tag) > -1) {
25084                 return;
25085             }
25086             if (this.cwhite.indexOf(tag) > -1) {
25087                 return;
25088             }
25089             this.cwhite.push(tag);
25090             
25091         }, this);
25092         
25093         
25094         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25095             if (w.indexOf(tag) > -1) {
25096                 return;
25097             }
25098             this.cblack.push(tag);
25099             
25100         }, this);
25101         
25102         Roo.each(b, function(tag) {
25103             if (w.indexOf(tag) > -1) {
25104                 return;
25105             }
25106             if (this.cblack.indexOf(tag) > -1) {
25107                 return;
25108             }
25109             this.cblack.push(tag);
25110             
25111         }, this);
25112     },
25113     
25114     setStylesheets : function(stylesheets)
25115     {
25116         if(typeof(stylesheets) == 'string'){
25117             Roo.get(this.iframe.contentDocument.head).createChild({
25118                 tag : 'link',
25119                 rel : 'stylesheet',
25120                 type : 'text/css',
25121                 href : stylesheets
25122             });
25123             
25124             return;
25125         }
25126         var _this = this;
25127      
25128         Roo.each(stylesheets, function(s) {
25129             if(!s.length){
25130                 return;
25131             }
25132             
25133             Roo.get(_this.iframe.contentDocument.head).createChild({
25134                 tag : 'link',
25135                 rel : 'stylesheet',
25136                 type : 'text/css',
25137                 href : s
25138             });
25139         });
25140
25141         
25142     },
25143     
25144     removeStylesheets : function()
25145     {
25146         var _this = this;
25147         
25148         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25149             s.remove();
25150         });
25151     },
25152     
25153     setStyle : function(style)
25154     {
25155         Roo.get(this.iframe.contentDocument.head).createChild({
25156             tag : 'style',
25157             type : 'text/css',
25158             html : style
25159         });
25160
25161         return;
25162     }
25163     
25164     // hide stuff that is not compatible
25165     /**
25166      * @event blur
25167      * @hide
25168      */
25169     /**
25170      * @event change
25171      * @hide
25172      */
25173     /**
25174      * @event focus
25175      * @hide
25176      */
25177     /**
25178      * @event specialkey
25179      * @hide
25180      */
25181     /**
25182      * @cfg {String} fieldClass @hide
25183      */
25184     /**
25185      * @cfg {String} focusClass @hide
25186      */
25187     /**
25188      * @cfg {String} autoCreate @hide
25189      */
25190     /**
25191      * @cfg {String} inputType @hide
25192      */
25193     /**
25194      * @cfg {String} invalidClass @hide
25195      */
25196     /**
25197      * @cfg {String} invalidText @hide
25198      */
25199     /**
25200      * @cfg {String} msgFx @hide
25201      */
25202     /**
25203      * @cfg {String} validateOnBlur @hide
25204      */
25205 });
25206
25207 Roo.HtmlEditorCore.white = [
25208         'area', 'br', 'img', 'input', 'hr', 'wbr',
25209         
25210        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25211        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25212        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25213        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25214        'table',   'ul',         'xmp', 
25215        
25216        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25217       'thead',   'tr', 
25218      
25219       'dir', 'menu', 'ol', 'ul', 'dl',
25220        
25221       'embed',  'object'
25222 ];
25223
25224
25225 Roo.HtmlEditorCore.black = [
25226     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25227         'applet', // 
25228         'base',   'basefont', 'bgsound', 'blink',  'body', 
25229         'frame',  'frameset', 'head',    'html',   'ilayer', 
25230         'iframe', 'layer',  'link',     'meta',    'object',   
25231         'script', 'style' ,'title',  'xml' // clean later..
25232 ];
25233 Roo.HtmlEditorCore.clean = [
25234     'script', 'style', 'title', 'xml'
25235 ];
25236 Roo.HtmlEditorCore.remove = [
25237     'font'
25238 ];
25239 // attributes..
25240
25241 Roo.HtmlEditorCore.ablack = [
25242     'on'
25243 ];
25244     
25245 Roo.HtmlEditorCore.aclean = [ 
25246     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
25247 ];
25248
25249 // protocols..
25250 Roo.HtmlEditorCore.pwhite= [
25251         'http',  'https',  'mailto'
25252 ];
25253
25254 // white listed style attributes.
25255 Roo.HtmlEditorCore.cwhite= [
25256       //  'text-align', /// default is to allow most things..
25257       
25258          
25259 //        'font-size'//??
25260 ];
25261
25262 // black listed style attributes.
25263 Roo.HtmlEditorCore.cblack= [
25264       //  'font-size' -- this can be set by the project 
25265 ];
25266
25267
25268 Roo.HtmlEditorCore.swapCodes   =[ 
25269     [    8211, "--" ], 
25270     [    8212, "--" ], 
25271     [    8216,  "'" ],  
25272     [    8217, "'" ],  
25273     [    8220, '"' ],  
25274     [    8221, '"' ],  
25275     [    8226, "*" ],  
25276     [    8230, "..." ]
25277 ]; 
25278
25279     /*
25280  * - LGPL
25281  *
25282  * HtmlEditor
25283  * 
25284  */
25285
25286 /**
25287  * @class Roo.bootstrap.HtmlEditor
25288  * @extends Roo.bootstrap.TextArea
25289  * Bootstrap HtmlEditor class
25290
25291  * @constructor
25292  * Create a new HtmlEditor
25293  * @param {Object} config The config object
25294  */
25295
25296 Roo.bootstrap.HtmlEditor = function(config){
25297     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25298     if (!this.toolbars) {
25299         this.toolbars = [];
25300     }
25301     
25302     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25303     this.addEvents({
25304             /**
25305              * @event initialize
25306              * Fires when the editor is fully initialized (including the iframe)
25307              * @param {HtmlEditor} this
25308              */
25309             initialize: true,
25310             /**
25311              * @event activate
25312              * Fires when the editor is first receives the focus. Any insertion must wait
25313              * until after this event.
25314              * @param {HtmlEditor} this
25315              */
25316             activate: true,
25317              /**
25318              * @event beforesync
25319              * Fires before the textarea is updated with content from the editor iframe. Return false
25320              * to cancel the sync.
25321              * @param {HtmlEditor} this
25322              * @param {String} html
25323              */
25324             beforesync: true,
25325              /**
25326              * @event beforepush
25327              * Fires before the iframe editor is updated with content from the textarea. Return false
25328              * to cancel the push.
25329              * @param {HtmlEditor} this
25330              * @param {String} html
25331              */
25332             beforepush: true,
25333              /**
25334              * @event sync
25335              * Fires when the textarea is updated with content from the editor iframe.
25336              * @param {HtmlEditor} this
25337              * @param {String} html
25338              */
25339             sync: true,
25340              /**
25341              * @event push
25342              * Fires when the iframe editor is updated with content from the textarea.
25343              * @param {HtmlEditor} this
25344              * @param {String} html
25345              */
25346             push: true,
25347              /**
25348              * @event editmodechange
25349              * Fires when the editor switches edit modes
25350              * @param {HtmlEditor} this
25351              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25352              */
25353             editmodechange: true,
25354             /**
25355              * @event editorevent
25356              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25357              * @param {HtmlEditor} this
25358              */
25359             editorevent: true,
25360             /**
25361              * @event firstfocus
25362              * Fires when on first focus - needed by toolbars..
25363              * @param {HtmlEditor} this
25364              */
25365             firstfocus: true,
25366             /**
25367              * @event autosave
25368              * Auto save the htmlEditor value as a file into Events
25369              * @param {HtmlEditor} this
25370              */
25371             autosave: true,
25372             /**
25373              * @event savedpreview
25374              * preview the saved version of htmlEditor
25375              * @param {HtmlEditor} this
25376              */
25377             savedpreview: true
25378         });
25379 };
25380
25381
25382 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
25383     
25384     
25385       /**
25386      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25387      */
25388     toolbars : false,
25389     
25390      /**
25391     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25392     */
25393     btns : [],
25394    
25395      /**
25396      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25397      *                        Roo.resizable.
25398      */
25399     resizable : false,
25400      /**
25401      * @cfg {Number} height (in pixels)
25402      */   
25403     height: 300,
25404    /**
25405      * @cfg {Number} width (in pixels)
25406      */   
25407     width: false,
25408     
25409     /**
25410      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25411      * 
25412      */
25413     stylesheets: false,
25414     
25415     // id of frame..
25416     frameId: false,
25417     
25418     // private properties
25419     validationEvent : false,
25420     deferHeight: true,
25421     initialized : false,
25422     activated : false,
25423     
25424     onFocus : Roo.emptyFn,
25425     iframePad:3,
25426     hideMode:'offsets',
25427     
25428     tbContainer : false,
25429     
25430     bodyCls : '',
25431     
25432     toolbarContainer :function() {
25433         return this.wrap.select('.x-html-editor-tb',true).first();
25434     },
25435
25436     /**
25437      * Protected method that will not generally be called directly. It
25438      * is called when the editor creates its toolbar. Override this method if you need to
25439      * add custom toolbar buttons.
25440      * @param {HtmlEditor} editor
25441      */
25442     createToolbar : function(){
25443         Roo.log('renewing');
25444         Roo.log("create toolbars");
25445         
25446         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25447         this.toolbars[0].render(this.toolbarContainer());
25448         
25449         return;
25450         
25451 //        if (!editor.toolbars || !editor.toolbars.length) {
25452 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25453 //        }
25454 //        
25455 //        for (var i =0 ; i < editor.toolbars.length;i++) {
25456 //            editor.toolbars[i] = Roo.factory(
25457 //                    typeof(editor.toolbars[i]) == 'string' ?
25458 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
25459 //                Roo.bootstrap.HtmlEditor);
25460 //            editor.toolbars[i].init(editor);
25461 //        }
25462     },
25463
25464      
25465     // private
25466     onRender : function(ct, position)
25467     {
25468        // Roo.log("Call onRender: " + this.xtype);
25469         var _t = this;
25470         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25471       
25472         this.wrap = this.inputEl().wrap({
25473             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25474         });
25475         
25476         this.editorcore.onRender(ct, position);
25477          
25478         if (this.resizable) {
25479             this.resizeEl = new Roo.Resizable(this.wrap, {
25480                 pinned : true,
25481                 wrap: true,
25482                 dynamic : true,
25483                 minHeight : this.height,
25484                 height: this.height,
25485                 handles : this.resizable,
25486                 width: this.width,
25487                 listeners : {
25488                     resize : function(r, w, h) {
25489                         _t.onResize(w,h); // -something
25490                     }
25491                 }
25492             });
25493             
25494         }
25495         this.createToolbar(this);
25496        
25497         
25498         if(!this.width && this.resizable){
25499             this.setSize(this.wrap.getSize());
25500         }
25501         if (this.resizeEl) {
25502             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25503             // should trigger onReize..
25504         }
25505         
25506     },
25507
25508     // private
25509     onResize : function(w, h)
25510     {
25511         Roo.log('resize: ' +w + ',' + h );
25512         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
25513         var ew = false;
25514         var eh = false;
25515         
25516         if(this.inputEl() ){
25517             if(typeof w == 'number'){
25518                 var aw = w - this.wrap.getFrameWidth('lr');
25519                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
25520                 ew = aw;
25521             }
25522             if(typeof h == 'number'){
25523                  var tbh = -11;  // fixme it needs to tool bar size!
25524                 for (var i =0; i < this.toolbars.length;i++) {
25525                     // fixme - ask toolbars for heights?
25526                     tbh += this.toolbars[i].el.getHeight();
25527                     //if (this.toolbars[i].footer) {
25528                     //    tbh += this.toolbars[i].footer.el.getHeight();
25529                     //}
25530                 }
25531               
25532                 
25533                 
25534                 
25535                 
25536                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25537                 ah -= 5; // knock a few pixes off for look..
25538                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
25539                 var eh = ah;
25540             }
25541         }
25542         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
25543         this.editorcore.onResize(ew,eh);
25544         
25545     },
25546
25547     /**
25548      * Toggles the editor between standard and source edit mode.
25549      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25550      */
25551     toggleSourceEdit : function(sourceEditMode)
25552     {
25553         this.editorcore.toggleSourceEdit(sourceEditMode);
25554         
25555         if(this.editorcore.sourceEditMode){
25556             Roo.log('editor - showing textarea');
25557             
25558 //            Roo.log('in');
25559 //            Roo.log(this.syncValue());
25560             this.syncValue();
25561             this.inputEl().removeClass(['hide', 'x-hidden']);
25562             this.inputEl().dom.removeAttribute('tabIndex');
25563             this.inputEl().focus();
25564         }else{
25565             Roo.log('editor - hiding textarea');
25566 //            Roo.log('out')
25567 //            Roo.log(this.pushValue()); 
25568             this.pushValue();
25569             
25570             this.inputEl().addClass(['hide', 'x-hidden']);
25571             this.inputEl().dom.setAttribute('tabIndex', -1);
25572             //this.deferFocus();
25573         }
25574          
25575         if(this.resizable){
25576             this.setSize(this.wrap.getSize());
25577         }
25578         
25579         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
25580     },
25581  
25582     // private (for BoxComponent)
25583     adjustSize : Roo.BoxComponent.prototype.adjustSize,
25584
25585     // private (for BoxComponent)
25586     getResizeEl : function(){
25587         return this.wrap;
25588     },
25589
25590     // private (for BoxComponent)
25591     getPositionEl : function(){
25592         return this.wrap;
25593     },
25594
25595     // private
25596     initEvents : function(){
25597         this.originalValue = this.getValue();
25598     },
25599
25600 //    /**
25601 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25602 //     * @method
25603 //     */
25604 //    markInvalid : Roo.emptyFn,
25605 //    /**
25606 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25607 //     * @method
25608 //     */
25609 //    clearInvalid : Roo.emptyFn,
25610
25611     setValue : function(v){
25612         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
25613         this.editorcore.pushValue();
25614     },
25615
25616      
25617     // private
25618     deferFocus : function(){
25619         this.focus.defer(10, this);
25620     },
25621
25622     // doc'ed in Field
25623     focus : function(){
25624         this.editorcore.focus();
25625         
25626     },
25627       
25628
25629     // private
25630     onDestroy : function(){
25631         
25632         
25633         
25634         if(this.rendered){
25635             
25636             for (var i =0; i < this.toolbars.length;i++) {
25637                 // fixme - ask toolbars for heights?
25638                 this.toolbars[i].onDestroy();
25639             }
25640             
25641             this.wrap.dom.innerHTML = '';
25642             this.wrap.remove();
25643         }
25644     },
25645
25646     // private
25647     onFirstFocus : function(){
25648         //Roo.log("onFirstFocus");
25649         this.editorcore.onFirstFocus();
25650          for (var i =0; i < this.toolbars.length;i++) {
25651             this.toolbars[i].onFirstFocus();
25652         }
25653         
25654     },
25655     
25656     // private
25657     syncValue : function()
25658     {   
25659         this.editorcore.syncValue();
25660     },
25661     
25662     pushValue : function()
25663     {   
25664         this.editorcore.pushValue();
25665     }
25666      
25667     
25668     // hide stuff that is not compatible
25669     /**
25670      * @event blur
25671      * @hide
25672      */
25673     /**
25674      * @event change
25675      * @hide
25676      */
25677     /**
25678      * @event focus
25679      * @hide
25680      */
25681     /**
25682      * @event specialkey
25683      * @hide
25684      */
25685     /**
25686      * @cfg {String} fieldClass @hide
25687      */
25688     /**
25689      * @cfg {String} focusClass @hide
25690      */
25691     /**
25692      * @cfg {String} autoCreate @hide
25693      */
25694     /**
25695      * @cfg {String} inputType @hide
25696      */
25697      
25698     /**
25699      * @cfg {String} invalidText @hide
25700      */
25701     /**
25702      * @cfg {String} msgFx @hide
25703      */
25704     /**
25705      * @cfg {String} validateOnBlur @hide
25706      */
25707 });
25708  
25709     
25710    
25711    
25712    
25713       
25714 Roo.namespace('Roo.bootstrap.htmleditor');
25715 /**
25716  * @class Roo.bootstrap.HtmlEditorToolbar1
25717  * Basic Toolbar
25718  * 
25719  * @example
25720  * Usage:
25721  *
25722  new Roo.bootstrap.HtmlEditor({
25723     ....
25724     toolbars : [
25725         new Roo.bootstrap.HtmlEditorToolbar1({
25726             disable : { fonts: 1 , format: 1, ..., ... , ...],
25727             btns : [ .... ]
25728         })
25729     }
25730      
25731  * 
25732  * @cfg {Object} disable List of elements to disable..
25733  * @cfg {Array} btns List of additional buttons.
25734  * 
25735  * 
25736  * NEEDS Extra CSS? 
25737  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
25738  */
25739  
25740 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
25741 {
25742     
25743     Roo.apply(this, config);
25744     
25745     // default disabled, based on 'good practice'..
25746     this.disable = this.disable || {};
25747     Roo.applyIf(this.disable, {
25748         fontSize : true,
25749         colors : true,
25750         specialElements : true
25751     });
25752     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
25753     
25754     this.editor = config.editor;
25755     this.editorcore = config.editor.editorcore;
25756     
25757     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
25758     
25759     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25760     // dont call parent... till later.
25761 }
25762 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
25763      
25764     bar : true,
25765     
25766     editor : false,
25767     editorcore : false,
25768     
25769     
25770     formats : [
25771         "p" ,  
25772         "h1","h2","h3","h4","h5","h6", 
25773         "pre", "code", 
25774         "abbr", "acronym", "address", "cite", "samp", "var",
25775         'div','span'
25776     ],
25777     
25778     onRender : function(ct, position)
25779     {
25780        // Roo.log("Call onRender: " + this.xtype);
25781         
25782        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
25783        Roo.log(this.el);
25784        this.el.dom.style.marginBottom = '0';
25785        var _this = this;
25786        var editorcore = this.editorcore;
25787        var editor= this.editor;
25788        
25789        var children = [];
25790        var btn = function(id,cmd , toggle, handler, html){
25791        
25792             var  event = toggle ? 'toggle' : 'click';
25793        
25794             var a = {
25795                 size : 'sm',
25796                 xtype: 'Button',
25797                 xns: Roo.bootstrap,
25798                 //glyphicon : id,
25799                 fa: id,
25800                 cmd : id || cmd,
25801                 enableToggle:toggle !== false,
25802                 html : html || '',
25803                 pressed : toggle ? false : null,
25804                 listeners : {}
25805             };
25806             a.listeners[toggle ? 'toggle' : 'click'] = function() {
25807                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
25808             };
25809             children.push(a);
25810             return a;
25811        }
25812        
25813     //    var cb_box = function...
25814         
25815         var style = {
25816                 xtype: 'Button',
25817                 size : 'sm',
25818                 xns: Roo.bootstrap,
25819                 fa : 'font',
25820                 //html : 'submit'
25821                 menu : {
25822                     xtype: 'Menu',
25823                     xns: Roo.bootstrap,
25824                     items:  []
25825                 }
25826         };
25827         Roo.each(this.formats, function(f) {
25828             style.menu.items.push({
25829                 xtype :'MenuItem',
25830                 xns: Roo.bootstrap,
25831                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
25832                 tagname : f,
25833                 listeners : {
25834                     click : function()
25835                     {
25836                         editorcore.insertTag(this.tagname);
25837                         editor.focus();
25838                     }
25839                 }
25840                 
25841             });
25842         });
25843         children.push(style);   
25844         
25845         btn('bold',false,true);
25846         btn('italic',false,true);
25847         btn('align-left', 'justifyleft',true);
25848         btn('align-center', 'justifycenter',true);
25849         btn('align-right' , 'justifyright',true);
25850         btn('link', false, false, function(btn) {
25851             //Roo.log("create link?");
25852             var url = prompt(this.createLinkText, this.defaultLinkValue);
25853             if(url && url != 'http:/'+'/'){
25854                 this.editorcore.relayCmd('createlink', url);
25855             }
25856         }),
25857         btn('list','insertunorderedlist',true);
25858         btn('pencil', false,true, function(btn){
25859                 Roo.log(this);
25860                 this.toggleSourceEdit(btn.pressed);
25861         });
25862         
25863         if (this.editor.btns.length > 0) {
25864             for (var i = 0; i<this.editor.btns.length; i++) {
25865                 children.push(this.editor.btns[i]);
25866             }
25867         }
25868         
25869         /*
25870         var cog = {
25871                 xtype: 'Button',
25872                 size : 'sm',
25873                 xns: Roo.bootstrap,
25874                 glyphicon : 'cog',
25875                 //html : 'submit'
25876                 menu : {
25877                     xtype: 'Menu',
25878                     xns: Roo.bootstrap,
25879                     items:  []
25880                 }
25881         };
25882         
25883         cog.menu.items.push({
25884             xtype :'MenuItem',
25885             xns: Roo.bootstrap,
25886             html : Clean styles,
25887             tagname : f,
25888             listeners : {
25889                 click : function()
25890                 {
25891                     editorcore.insertTag(this.tagname);
25892                     editor.focus();
25893                 }
25894             }
25895             
25896         });
25897        */
25898         
25899          
25900        this.xtype = 'NavSimplebar';
25901         
25902         for(var i=0;i< children.length;i++) {
25903             
25904             this.buttons.add(this.addxtypeChild(children[i]));
25905             
25906         }
25907         
25908         editor.on('editorevent', this.updateToolbar, this);
25909     },
25910     onBtnClick : function(id)
25911     {
25912        this.editorcore.relayCmd(id);
25913        this.editorcore.focus();
25914     },
25915     
25916     /**
25917      * Protected method that will not generally be called directly. It triggers
25918      * a toolbar update by reading the markup state of the current selection in the editor.
25919      */
25920     updateToolbar: function(){
25921
25922         if(!this.editorcore.activated){
25923             this.editor.onFirstFocus(); // is this neeed?
25924             return;
25925         }
25926
25927         var btns = this.buttons; 
25928         var doc = this.editorcore.doc;
25929         btns.get('bold').setActive(doc.queryCommandState('bold'));
25930         btns.get('italic').setActive(doc.queryCommandState('italic'));
25931         //btns.get('underline').setActive(doc.queryCommandState('underline'));
25932         
25933         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
25934         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
25935         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
25936         
25937         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
25938         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
25939          /*
25940         
25941         var ans = this.editorcore.getAllAncestors();
25942         if (this.formatCombo) {
25943             
25944             
25945             var store = this.formatCombo.store;
25946             this.formatCombo.setValue("");
25947             for (var i =0; i < ans.length;i++) {
25948                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
25949                     // select it..
25950                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
25951                     break;
25952                 }
25953             }
25954         }
25955         
25956         
25957         
25958         // hides menus... - so this cant be on a menu...
25959         Roo.bootstrap.MenuMgr.hideAll();
25960         */
25961         Roo.bootstrap.MenuMgr.hideAll();
25962         //this.editorsyncValue();
25963     },
25964     onFirstFocus: function() {
25965         this.buttons.each(function(item){
25966            item.enable();
25967         });
25968     },
25969     toggleSourceEdit : function(sourceEditMode){
25970         
25971           
25972         if(sourceEditMode){
25973             Roo.log("disabling buttons");
25974            this.buttons.each( function(item){
25975                 if(item.cmd != 'pencil'){
25976                     item.disable();
25977                 }
25978             });
25979           
25980         }else{
25981             Roo.log("enabling buttons");
25982             if(this.editorcore.initialized){
25983                 this.buttons.each( function(item){
25984                     item.enable();
25985                 });
25986             }
25987             
25988         }
25989         Roo.log("calling toggole on editor");
25990         // tell the editor that it's been pressed..
25991         this.editor.toggleSourceEdit(sourceEditMode);
25992        
25993     }
25994 });
25995
25996
25997
25998
25999
26000 /**
26001  * @class Roo.bootstrap.Table.AbstractSelectionModel
26002  * @extends Roo.util.Observable
26003  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26004  * implemented by descendant classes.  This class should not be directly instantiated.
26005  * @constructor
26006  */
26007 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26008     this.locked = false;
26009     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26010 };
26011
26012
26013 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26014     /** @ignore Called by the grid automatically. Do not call directly. */
26015     init : function(grid){
26016         this.grid = grid;
26017         this.initEvents();
26018     },
26019
26020     /**
26021      * Locks the selections.
26022      */
26023     lock : function(){
26024         this.locked = true;
26025     },
26026
26027     /**
26028      * Unlocks the selections.
26029      */
26030     unlock : function(){
26031         this.locked = false;
26032     },
26033
26034     /**
26035      * Returns true if the selections are locked.
26036      * @return {Boolean}
26037      */
26038     isLocked : function(){
26039         return this.locked;
26040     },
26041     
26042     
26043     initEvents : function ()
26044     {
26045         
26046     }
26047 });
26048 /**
26049  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26050  * @class Roo.bootstrap.Table.RowSelectionModel
26051  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26052  * It supports multiple selections and keyboard selection/navigation. 
26053  * @constructor
26054  * @param {Object} config
26055  */
26056
26057 Roo.bootstrap.Table.RowSelectionModel = function(config){
26058     Roo.apply(this, config);
26059     this.selections = new Roo.util.MixedCollection(false, function(o){
26060         return o.id;
26061     });
26062
26063     this.last = false;
26064     this.lastActive = false;
26065
26066     this.addEvents({
26067         /**
26068              * @event selectionchange
26069              * Fires when the selection changes
26070              * @param {SelectionModel} this
26071              */
26072             "selectionchange" : true,
26073         /**
26074              * @event afterselectionchange
26075              * Fires after the selection changes (eg. by key press or clicking)
26076              * @param {SelectionModel} this
26077              */
26078             "afterselectionchange" : true,
26079         /**
26080              * @event beforerowselect
26081              * Fires when a row is selected being selected, return false to cancel.
26082              * @param {SelectionModel} this
26083              * @param {Number} rowIndex The selected index
26084              * @param {Boolean} keepExisting False if other selections will be cleared
26085              */
26086             "beforerowselect" : true,
26087         /**
26088              * @event rowselect
26089              * Fires when a row is selected.
26090              * @param {SelectionModel} this
26091              * @param {Number} rowIndex The selected index
26092              * @param {Roo.data.Record} r The record
26093              */
26094             "rowselect" : true,
26095         /**
26096              * @event rowdeselect
26097              * Fires when a row is deselected.
26098              * @param {SelectionModel} this
26099              * @param {Number} rowIndex The selected index
26100              */
26101         "rowdeselect" : true
26102     });
26103     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26104     this.locked = false;
26105  };
26106
26107 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
26108     /**
26109      * @cfg {Boolean} singleSelect
26110      * True to allow selection of only one row at a time (defaults to false)
26111      */
26112     singleSelect : false,
26113
26114     // private
26115     initEvents : function()
26116     {
26117
26118         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26119         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
26120         //}else{ // allow click to work like normal
26121          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
26122         //}
26123         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26124         this.grid.on("rowclick", this.handleMouseDown, this);
26125         
26126         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26127             "up" : function(e){
26128                 if(!e.shiftKey){
26129                     this.selectPrevious(e.shiftKey);
26130                 }else if(this.last !== false && this.lastActive !== false){
26131                     var last = this.last;
26132                     this.selectRange(this.last,  this.lastActive-1);
26133                     this.grid.getView().focusRow(this.lastActive);
26134                     if(last !== false){
26135                         this.last = last;
26136                     }
26137                 }else{
26138                     this.selectFirstRow();
26139                 }
26140                 this.fireEvent("afterselectionchange", this);
26141             },
26142             "down" : function(e){
26143                 if(!e.shiftKey){
26144                     this.selectNext(e.shiftKey);
26145                 }else if(this.last !== false && this.lastActive !== false){
26146                     var last = this.last;
26147                     this.selectRange(this.last,  this.lastActive+1);
26148                     this.grid.getView().focusRow(this.lastActive);
26149                     if(last !== false){
26150                         this.last = last;
26151                     }
26152                 }else{
26153                     this.selectFirstRow();
26154                 }
26155                 this.fireEvent("afterselectionchange", this);
26156             },
26157             scope: this
26158         });
26159         this.grid.store.on('load', function(){
26160             this.selections.clear();
26161         },this);
26162         /*
26163         var view = this.grid.view;
26164         view.on("refresh", this.onRefresh, this);
26165         view.on("rowupdated", this.onRowUpdated, this);
26166         view.on("rowremoved", this.onRemove, this);
26167         */
26168     },
26169
26170     // private
26171     onRefresh : function()
26172     {
26173         var ds = this.grid.store, i, v = this.grid.view;
26174         var s = this.selections;
26175         s.each(function(r){
26176             if((i = ds.indexOfId(r.id)) != -1){
26177                 v.onRowSelect(i);
26178             }else{
26179                 s.remove(r);
26180             }
26181         });
26182     },
26183
26184     // private
26185     onRemove : function(v, index, r){
26186         this.selections.remove(r);
26187     },
26188
26189     // private
26190     onRowUpdated : function(v, index, r){
26191         if(this.isSelected(r)){
26192             v.onRowSelect(index);
26193         }
26194     },
26195
26196     /**
26197      * Select records.
26198      * @param {Array} records The records to select
26199      * @param {Boolean} keepExisting (optional) True to keep existing selections
26200      */
26201     selectRecords : function(records, keepExisting)
26202     {
26203         if(!keepExisting){
26204             this.clearSelections();
26205         }
26206             var ds = this.grid.store;
26207         for(var i = 0, len = records.length; i < len; i++){
26208             this.selectRow(ds.indexOf(records[i]), true);
26209         }
26210     },
26211
26212     /**
26213      * Gets the number of selected rows.
26214      * @return {Number}
26215      */
26216     getCount : function(){
26217         return this.selections.length;
26218     },
26219
26220     /**
26221      * Selects the first row in the grid.
26222      */
26223     selectFirstRow : function(){
26224         this.selectRow(0);
26225     },
26226
26227     /**
26228      * Select the last row.
26229      * @param {Boolean} keepExisting (optional) True to keep existing selections
26230      */
26231     selectLastRow : function(keepExisting){
26232         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26233         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26234     },
26235
26236     /**
26237      * Selects the row immediately following the last selected row.
26238      * @param {Boolean} keepExisting (optional) True to keep existing selections
26239      */
26240     selectNext : function(keepExisting)
26241     {
26242             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26243             this.selectRow(this.last+1, keepExisting);
26244             this.grid.getView().focusRow(this.last);
26245         }
26246     },
26247
26248     /**
26249      * Selects the row that precedes the last selected row.
26250      * @param {Boolean} keepExisting (optional) True to keep existing selections
26251      */
26252     selectPrevious : function(keepExisting){
26253         if(this.last){
26254             this.selectRow(this.last-1, keepExisting);
26255             this.grid.getView().focusRow(this.last);
26256         }
26257     },
26258
26259     /**
26260      * Returns the selected records
26261      * @return {Array} Array of selected records
26262      */
26263     getSelections : function(){
26264         return [].concat(this.selections.items);
26265     },
26266
26267     /**
26268      * Returns the first selected record.
26269      * @return {Record}
26270      */
26271     getSelected : function(){
26272         return this.selections.itemAt(0);
26273     },
26274
26275
26276     /**
26277      * Clears all selections.
26278      */
26279     clearSelections : function(fast)
26280     {
26281         if(this.locked) {
26282             return;
26283         }
26284         if(fast !== true){
26285                 var ds = this.grid.store;
26286             var s = this.selections;
26287             s.each(function(r){
26288                 this.deselectRow(ds.indexOfId(r.id));
26289             }, this);
26290             s.clear();
26291         }else{
26292             this.selections.clear();
26293         }
26294         this.last = false;
26295     },
26296
26297
26298     /**
26299      * Selects all rows.
26300      */
26301     selectAll : function(){
26302         if(this.locked) {
26303             return;
26304         }
26305         this.selections.clear();
26306         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26307             this.selectRow(i, true);
26308         }
26309     },
26310
26311     /**
26312      * Returns True if there is a selection.
26313      * @return {Boolean}
26314      */
26315     hasSelection : function(){
26316         return this.selections.length > 0;
26317     },
26318
26319     /**
26320      * Returns True if the specified row is selected.
26321      * @param {Number/Record} record The record or index of the record to check
26322      * @return {Boolean}
26323      */
26324     isSelected : function(index){
26325             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26326         return (r && this.selections.key(r.id) ? true : false);
26327     },
26328
26329     /**
26330      * Returns True if the specified record id is selected.
26331      * @param {String} id The id of record to check
26332      * @return {Boolean}
26333      */
26334     isIdSelected : function(id){
26335         return (this.selections.key(id) ? true : false);
26336     },
26337
26338
26339     // private
26340     handleMouseDBClick : function(e, t){
26341         
26342     },
26343     // private
26344     handleMouseDown : function(e, t)
26345     {
26346             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26347         if(this.isLocked() || rowIndex < 0 ){
26348             return;
26349         };
26350         if(e.shiftKey && this.last !== false){
26351             var last = this.last;
26352             this.selectRange(last, rowIndex, e.ctrlKey);
26353             this.last = last; // reset the last
26354             t.focus();
26355     
26356         }else{
26357             var isSelected = this.isSelected(rowIndex);
26358             //Roo.log("select row:" + rowIndex);
26359             if(isSelected){
26360                 this.deselectRow(rowIndex);
26361             } else {
26362                         this.selectRow(rowIndex, true);
26363             }
26364     
26365             /*
26366                 if(e.button !== 0 && isSelected){
26367                 alert('rowIndex 2: ' + rowIndex);
26368                     view.focusRow(rowIndex);
26369                 }else if(e.ctrlKey && isSelected){
26370                     this.deselectRow(rowIndex);
26371                 }else if(!isSelected){
26372                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26373                     view.focusRow(rowIndex);
26374                 }
26375             */
26376         }
26377         this.fireEvent("afterselectionchange", this);
26378     },
26379     // private
26380     handleDragableRowClick :  function(grid, rowIndex, e) 
26381     {
26382         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26383             this.selectRow(rowIndex, false);
26384             grid.view.focusRow(rowIndex);
26385              this.fireEvent("afterselectionchange", this);
26386         }
26387     },
26388     
26389     /**
26390      * Selects multiple rows.
26391      * @param {Array} rows Array of the indexes of the row to select
26392      * @param {Boolean} keepExisting (optional) True to keep existing selections
26393      */
26394     selectRows : function(rows, keepExisting){
26395         if(!keepExisting){
26396             this.clearSelections();
26397         }
26398         for(var i = 0, len = rows.length; i < len; i++){
26399             this.selectRow(rows[i], true);
26400         }
26401     },
26402
26403     /**
26404      * Selects a range of rows. All rows in between startRow and endRow are also selected.
26405      * @param {Number} startRow The index of the first row in the range
26406      * @param {Number} endRow The index of the last row in the range
26407      * @param {Boolean} keepExisting (optional) True to retain existing selections
26408      */
26409     selectRange : function(startRow, endRow, keepExisting){
26410         if(this.locked) {
26411             return;
26412         }
26413         if(!keepExisting){
26414             this.clearSelections();
26415         }
26416         if(startRow <= endRow){
26417             for(var i = startRow; i <= endRow; i++){
26418                 this.selectRow(i, true);
26419             }
26420         }else{
26421             for(var i = startRow; i >= endRow; i--){
26422                 this.selectRow(i, true);
26423             }
26424         }
26425     },
26426
26427     /**
26428      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
26429      * @param {Number} startRow The index of the first row in the range
26430      * @param {Number} endRow The index of the last row in the range
26431      */
26432     deselectRange : function(startRow, endRow, preventViewNotify){
26433         if(this.locked) {
26434             return;
26435         }
26436         for(var i = startRow; i <= endRow; i++){
26437             this.deselectRow(i, preventViewNotify);
26438         }
26439     },
26440
26441     /**
26442      * Selects a row.
26443      * @param {Number} row The index of the row to select
26444      * @param {Boolean} keepExisting (optional) True to keep existing selections
26445      */
26446     selectRow : function(index, keepExisting, preventViewNotify)
26447     {
26448             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
26449             return;
26450         }
26451         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
26452             if(!keepExisting || this.singleSelect){
26453                 this.clearSelections();
26454             }
26455             
26456             var r = this.grid.store.getAt(index);
26457             //console.log('selectRow - record id :' + r.id);
26458             
26459             this.selections.add(r);
26460             this.last = this.lastActive = index;
26461             if(!preventViewNotify){
26462                 var proxy = new Roo.Element(
26463                                 this.grid.getRowDom(index)
26464                 );
26465                 proxy.addClass('bg-info info');
26466             }
26467             this.fireEvent("rowselect", this, index, r);
26468             this.fireEvent("selectionchange", this);
26469         }
26470     },
26471
26472     /**
26473      * Deselects a row.
26474      * @param {Number} row The index of the row to deselect
26475      */
26476     deselectRow : function(index, preventViewNotify)
26477     {
26478         if(this.locked) {
26479             return;
26480         }
26481         if(this.last == index){
26482             this.last = false;
26483         }
26484         if(this.lastActive == index){
26485             this.lastActive = false;
26486         }
26487         
26488         var r = this.grid.store.getAt(index);
26489         if (!r) {
26490             return;
26491         }
26492         
26493         this.selections.remove(r);
26494         //.console.log('deselectRow - record id :' + r.id);
26495         if(!preventViewNotify){
26496         
26497             var proxy = new Roo.Element(
26498                 this.grid.getRowDom(index)
26499             );
26500             proxy.removeClass('bg-info info');
26501         }
26502         this.fireEvent("rowdeselect", this, index);
26503         this.fireEvent("selectionchange", this);
26504     },
26505
26506     // private
26507     restoreLast : function(){
26508         if(this._last){
26509             this.last = this._last;
26510         }
26511     },
26512
26513     // private
26514     acceptsNav : function(row, col, cm){
26515         return !cm.isHidden(col) && cm.isCellEditable(col, row);
26516     },
26517
26518     // private
26519     onEditorKey : function(field, e){
26520         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
26521         if(k == e.TAB){
26522             e.stopEvent();
26523             ed.completeEdit();
26524             if(e.shiftKey){
26525                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
26526             }else{
26527                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
26528             }
26529         }else if(k == e.ENTER && !e.ctrlKey){
26530             e.stopEvent();
26531             ed.completeEdit();
26532             if(e.shiftKey){
26533                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
26534             }else{
26535                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
26536             }
26537         }else if(k == e.ESC){
26538             ed.cancelEdit();
26539         }
26540         if(newCell){
26541             g.startEditing(newCell[0], newCell[1]);
26542         }
26543     }
26544 });
26545 /*
26546  * Based on:
26547  * Ext JS Library 1.1.1
26548  * Copyright(c) 2006-2007, Ext JS, LLC.
26549  *
26550  * Originally Released Under LGPL - original licence link has changed is not relivant.
26551  *
26552  * Fork - LGPL
26553  * <script type="text/javascript">
26554  */
26555  
26556 /**
26557  * @class Roo.bootstrap.PagingToolbar
26558  * @extends Roo.bootstrap.NavSimplebar
26559  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
26560  * @constructor
26561  * Create a new PagingToolbar
26562  * @param {Object} config The config object
26563  * @param {Roo.data.Store} store
26564  */
26565 Roo.bootstrap.PagingToolbar = function(config)
26566 {
26567     // old args format still supported... - xtype is prefered..
26568         // created from xtype...
26569     
26570     this.ds = config.dataSource;
26571     
26572     if (config.store && !this.ds) {
26573         this.store= Roo.factory(config.store, Roo.data);
26574         this.ds = this.store;
26575         this.ds.xmodule = this.xmodule || false;
26576     }
26577     
26578     this.toolbarItems = [];
26579     if (config.items) {
26580         this.toolbarItems = config.items;
26581     }
26582     
26583     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
26584     
26585     this.cursor = 0;
26586     
26587     if (this.ds) { 
26588         this.bind(this.ds);
26589     }
26590     
26591     if (Roo.bootstrap.version == 4) {
26592         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
26593     } else {
26594         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
26595     }
26596     
26597 };
26598
26599 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
26600     /**
26601      * @cfg {Roo.data.Store} dataSource
26602      * The underlying data store providing the paged data
26603      */
26604     /**
26605      * @cfg {String/HTMLElement/Element} container
26606      * container The id or element that will contain the toolbar
26607      */
26608     /**
26609      * @cfg {Boolean} displayInfo
26610      * True to display the displayMsg (defaults to false)
26611      */
26612     /**
26613      * @cfg {Number} pageSize
26614      * The number of records to display per page (defaults to 20)
26615      */
26616     pageSize: 20,
26617     /**
26618      * @cfg {String} displayMsg
26619      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
26620      */
26621     displayMsg : 'Displaying {0} - {1} of {2}',
26622     /**
26623      * @cfg {String} emptyMsg
26624      * The message to display when no records are found (defaults to "No data to display")
26625      */
26626     emptyMsg : 'No data to display',
26627     /**
26628      * Customizable piece of the default paging text (defaults to "Page")
26629      * @type String
26630      */
26631     beforePageText : "Page",
26632     /**
26633      * Customizable piece of the default paging text (defaults to "of %0")
26634      * @type String
26635      */
26636     afterPageText : "of {0}",
26637     /**
26638      * Customizable piece of the default paging text (defaults to "First Page")
26639      * @type String
26640      */
26641     firstText : "First Page",
26642     /**
26643      * Customizable piece of the default paging text (defaults to "Previous Page")
26644      * @type String
26645      */
26646     prevText : "Previous Page",
26647     /**
26648      * Customizable piece of the default paging text (defaults to "Next Page")
26649      * @type String
26650      */
26651     nextText : "Next Page",
26652     /**
26653      * Customizable piece of the default paging text (defaults to "Last Page")
26654      * @type String
26655      */
26656     lastText : "Last Page",
26657     /**
26658      * Customizable piece of the default paging text (defaults to "Refresh")
26659      * @type String
26660      */
26661     refreshText : "Refresh",
26662
26663     buttons : false,
26664     // private
26665     onRender : function(ct, position) 
26666     {
26667         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
26668         this.navgroup.parentId = this.id;
26669         this.navgroup.onRender(this.el, null);
26670         // add the buttons to the navgroup
26671         
26672         if(this.displayInfo){
26673             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
26674             this.displayEl = this.el.select('.x-paging-info', true).first();
26675 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
26676 //            this.displayEl = navel.el.select('span',true).first();
26677         }
26678         
26679         var _this = this;
26680         
26681         if(this.buttons){
26682             Roo.each(_this.buttons, function(e){ // this might need to use render????
26683                Roo.factory(e).render(_this.el);
26684             });
26685         }
26686             
26687         Roo.each(_this.toolbarItems, function(e) {
26688             _this.navgroup.addItem(e);
26689         });
26690         
26691         
26692         this.first = this.navgroup.addItem({
26693             tooltip: this.firstText,
26694             cls: "prev btn-outline-secondary",
26695             html : ' <i class="fa fa-step-backward"></i>',
26696             disabled: true,
26697             preventDefault: true,
26698             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
26699         });
26700         
26701         this.prev =  this.navgroup.addItem({
26702             tooltip: this.prevText,
26703             cls: "prev btn-outline-secondary",
26704             html : ' <i class="fa fa-backward"></i>',
26705             disabled: true,
26706             preventDefault: true,
26707             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
26708         });
26709     //this.addSeparator();
26710         
26711         
26712         var field = this.navgroup.addItem( {
26713             tagtype : 'span',
26714             cls : 'x-paging-position  btn-outline-secondary',
26715              disabled: true,
26716             html : this.beforePageText  +
26717                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
26718                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
26719          } ); //?? escaped?
26720         
26721         this.field = field.el.select('input', true).first();
26722         this.field.on("keydown", this.onPagingKeydown, this);
26723         this.field.on("focus", function(){this.dom.select();});
26724     
26725     
26726         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
26727         //this.field.setHeight(18);
26728         //this.addSeparator();
26729         this.next = this.navgroup.addItem({
26730             tooltip: this.nextText,
26731             cls: "next btn-outline-secondary",
26732             html : ' <i class="fa fa-forward"></i>',
26733             disabled: true,
26734             preventDefault: true,
26735             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
26736         });
26737         this.last = this.navgroup.addItem({
26738             tooltip: this.lastText,
26739             html : ' <i class="fa fa-step-forward"></i>',
26740             cls: "next btn-outline-secondary",
26741             disabled: true,
26742             preventDefault: true,
26743             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
26744         });
26745     //this.addSeparator();
26746         this.loading = this.navgroup.addItem({
26747             tooltip: this.refreshText,
26748             cls: "btn-outline-secondary",
26749             html : ' <i class="fa fa-refresh"></i>',
26750             preventDefault: true,
26751             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
26752         });
26753         
26754     },
26755
26756     // private
26757     updateInfo : function(){
26758         if(this.displayEl){
26759             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
26760             var msg = count == 0 ?
26761                 this.emptyMsg :
26762                 String.format(
26763                     this.displayMsg,
26764                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
26765                 );
26766             this.displayEl.update(msg);
26767         }
26768     },
26769
26770     // private
26771     onLoad : function(ds, r, o)
26772     {
26773         this.cursor = o.params.start ? o.params.start : 0;
26774         
26775         var d = this.getPageData(),
26776             ap = d.activePage,
26777             ps = d.pages;
26778         
26779         
26780         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
26781         this.field.dom.value = ap;
26782         this.first.setDisabled(ap == 1);
26783         this.prev.setDisabled(ap == 1);
26784         this.next.setDisabled(ap == ps);
26785         this.last.setDisabled(ap == ps);
26786         this.loading.enable();
26787         this.updateInfo();
26788     },
26789
26790     // private
26791     getPageData : function(){
26792         var total = this.ds.getTotalCount();
26793         return {
26794             total : total,
26795             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
26796             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
26797         };
26798     },
26799
26800     // private
26801     onLoadError : function(){
26802         this.loading.enable();
26803     },
26804
26805     // private
26806     onPagingKeydown : function(e){
26807         var k = e.getKey();
26808         var d = this.getPageData();
26809         if(k == e.RETURN){
26810             var v = this.field.dom.value, pageNum;
26811             if(!v || isNaN(pageNum = parseInt(v, 10))){
26812                 this.field.dom.value = d.activePage;
26813                 return;
26814             }
26815             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
26816             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
26817             e.stopEvent();
26818         }
26819         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))
26820         {
26821           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
26822           this.field.dom.value = pageNum;
26823           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
26824           e.stopEvent();
26825         }
26826         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
26827         {
26828           var v = this.field.dom.value, pageNum; 
26829           var increment = (e.shiftKey) ? 10 : 1;
26830           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
26831                 increment *= -1;
26832           }
26833           if(!v || isNaN(pageNum = parseInt(v, 10))) {
26834             this.field.dom.value = d.activePage;
26835             return;
26836           }
26837           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
26838           {
26839             this.field.dom.value = parseInt(v, 10) + increment;
26840             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
26841             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
26842           }
26843           e.stopEvent();
26844         }
26845     },
26846
26847     // private
26848     beforeLoad : function(){
26849         if(this.loading){
26850             this.loading.disable();
26851         }
26852     },
26853
26854     // private
26855     onClick : function(which){
26856         
26857         var ds = this.ds;
26858         if (!ds) {
26859             return;
26860         }
26861         
26862         switch(which){
26863             case "first":
26864                 ds.load({params:{start: 0, limit: this.pageSize}});
26865             break;
26866             case "prev":
26867                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
26868             break;
26869             case "next":
26870                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
26871             break;
26872             case "last":
26873                 var total = ds.getTotalCount();
26874                 var extra = total % this.pageSize;
26875                 var lastStart = extra ? (total - extra) : total-this.pageSize;
26876                 ds.load({params:{start: lastStart, limit: this.pageSize}});
26877             break;
26878             case "refresh":
26879                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
26880             break;
26881         }
26882     },
26883
26884     /**
26885      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
26886      * @param {Roo.data.Store} store The data store to unbind
26887      */
26888     unbind : function(ds){
26889         ds.un("beforeload", this.beforeLoad, this);
26890         ds.un("load", this.onLoad, this);
26891         ds.un("loadexception", this.onLoadError, this);
26892         ds.un("remove", this.updateInfo, this);
26893         ds.un("add", this.updateInfo, this);
26894         this.ds = undefined;
26895     },
26896
26897     /**
26898      * Binds the paging toolbar to the specified {@link Roo.data.Store}
26899      * @param {Roo.data.Store} store The data store to bind
26900      */
26901     bind : function(ds){
26902         ds.on("beforeload", this.beforeLoad, this);
26903         ds.on("load", this.onLoad, this);
26904         ds.on("loadexception", this.onLoadError, this);
26905         ds.on("remove", this.updateInfo, this);
26906         ds.on("add", this.updateInfo, this);
26907         this.ds = ds;
26908     }
26909 });/*
26910  * - LGPL
26911  *
26912  * element
26913  * 
26914  */
26915
26916 /**
26917  * @class Roo.bootstrap.MessageBar
26918  * @extends Roo.bootstrap.Component
26919  * Bootstrap MessageBar class
26920  * @cfg {String} html contents of the MessageBar
26921  * @cfg {String} weight (info | success | warning | danger) default info
26922  * @cfg {String} beforeClass insert the bar before the given class
26923  * @cfg {Boolean} closable (true | false) default false
26924  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
26925  * 
26926  * @constructor
26927  * Create a new Element
26928  * @param {Object} config The config object
26929  */
26930
26931 Roo.bootstrap.MessageBar = function(config){
26932     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
26933 };
26934
26935 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
26936     
26937     html: '',
26938     weight: 'info',
26939     closable: false,
26940     fixed: false,
26941     beforeClass: 'bootstrap-sticky-wrap',
26942     
26943     getAutoCreate : function(){
26944         
26945         var cfg = {
26946             tag: 'div',
26947             cls: 'alert alert-dismissable alert-' + this.weight,
26948             cn: [
26949                 {
26950                     tag: 'span',
26951                     cls: 'message',
26952                     html: this.html || ''
26953                 }
26954             ]
26955         };
26956         
26957         if(this.fixed){
26958             cfg.cls += ' alert-messages-fixed';
26959         }
26960         
26961         if(this.closable){
26962             cfg.cn.push({
26963                 tag: 'button',
26964                 cls: 'close',
26965                 html: 'x'
26966             });
26967         }
26968         
26969         return cfg;
26970     },
26971     
26972     onRender : function(ct, position)
26973     {
26974         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
26975         
26976         if(!this.el){
26977             var cfg = Roo.apply({},  this.getAutoCreate());
26978             cfg.id = Roo.id();
26979             
26980             if (this.cls) {
26981                 cfg.cls += ' ' + this.cls;
26982             }
26983             if (this.style) {
26984                 cfg.style = this.style;
26985             }
26986             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
26987             
26988             this.el.setVisibilityMode(Roo.Element.DISPLAY);
26989         }
26990         
26991         this.el.select('>button.close').on('click', this.hide, this);
26992         
26993     },
26994     
26995     show : function()
26996     {
26997         if (!this.rendered) {
26998             this.render();
26999         }
27000         
27001         this.el.show();
27002         
27003         this.fireEvent('show', this);
27004         
27005     },
27006     
27007     hide : function()
27008     {
27009         if (!this.rendered) {
27010             this.render();
27011         }
27012         
27013         this.el.hide();
27014         
27015         this.fireEvent('hide', this);
27016     },
27017     
27018     update : function()
27019     {
27020 //        var e = this.el.dom.firstChild;
27021 //        
27022 //        if(this.closable){
27023 //            e = e.nextSibling;
27024 //        }
27025 //        
27026 //        e.data = this.html || '';
27027
27028         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27029     }
27030    
27031 });
27032
27033  
27034
27035      /*
27036  * - LGPL
27037  *
27038  * Graph
27039  * 
27040  */
27041
27042
27043 /**
27044  * @class Roo.bootstrap.Graph
27045  * @extends Roo.bootstrap.Component
27046  * Bootstrap Graph class
27047 > Prameters
27048  -sm {number} sm 4
27049  -md {number} md 5
27050  @cfg {String} graphtype  bar | vbar | pie
27051  @cfg {number} g_x coodinator | centre x (pie)
27052  @cfg {number} g_y coodinator | centre y (pie)
27053  @cfg {number} g_r radius (pie)
27054  @cfg {number} g_height height of the chart (respected by all elements in the set)
27055  @cfg {number} g_width width of the chart (respected by all elements in the set)
27056  @cfg {Object} title The title of the chart
27057     
27058  -{Array}  values
27059  -opts (object) options for the chart 
27060      o {
27061      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27062      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27063      o vgutter (number)
27064      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.
27065      o stacked (boolean) whether or not to tread values as in a stacked bar chart
27066      o to
27067      o stretch (boolean)
27068      o }
27069  -opts (object) options for the pie
27070      o{
27071      o cut
27072      o startAngle (number)
27073      o endAngle (number)
27074      } 
27075  *
27076  * @constructor
27077  * Create a new Input
27078  * @param {Object} config The config object
27079  */
27080
27081 Roo.bootstrap.Graph = function(config){
27082     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27083     
27084     this.addEvents({
27085         // img events
27086         /**
27087          * @event click
27088          * The img click event for the img.
27089          * @param {Roo.EventObject} e
27090          */
27091         "click" : true
27092     });
27093 };
27094
27095 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
27096     
27097     sm: 4,
27098     md: 5,
27099     graphtype: 'bar',
27100     g_height: 250,
27101     g_width: 400,
27102     g_x: 50,
27103     g_y: 50,
27104     g_r: 30,
27105     opts:{
27106         //g_colors: this.colors,
27107         g_type: 'soft',
27108         g_gutter: '20%'
27109
27110     },
27111     title : false,
27112
27113     getAutoCreate : function(){
27114         
27115         var cfg = {
27116             tag: 'div',
27117             html : null
27118         };
27119         
27120         
27121         return  cfg;
27122     },
27123
27124     onRender : function(ct,position){
27125         
27126         
27127         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27128         
27129         if (typeof(Raphael) == 'undefined') {
27130             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27131             return;
27132         }
27133         
27134         this.raphael = Raphael(this.el.dom);
27135         
27136                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27137                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27138                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27139                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27140                 /*
27141                 r.text(160, 10, "Single Series Chart").attr(txtattr);
27142                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27143                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27144                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27145                 
27146                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27147                 r.barchart(330, 10, 300, 220, data1);
27148                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27149                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27150                 */
27151                 
27152                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27153                 // r.barchart(30, 30, 560, 250,  xdata, {
27154                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27155                 //     axis : "0 0 1 1",
27156                 //     axisxlabels :  xdata
27157                 //     //yvalues : cols,
27158                    
27159                 // });
27160 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27161 //        
27162 //        this.load(null,xdata,{
27163 //                axis : "0 0 1 1",
27164 //                axisxlabels :  xdata
27165 //                });
27166
27167     },
27168
27169     load : function(graphtype,xdata,opts)
27170     {
27171         this.raphael.clear();
27172         if(!graphtype) {
27173             graphtype = this.graphtype;
27174         }
27175         if(!opts){
27176             opts = this.opts;
27177         }
27178         var r = this.raphael,
27179             fin = function () {
27180                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27181             },
27182             fout = function () {
27183                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27184             },
27185             pfin = function() {
27186                 this.sector.stop();
27187                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27188
27189                 if (this.label) {
27190                     this.label[0].stop();
27191                     this.label[0].attr({ r: 7.5 });
27192                     this.label[1].attr({ "font-weight": 800 });
27193                 }
27194             },
27195             pfout = function() {
27196                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27197
27198                 if (this.label) {
27199                     this.label[0].animate({ r: 5 }, 500, "bounce");
27200                     this.label[1].attr({ "font-weight": 400 });
27201                 }
27202             };
27203
27204         switch(graphtype){
27205             case 'bar':
27206                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27207                 break;
27208             case 'hbar':
27209                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27210                 break;
27211             case 'pie':
27212 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
27213 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27214 //            
27215                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27216                 
27217                 break;
27218
27219         }
27220         
27221         if(this.title){
27222             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27223         }
27224         
27225     },
27226     
27227     setTitle: function(o)
27228     {
27229         this.title = o;
27230     },
27231     
27232     initEvents: function() {
27233         
27234         if(!this.href){
27235             this.el.on('click', this.onClick, this);
27236         }
27237     },
27238     
27239     onClick : function(e)
27240     {
27241         Roo.log('img onclick');
27242         this.fireEvent('click', this, e);
27243     }
27244    
27245 });
27246
27247  
27248 /*
27249  * - LGPL
27250  *
27251  * numberBox
27252  * 
27253  */
27254 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27255
27256 /**
27257  * @class Roo.bootstrap.dash.NumberBox
27258  * @extends Roo.bootstrap.Component
27259  * Bootstrap NumberBox class
27260  * @cfg {String} headline Box headline
27261  * @cfg {String} content Box content
27262  * @cfg {String} icon Box icon
27263  * @cfg {String} footer Footer text
27264  * @cfg {String} fhref Footer href
27265  * 
27266  * @constructor
27267  * Create a new NumberBox
27268  * @param {Object} config The config object
27269  */
27270
27271
27272 Roo.bootstrap.dash.NumberBox = function(config){
27273     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27274     
27275 };
27276
27277 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
27278     
27279     headline : '',
27280     content : '',
27281     icon : '',
27282     footer : '',
27283     fhref : '',
27284     ficon : '',
27285     
27286     getAutoCreate : function(){
27287         
27288         var cfg = {
27289             tag : 'div',
27290             cls : 'small-box ',
27291             cn : [
27292                 {
27293                     tag : 'div',
27294                     cls : 'inner',
27295                     cn :[
27296                         {
27297                             tag : 'h3',
27298                             cls : 'roo-headline',
27299                             html : this.headline
27300                         },
27301                         {
27302                             tag : 'p',
27303                             cls : 'roo-content',
27304                             html : this.content
27305                         }
27306                     ]
27307                 }
27308             ]
27309         };
27310         
27311         if(this.icon){
27312             cfg.cn.push({
27313                 tag : 'div',
27314                 cls : 'icon',
27315                 cn :[
27316                     {
27317                         tag : 'i',
27318                         cls : 'ion ' + this.icon
27319                     }
27320                 ]
27321             });
27322         }
27323         
27324         if(this.footer){
27325             var footer = {
27326                 tag : 'a',
27327                 cls : 'small-box-footer',
27328                 href : this.fhref || '#',
27329                 html : this.footer
27330             };
27331             
27332             cfg.cn.push(footer);
27333             
27334         }
27335         
27336         return  cfg;
27337     },
27338
27339     onRender : function(ct,position){
27340         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27341
27342
27343        
27344                 
27345     },
27346
27347     setHeadline: function (value)
27348     {
27349         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27350     },
27351     
27352     setFooter: function (value, href)
27353     {
27354         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27355         
27356         if(href){
27357             this.el.select('a.small-box-footer',true).first().attr('href', href);
27358         }
27359         
27360     },
27361
27362     setContent: function (value)
27363     {
27364         this.el.select('.roo-content',true).first().dom.innerHTML = value;
27365     },
27366
27367     initEvents: function() 
27368     {   
27369         
27370     }
27371     
27372 });
27373
27374  
27375 /*
27376  * - LGPL
27377  *
27378  * TabBox
27379  * 
27380  */
27381 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27382
27383 /**
27384  * @class Roo.bootstrap.dash.TabBox
27385  * @extends Roo.bootstrap.Component
27386  * Bootstrap TabBox class
27387  * @cfg {String} title Title of the TabBox
27388  * @cfg {String} icon Icon of the TabBox
27389  * @cfg {Boolean} showtabs (true|false) show the tabs default true
27390  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
27391  * 
27392  * @constructor
27393  * Create a new TabBox
27394  * @param {Object} config The config object
27395  */
27396
27397
27398 Roo.bootstrap.dash.TabBox = function(config){
27399     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
27400     this.addEvents({
27401         // raw events
27402         /**
27403          * @event addpane
27404          * When a pane is added
27405          * @param {Roo.bootstrap.dash.TabPane} pane
27406          */
27407         "addpane" : true,
27408         /**
27409          * @event activatepane
27410          * When a pane is activated
27411          * @param {Roo.bootstrap.dash.TabPane} pane
27412          */
27413         "activatepane" : true
27414         
27415          
27416     });
27417     
27418     this.panes = [];
27419 };
27420
27421 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
27422
27423     title : '',
27424     icon : false,
27425     showtabs : true,
27426     tabScrollable : false,
27427     
27428     getChildContainer : function()
27429     {
27430         return this.el.select('.tab-content', true).first();
27431     },
27432     
27433     getAutoCreate : function(){
27434         
27435         var header = {
27436             tag: 'li',
27437             cls: 'pull-left header',
27438             html: this.title,
27439             cn : []
27440         };
27441         
27442         if(this.icon){
27443             header.cn.push({
27444                 tag: 'i',
27445                 cls: 'fa ' + this.icon
27446             });
27447         }
27448         
27449         var h = {
27450             tag: 'ul',
27451             cls: 'nav nav-tabs pull-right',
27452             cn: [
27453                 header
27454             ]
27455         };
27456         
27457         if(this.tabScrollable){
27458             h = {
27459                 tag: 'div',
27460                 cls: 'tab-header',
27461                 cn: [
27462                     {
27463                         tag: 'ul',
27464                         cls: 'nav nav-tabs pull-right',
27465                         cn: [
27466                             header
27467                         ]
27468                     }
27469                 ]
27470             };
27471         }
27472         
27473         var cfg = {
27474             tag: 'div',
27475             cls: 'nav-tabs-custom',
27476             cn: [
27477                 h,
27478                 {
27479                     tag: 'div',
27480                     cls: 'tab-content no-padding',
27481                     cn: []
27482                 }
27483             ]
27484         };
27485
27486         return  cfg;
27487     },
27488     initEvents : function()
27489     {
27490         //Roo.log('add add pane handler');
27491         this.on('addpane', this.onAddPane, this);
27492     },
27493      /**
27494      * Updates the box title
27495      * @param {String} html to set the title to.
27496      */
27497     setTitle : function(value)
27498     {
27499         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
27500     },
27501     onAddPane : function(pane)
27502     {
27503         this.panes.push(pane);
27504         //Roo.log('addpane');
27505         //Roo.log(pane);
27506         // tabs are rendere left to right..
27507         if(!this.showtabs){
27508             return;
27509         }
27510         
27511         var ctr = this.el.select('.nav-tabs', true).first();
27512          
27513          
27514         var existing = ctr.select('.nav-tab',true);
27515         var qty = existing.getCount();;
27516         
27517         
27518         var tab = ctr.createChild({
27519             tag : 'li',
27520             cls : 'nav-tab' + (qty ? '' : ' active'),
27521             cn : [
27522                 {
27523                     tag : 'a',
27524                     href:'#',
27525                     html : pane.title
27526                 }
27527             ]
27528         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
27529         pane.tab = tab;
27530         
27531         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
27532         if (!qty) {
27533             pane.el.addClass('active');
27534         }
27535         
27536                 
27537     },
27538     onTabClick : function(ev,un,ob,pane)
27539     {
27540         //Roo.log('tab - prev default');
27541         ev.preventDefault();
27542         
27543         
27544         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
27545         pane.tab.addClass('active');
27546         //Roo.log(pane.title);
27547         this.getChildContainer().select('.tab-pane',true).removeClass('active');
27548         // technically we should have a deactivate event.. but maybe add later.
27549         // and it should not de-activate the selected tab...
27550         this.fireEvent('activatepane', pane);
27551         pane.el.addClass('active');
27552         pane.fireEvent('activate');
27553         
27554         
27555     },
27556     
27557     getActivePane : function()
27558     {
27559         var r = false;
27560         Roo.each(this.panes, function(p) {
27561             if(p.el.hasClass('active')){
27562                 r = p;
27563                 return false;
27564             }
27565             
27566             return;
27567         });
27568         
27569         return r;
27570     }
27571     
27572     
27573 });
27574
27575  
27576 /*
27577  * - LGPL
27578  *
27579  * Tab pane
27580  * 
27581  */
27582 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27583 /**
27584  * @class Roo.bootstrap.TabPane
27585  * @extends Roo.bootstrap.Component
27586  * Bootstrap TabPane class
27587  * @cfg {Boolean} active (false | true) Default false
27588  * @cfg {String} title title of panel
27589
27590  * 
27591  * @constructor
27592  * Create a new TabPane
27593  * @param {Object} config The config object
27594  */
27595
27596 Roo.bootstrap.dash.TabPane = function(config){
27597     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
27598     
27599     this.addEvents({
27600         // raw events
27601         /**
27602          * @event activate
27603          * When a pane is activated
27604          * @param {Roo.bootstrap.dash.TabPane} pane
27605          */
27606         "activate" : true
27607          
27608     });
27609 };
27610
27611 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
27612     
27613     active : false,
27614     title : '',
27615     
27616     // the tabBox that this is attached to.
27617     tab : false,
27618      
27619     getAutoCreate : function() 
27620     {
27621         var cfg = {
27622             tag: 'div',
27623             cls: 'tab-pane'
27624         };
27625         
27626         if(this.active){
27627             cfg.cls += ' active';
27628         }
27629         
27630         return cfg;
27631     },
27632     initEvents  : function()
27633     {
27634         //Roo.log('trigger add pane handler');
27635         this.parent().fireEvent('addpane', this)
27636     },
27637     
27638      /**
27639      * Updates the tab title 
27640      * @param {String} html to set the title to.
27641      */
27642     setTitle: function(str)
27643     {
27644         if (!this.tab) {
27645             return;
27646         }
27647         this.title = str;
27648         this.tab.select('a', true).first().dom.innerHTML = str;
27649         
27650     }
27651     
27652     
27653     
27654 });
27655
27656  
27657
27658
27659  /*
27660  * - LGPL
27661  *
27662  * menu
27663  * 
27664  */
27665 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
27666
27667 /**
27668  * @class Roo.bootstrap.menu.Menu
27669  * @extends Roo.bootstrap.Component
27670  * Bootstrap Menu class - container for Menu
27671  * @cfg {String} html Text of the menu
27672  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
27673  * @cfg {String} icon Font awesome icon
27674  * @cfg {String} pos Menu align to (top | bottom) default bottom
27675  * 
27676  * 
27677  * @constructor
27678  * Create a new Menu
27679  * @param {Object} config The config object
27680  */
27681
27682
27683 Roo.bootstrap.menu.Menu = function(config){
27684     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
27685     
27686     this.addEvents({
27687         /**
27688          * @event beforeshow
27689          * Fires before this menu is displayed
27690          * @param {Roo.bootstrap.menu.Menu} this
27691          */
27692         beforeshow : true,
27693         /**
27694          * @event beforehide
27695          * Fires before this menu is hidden
27696          * @param {Roo.bootstrap.menu.Menu} this
27697          */
27698         beforehide : true,
27699         /**
27700          * @event show
27701          * Fires after this menu is displayed
27702          * @param {Roo.bootstrap.menu.Menu} this
27703          */
27704         show : true,
27705         /**
27706          * @event hide
27707          * Fires after this menu is hidden
27708          * @param {Roo.bootstrap.menu.Menu} this
27709          */
27710         hide : true,
27711         /**
27712          * @event click
27713          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
27714          * @param {Roo.bootstrap.menu.Menu} this
27715          * @param {Roo.EventObject} e
27716          */
27717         click : true
27718     });
27719     
27720 };
27721
27722 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
27723     
27724     submenu : false,
27725     html : '',
27726     weight : 'default',
27727     icon : false,
27728     pos : 'bottom',
27729     
27730     
27731     getChildContainer : function() {
27732         if(this.isSubMenu){
27733             return this.el;
27734         }
27735         
27736         return this.el.select('ul.dropdown-menu', true).first();  
27737     },
27738     
27739     getAutoCreate : function()
27740     {
27741         var text = [
27742             {
27743                 tag : 'span',
27744                 cls : 'roo-menu-text',
27745                 html : this.html
27746             }
27747         ];
27748         
27749         if(this.icon){
27750             text.unshift({
27751                 tag : 'i',
27752                 cls : 'fa ' + this.icon
27753             })
27754         }
27755         
27756         
27757         var cfg = {
27758             tag : 'div',
27759             cls : 'btn-group',
27760             cn : [
27761                 {
27762                     tag : 'button',
27763                     cls : 'dropdown-button btn btn-' + this.weight,
27764                     cn : text
27765                 },
27766                 {
27767                     tag : 'button',
27768                     cls : 'dropdown-toggle btn btn-' + this.weight,
27769                     cn : [
27770                         {
27771                             tag : 'span',
27772                             cls : 'caret'
27773                         }
27774                     ]
27775                 },
27776                 {
27777                     tag : 'ul',
27778                     cls : 'dropdown-menu'
27779                 }
27780             ]
27781             
27782         };
27783         
27784         if(this.pos == 'top'){
27785             cfg.cls += ' dropup';
27786         }
27787         
27788         if(this.isSubMenu){
27789             cfg = {
27790                 tag : 'ul',
27791                 cls : 'dropdown-menu'
27792             }
27793         }
27794         
27795         return cfg;
27796     },
27797     
27798     onRender : function(ct, position)
27799     {
27800         this.isSubMenu = ct.hasClass('dropdown-submenu');
27801         
27802         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
27803     },
27804     
27805     initEvents : function() 
27806     {
27807         if(this.isSubMenu){
27808             return;
27809         }
27810         
27811         this.hidden = true;
27812         
27813         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
27814         this.triggerEl.on('click', this.onTriggerPress, this);
27815         
27816         this.buttonEl = this.el.select('button.dropdown-button', true).first();
27817         this.buttonEl.on('click', this.onClick, this);
27818         
27819     },
27820     
27821     list : function()
27822     {
27823         if(this.isSubMenu){
27824             return this.el;
27825         }
27826         
27827         return this.el.select('ul.dropdown-menu', true).first();
27828     },
27829     
27830     onClick : function(e)
27831     {
27832         this.fireEvent("click", this, e);
27833     },
27834     
27835     onTriggerPress  : function(e)
27836     {   
27837         if (this.isVisible()) {
27838             this.hide();
27839         } else {
27840             this.show();
27841         }
27842     },
27843     
27844     isVisible : function(){
27845         return !this.hidden;
27846     },
27847     
27848     show : function()
27849     {
27850         this.fireEvent("beforeshow", this);
27851         
27852         this.hidden = false;
27853         this.el.addClass('open');
27854         
27855         Roo.get(document).on("mouseup", this.onMouseUp, this);
27856         
27857         this.fireEvent("show", this);
27858         
27859         
27860     },
27861     
27862     hide : function()
27863     {
27864         this.fireEvent("beforehide", this);
27865         
27866         this.hidden = true;
27867         this.el.removeClass('open');
27868         
27869         Roo.get(document).un("mouseup", this.onMouseUp);
27870         
27871         this.fireEvent("hide", this);
27872     },
27873     
27874     onMouseUp : function()
27875     {
27876         this.hide();
27877     }
27878     
27879 });
27880
27881  
27882  /*
27883  * - LGPL
27884  *
27885  * menu item
27886  * 
27887  */
27888 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
27889
27890 /**
27891  * @class Roo.bootstrap.menu.Item
27892  * @extends Roo.bootstrap.Component
27893  * Bootstrap MenuItem class
27894  * @cfg {Boolean} submenu (true | false) default false
27895  * @cfg {String} html text of the item
27896  * @cfg {String} href the link
27897  * @cfg {Boolean} disable (true | false) default false
27898  * @cfg {Boolean} preventDefault (true | false) default true
27899  * @cfg {String} icon Font awesome icon
27900  * @cfg {String} pos Submenu align to (left | right) default right 
27901  * 
27902  * 
27903  * @constructor
27904  * Create a new Item
27905  * @param {Object} config The config object
27906  */
27907
27908
27909 Roo.bootstrap.menu.Item = function(config){
27910     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
27911     this.addEvents({
27912         /**
27913          * @event mouseover
27914          * Fires when the mouse is hovering over this menu
27915          * @param {Roo.bootstrap.menu.Item} this
27916          * @param {Roo.EventObject} e
27917          */
27918         mouseover : true,
27919         /**
27920          * @event mouseout
27921          * Fires when the mouse exits this menu
27922          * @param {Roo.bootstrap.menu.Item} this
27923          * @param {Roo.EventObject} e
27924          */
27925         mouseout : true,
27926         // raw events
27927         /**
27928          * @event click
27929          * The raw click event for the entire grid.
27930          * @param {Roo.EventObject} e
27931          */
27932         click : true
27933     });
27934 };
27935
27936 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
27937     
27938     submenu : false,
27939     href : '',
27940     html : '',
27941     preventDefault: true,
27942     disable : false,
27943     icon : false,
27944     pos : 'right',
27945     
27946     getAutoCreate : function()
27947     {
27948         var text = [
27949             {
27950                 tag : 'span',
27951                 cls : 'roo-menu-item-text',
27952                 html : this.html
27953             }
27954         ];
27955         
27956         if(this.icon){
27957             text.unshift({
27958                 tag : 'i',
27959                 cls : 'fa ' + this.icon
27960             })
27961         }
27962         
27963         var cfg = {
27964             tag : 'li',
27965             cn : [
27966                 {
27967                     tag : 'a',
27968                     href : this.href || '#',
27969                     cn : text
27970                 }
27971             ]
27972         };
27973         
27974         if(this.disable){
27975             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
27976         }
27977         
27978         if(this.submenu){
27979             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
27980             
27981             if(this.pos == 'left'){
27982                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
27983             }
27984         }
27985         
27986         return cfg;
27987     },
27988     
27989     initEvents : function() 
27990     {
27991         this.el.on('mouseover', this.onMouseOver, this);
27992         this.el.on('mouseout', this.onMouseOut, this);
27993         
27994         this.el.select('a', true).first().on('click', this.onClick, this);
27995         
27996     },
27997     
27998     onClick : function(e)
27999     {
28000         if(this.preventDefault){
28001             e.preventDefault();
28002         }
28003         
28004         this.fireEvent("click", this, e);
28005     },
28006     
28007     onMouseOver : function(e)
28008     {
28009         if(this.submenu && this.pos == 'left'){
28010             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28011         }
28012         
28013         this.fireEvent("mouseover", this, e);
28014     },
28015     
28016     onMouseOut : function(e)
28017     {
28018         this.fireEvent("mouseout", this, e);
28019     }
28020 });
28021
28022  
28023
28024  /*
28025  * - LGPL
28026  *
28027  * menu separator
28028  * 
28029  */
28030 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28031
28032 /**
28033  * @class Roo.bootstrap.menu.Separator
28034  * @extends Roo.bootstrap.Component
28035  * Bootstrap Separator class
28036  * 
28037  * @constructor
28038  * Create a new Separator
28039  * @param {Object} config The config object
28040  */
28041
28042
28043 Roo.bootstrap.menu.Separator = function(config){
28044     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28045 };
28046
28047 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28048     
28049     getAutoCreate : function(){
28050         var cfg = {
28051             tag : 'li',
28052             cls: 'divider'
28053         };
28054         
28055         return cfg;
28056     }
28057    
28058 });
28059
28060  
28061
28062  /*
28063  * - LGPL
28064  *
28065  * Tooltip
28066  * 
28067  */
28068
28069 /**
28070  * @class Roo.bootstrap.Tooltip
28071  * Bootstrap Tooltip class
28072  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28073  * to determine which dom element triggers the tooltip.
28074  * 
28075  * It needs to add support for additional attributes like tooltip-position
28076  * 
28077  * @constructor
28078  * Create a new Toolti
28079  * @param {Object} config The config object
28080  */
28081
28082 Roo.bootstrap.Tooltip = function(config){
28083     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28084     
28085     this.alignment = Roo.bootstrap.Tooltip.alignment;
28086     
28087     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28088         this.alignment = config.alignment;
28089     }
28090     
28091 };
28092
28093 Roo.apply(Roo.bootstrap.Tooltip, {
28094     /**
28095      * @function init initialize tooltip monitoring.
28096      * @static
28097      */
28098     currentEl : false,
28099     currentTip : false,
28100     currentRegion : false,
28101     
28102     //  init : delay?
28103     
28104     init : function()
28105     {
28106         Roo.get(document).on('mouseover', this.enter ,this);
28107         Roo.get(document).on('mouseout', this.leave, this);
28108          
28109         
28110         this.currentTip = new Roo.bootstrap.Tooltip();
28111     },
28112     
28113     enter : function(ev)
28114     {
28115         var dom = ev.getTarget();
28116         
28117         //Roo.log(['enter',dom]);
28118         var el = Roo.fly(dom);
28119         if (this.currentEl) {
28120             //Roo.log(dom);
28121             //Roo.log(this.currentEl);
28122             //Roo.log(this.currentEl.contains(dom));
28123             if (this.currentEl == el) {
28124                 return;
28125             }
28126             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28127                 return;
28128             }
28129
28130         }
28131         
28132         if (this.currentTip.el) {
28133             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28134         }    
28135         //Roo.log(ev);
28136         
28137         if(!el || el.dom == document){
28138             return;
28139         }
28140         
28141         var bindEl = el;
28142         
28143         // you can not look for children, as if el is the body.. then everythign is the child..
28144         if (!el.attr('tooltip')) { //
28145             if (!el.select("[tooltip]").elements.length) {
28146                 return;
28147             }
28148             // is the mouse over this child...?
28149             bindEl = el.select("[tooltip]").first();
28150             var xy = ev.getXY();
28151             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28152                 //Roo.log("not in region.");
28153                 return;
28154             }
28155             //Roo.log("child element over..");
28156             
28157         }
28158         this.currentEl = bindEl;
28159         this.currentTip.bind(bindEl);
28160         this.currentRegion = Roo.lib.Region.getRegion(dom);
28161         this.currentTip.enter();
28162         
28163     },
28164     leave : function(ev)
28165     {
28166         var dom = ev.getTarget();
28167         //Roo.log(['leave',dom]);
28168         if (!this.currentEl) {
28169             return;
28170         }
28171         
28172         
28173         if (dom != this.currentEl.dom) {
28174             return;
28175         }
28176         var xy = ev.getXY();
28177         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
28178             return;
28179         }
28180         // only activate leave if mouse cursor is outside... bounding box..
28181         
28182         
28183         
28184         
28185         if (this.currentTip) {
28186             this.currentTip.leave();
28187         }
28188         //Roo.log('clear currentEl');
28189         this.currentEl = false;
28190         
28191         
28192     },
28193     alignment : {
28194         'left' : ['r-l', [-2,0], 'right'],
28195         'right' : ['l-r', [2,0], 'left'],
28196         'bottom' : ['t-b', [0,2], 'top'],
28197         'top' : [ 'b-t', [0,-2], 'bottom']
28198     }
28199     
28200 });
28201
28202
28203 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
28204     
28205     
28206     bindEl : false,
28207     
28208     delay : null, // can be { show : 300 , hide: 500}
28209     
28210     timeout : null,
28211     
28212     hoverState : null, //???
28213     
28214     placement : 'bottom', 
28215     
28216     alignment : false,
28217     
28218     getAutoCreate : function(){
28219     
28220         var cfg = {
28221            cls : 'tooltip',
28222            role : 'tooltip',
28223            cn : [
28224                 {
28225                     cls : 'tooltip-arrow'
28226                 },
28227                 {
28228                     cls : 'tooltip-inner'
28229                 }
28230            ]
28231         };
28232         
28233         return cfg;
28234     },
28235     bind : function(el)
28236     {
28237         this.bindEl = el;
28238     },
28239       
28240     
28241     enter : function () {
28242        
28243         if (this.timeout != null) {
28244             clearTimeout(this.timeout);
28245         }
28246         
28247         this.hoverState = 'in';
28248          //Roo.log("enter - show");
28249         if (!this.delay || !this.delay.show) {
28250             this.show();
28251             return;
28252         }
28253         var _t = this;
28254         this.timeout = setTimeout(function () {
28255             if (_t.hoverState == 'in') {
28256                 _t.show();
28257             }
28258         }, this.delay.show);
28259     },
28260     leave : function()
28261     {
28262         clearTimeout(this.timeout);
28263     
28264         this.hoverState = 'out';
28265          if (!this.delay || !this.delay.hide) {
28266             this.hide();
28267             return;
28268         }
28269        
28270         var _t = this;
28271         this.timeout = setTimeout(function () {
28272             //Roo.log("leave - timeout");
28273             
28274             if (_t.hoverState == 'out') {
28275                 _t.hide();
28276                 Roo.bootstrap.Tooltip.currentEl = false;
28277             }
28278         }, delay);
28279     },
28280     
28281     show : function (msg)
28282     {
28283         if (!this.el) {
28284             this.render(document.body);
28285         }
28286         // set content.
28287         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28288         
28289         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28290         
28291         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28292         
28293         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
28294         
28295         var placement = typeof this.placement == 'function' ?
28296             this.placement.call(this, this.el, on_el) :
28297             this.placement;
28298             
28299         var autoToken = /\s?auto?\s?/i;
28300         var autoPlace = autoToken.test(placement);
28301         if (autoPlace) {
28302             placement = placement.replace(autoToken, '') || 'top';
28303         }
28304         
28305         //this.el.detach()
28306         //this.el.setXY([0,0]);
28307         this.el.show();
28308         //this.el.dom.style.display='block';
28309         
28310         //this.el.appendTo(on_el);
28311         
28312         var p = this.getPosition();
28313         var box = this.el.getBox();
28314         
28315         if (autoPlace) {
28316             // fixme..
28317         }
28318         
28319         var align = this.alignment[placement];
28320         
28321         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28322         
28323         if(placement == 'top' || placement == 'bottom'){
28324             if(xy[0] < 0){
28325                 placement = 'right';
28326             }
28327             
28328             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28329                 placement = 'left';
28330             }
28331             
28332             var scroll = Roo.select('body', true).first().getScroll();
28333             
28334             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28335                 placement = 'top';
28336             }
28337             
28338             align = this.alignment[placement];
28339         }
28340         
28341         this.el.alignTo(this.bindEl, align[0],align[1]);
28342         //var arrow = this.el.select('.arrow',true).first();
28343         //arrow.set(align[2], 
28344         
28345         this.el.addClass(placement);
28346         
28347         this.el.addClass('in fade');
28348         
28349         this.hoverState = null;
28350         
28351         if (this.el.hasClass('fade')) {
28352             // fade it?
28353         }
28354         
28355     },
28356     hide : function()
28357     {
28358          
28359         if (!this.el) {
28360             return;
28361         }
28362         //this.el.setXY([0,0]);
28363         this.el.removeClass('in');
28364         //this.el.hide();
28365         
28366     }
28367     
28368 });
28369  
28370
28371  /*
28372  * - LGPL
28373  *
28374  * Location Picker
28375  * 
28376  */
28377
28378 /**
28379  * @class Roo.bootstrap.LocationPicker
28380  * @extends Roo.bootstrap.Component
28381  * Bootstrap LocationPicker class
28382  * @cfg {Number} latitude Position when init default 0
28383  * @cfg {Number} longitude Position when init default 0
28384  * @cfg {Number} zoom default 15
28385  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
28386  * @cfg {Boolean} mapTypeControl default false
28387  * @cfg {Boolean} disableDoubleClickZoom default false
28388  * @cfg {Boolean} scrollwheel default true
28389  * @cfg {Boolean} streetViewControl default false
28390  * @cfg {Number} radius default 0
28391  * @cfg {String} locationName
28392  * @cfg {Boolean} draggable default true
28393  * @cfg {Boolean} enableAutocomplete default false
28394  * @cfg {Boolean} enableReverseGeocode default true
28395  * @cfg {String} markerTitle
28396  * 
28397  * @constructor
28398  * Create a new LocationPicker
28399  * @param {Object} config The config object
28400  */
28401
28402
28403 Roo.bootstrap.LocationPicker = function(config){
28404     
28405     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
28406     
28407     this.addEvents({
28408         /**
28409          * @event initial
28410          * Fires when the picker initialized.
28411          * @param {Roo.bootstrap.LocationPicker} this
28412          * @param {Google Location} location
28413          */
28414         initial : true,
28415         /**
28416          * @event positionchanged
28417          * Fires when the picker position changed.
28418          * @param {Roo.bootstrap.LocationPicker} this
28419          * @param {Google Location} location
28420          */
28421         positionchanged : true,
28422         /**
28423          * @event resize
28424          * Fires when the map resize.
28425          * @param {Roo.bootstrap.LocationPicker} this
28426          */
28427         resize : true,
28428         /**
28429          * @event show
28430          * Fires when the map show.
28431          * @param {Roo.bootstrap.LocationPicker} this
28432          */
28433         show : true,
28434         /**
28435          * @event hide
28436          * Fires when the map hide.
28437          * @param {Roo.bootstrap.LocationPicker} this
28438          */
28439         hide : true,
28440         /**
28441          * @event mapClick
28442          * Fires when click the map.
28443          * @param {Roo.bootstrap.LocationPicker} this
28444          * @param {Map event} e
28445          */
28446         mapClick : true,
28447         /**
28448          * @event mapRightClick
28449          * Fires when right click the map.
28450          * @param {Roo.bootstrap.LocationPicker} this
28451          * @param {Map event} e
28452          */
28453         mapRightClick : true,
28454         /**
28455          * @event markerClick
28456          * Fires when click the marker.
28457          * @param {Roo.bootstrap.LocationPicker} this
28458          * @param {Map event} e
28459          */
28460         markerClick : true,
28461         /**
28462          * @event markerRightClick
28463          * Fires when right click the marker.
28464          * @param {Roo.bootstrap.LocationPicker} this
28465          * @param {Map event} e
28466          */
28467         markerRightClick : true,
28468         /**
28469          * @event OverlayViewDraw
28470          * Fires when OverlayView Draw
28471          * @param {Roo.bootstrap.LocationPicker} this
28472          */
28473         OverlayViewDraw : true,
28474         /**
28475          * @event OverlayViewOnAdd
28476          * Fires when OverlayView Draw
28477          * @param {Roo.bootstrap.LocationPicker} this
28478          */
28479         OverlayViewOnAdd : true,
28480         /**
28481          * @event OverlayViewOnRemove
28482          * Fires when OverlayView Draw
28483          * @param {Roo.bootstrap.LocationPicker} this
28484          */
28485         OverlayViewOnRemove : true,
28486         /**
28487          * @event OverlayViewShow
28488          * Fires when OverlayView Draw
28489          * @param {Roo.bootstrap.LocationPicker} this
28490          * @param {Pixel} cpx
28491          */
28492         OverlayViewShow : true,
28493         /**
28494          * @event OverlayViewHide
28495          * Fires when OverlayView Draw
28496          * @param {Roo.bootstrap.LocationPicker} this
28497          */
28498         OverlayViewHide : true,
28499         /**
28500          * @event loadexception
28501          * Fires when load google lib failed.
28502          * @param {Roo.bootstrap.LocationPicker} this
28503          */
28504         loadexception : true
28505     });
28506         
28507 };
28508
28509 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
28510     
28511     gMapContext: false,
28512     
28513     latitude: 0,
28514     longitude: 0,
28515     zoom: 15,
28516     mapTypeId: false,
28517     mapTypeControl: false,
28518     disableDoubleClickZoom: false,
28519     scrollwheel: true,
28520     streetViewControl: false,
28521     radius: 0,
28522     locationName: '',
28523     draggable: true,
28524     enableAutocomplete: false,
28525     enableReverseGeocode: true,
28526     markerTitle: '',
28527     
28528     getAutoCreate: function()
28529     {
28530
28531         var cfg = {
28532             tag: 'div',
28533             cls: 'roo-location-picker'
28534         };
28535         
28536         return cfg
28537     },
28538     
28539     initEvents: function(ct, position)
28540     {       
28541         if(!this.el.getWidth() || this.isApplied()){
28542             return;
28543         }
28544         
28545         this.el.setVisibilityMode(Roo.Element.DISPLAY);
28546         
28547         this.initial();
28548     },
28549     
28550     initial: function()
28551     {
28552         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
28553             this.fireEvent('loadexception', this);
28554             return;
28555         }
28556         
28557         if(!this.mapTypeId){
28558             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
28559         }
28560         
28561         this.gMapContext = this.GMapContext();
28562         
28563         this.initOverlayView();
28564         
28565         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
28566         
28567         var _this = this;
28568                 
28569         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
28570             _this.setPosition(_this.gMapContext.marker.position);
28571         });
28572         
28573         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
28574             _this.fireEvent('mapClick', this, event);
28575             
28576         });
28577
28578         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
28579             _this.fireEvent('mapRightClick', this, event);
28580             
28581         });
28582         
28583         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
28584             _this.fireEvent('markerClick', this, event);
28585             
28586         });
28587
28588         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
28589             _this.fireEvent('markerRightClick', this, event);
28590             
28591         });
28592         
28593         this.setPosition(this.gMapContext.location);
28594         
28595         this.fireEvent('initial', this, this.gMapContext.location);
28596     },
28597     
28598     initOverlayView: function()
28599     {
28600         var _this = this;
28601         
28602         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
28603             
28604             draw: function()
28605             {
28606                 _this.fireEvent('OverlayViewDraw', _this);
28607             },
28608             
28609             onAdd: function()
28610             {
28611                 _this.fireEvent('OverlayViewOnAdd', _this);
28612             },
28613             
28614             onRemove: function()
28615             {
28616                 _this.fireEvent('OverlayViewOnRemove', _this);
28617             },
28618             
28619             show: function(cpx)
28620             {
28621                 _this.fireEvent('OverlayViewShow', _this, cpx);
28622             },
28623             
28624             hide: function()
28625             {
28626                 _this.fireEvent('OverlayViewHide', _this);
28627             }
28628             
28629         });
28630     },
28631     
28632     fromLatLngToContainerPixel: function(event)
28633     {
28634         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
28635     },
28636     
28637     isApplied: function() 
28638     {
28639         return this.getGmapContext() == false ? false : true;
28640     },
28641     
28642     getGmapContext: function() 
28643     {
28644         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
28645     },
28646     
28647     GMapContext: function() 
28648     {
28649         var position = new google.maps.LatLng(this.latitude, this.longitude);
28650         
28651         var _map = new google.maps.Map(this.el.dom, {
28652             center: position,
28653             zoom: this.zoom,
28654             mapTypeId: this.mapTypeId,
28655             mapTypeControl: this.mapTypeControl,
28656             disableDoubleClickZoom: this.disableDoubleClickZoom,
28657             scrollwheel: this.scrollwheel,
28658             streetViewControl: this.streetViewControl,
28659             locationName: this.locationName,
28660             draggable: this.draggable,
28661             enableAutocomplete: this.enableAutocomplete,
28662             enableReverseGeocode: this.enableReverseGeocode
28663         });
28664         
28665         var _marker = new google.maps.Marker({
28666             position: position,
28667             map: _map,
28668             title: this.markerTitle,
28669             draggable: this.draggable
28670         });
28671         
28672         return {
28673             map: _map,
28674             marker: _marker,
28675             circle: null,
28676             location: position,
28677             radius: this.radius,
28678             locationName: this.locationName,
28679             addressComponents: {
28680                 formatted_address: null,
28681                 addressLine1: null,
28682                 addressLine2: null,
28683                 streetName: null,
28684                 streetNumber: null,
28685                 city: null,
28686                 district: null,
28687                 state: null,
28688                 stateOrProvince: null
28689             },
28690             settings: this,
28691             domContainer: this.el.dom,
28692             geodecoder: new google.maps.Geocoder()
28693         };
28694     },
28695     
28696     drawCircle: function(center, radius, options) 
28697     {
28698         if (this.gMapContext.circle != null) {
28699             this.gMapContext.circle.setMap(null);
28700         }
28701         if (radius > 0) {
28702             radius *= 1;
28703             options = Roo.apply({}, options, {
28704                 strokeColor: "#0000FF",
28705                 strokeOpacity: .35,
28706                 strokeWeight: 2,
28707                 fillColor: "#0000FF",
28708                 fillOpacity: .2
28709             });
28710             
28711             options.map = this.gMapContext.map;
28712             options.radius = radius;
28713             options.center = center;
28714             this.gMapContext.circle = new google.maps.Circle(options);
28715             return this.gMapContext.circle;
28716         }
28717         
28718         return null;
28719     },
28720     
28721     setPosition: function(location) 
28722     {
28723         this.gMapContext.location = location;
28724         this.gMapContext.marker.setPosition(location);
28725         this.gMapContext.map.panTo(location);
28726         this.drawCircle(location, this.gMapContext.radius, {});
28727         
28728         var _this = this;
28729         
28730         if (this.gMapContext.settings.enableReverseGeocode) {
28731             this.gMapContext.geodecoder.geocode({
28732                 latLng: this.gMapContext.location
28733             }, function(results, status) {
28734                 
28735                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
28736                     _this.gMapContext.locationName = results[0].formatted_address;
28737                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
28738                     
28739                     _this.fireEvent('positionchanged', this, location);
28740                 }
28741             });
28742             
28743             return;
28744         }
28745         
28746         this.fireEvent('positionchanged', this, location);
28747     },
28748     
28749     resize: function()
28750     {
28751         google.maps.event.trigger(this.gMapContext.map, "resize");
28752         
28753         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
28754         
28755         this.fireEvent('resize', this);
28756     },
28757     
28758     setPositionByLatLng: function(latitude, longitude)
28759     {
28760         this.setPosition(new google.maps.LatLng(latitude, longitude));
28761     },
28762     
28763     getCurrentPosition: function() 
28764     {
28765         return {
28766             latitude: this.gMapContext.location.lat(),
28767             longitude: this.gMapContext.location.lng()
28768         };
28769     },
28770     
28771     getAddressName: function() 
28772     {
28773         return this.gMapContext.locationName;
28774     },
28775     
28776     getAddressComponents: function() 
28777     {
28778         return this.gMapContext.addressComponents;
28779     },
28780     
28781     address_component_from_google_geocode: function(address_components) 
28782     {
28783         var result = {};
28784         
28785         for (var i = 0; i < address_components.length; i++) {
28786             var component = address_components[i];
28787             if (component.types.indexOf("postal_code") >= 0) {
28788                 result.postalCode = component.short_name;
28789             } else if (component.types.indexOf("street_number") >= 0) {
28790                 result.streetNumber = component.short_name;
28791             } else if (component.types.indexOf("route") >= 0) {
28792                 result.streetName = component.short_name;
28793             } else if (component.types.indexOf("neighborhood") >= 0) {
28794                 result.city = component.short_name;
28795             } else if (component.types.indexOf("locality") >= 0) {
28796                 result.city = component.short_name;
28797             } else if (component.types.indexOf("sublocality") >= 0) {
28798                 result.district = component.short_name;
28799             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
28800                 result.stateOrProvince = component.short_name;
28801             } else if (component.types.indexOf("country") >= 0) {
28802                 result.country = component.short_name;
28803             }
28804         }
28805         
28806         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
28807         result.addressLine2 = "";
28808         return result;
28809     },
28810     
28811     setZoomLevel: function(zoom)
28812     {
28813         this.gMapContext.map.setZoom(zoom);
28814     },
28815     
28816     show: function()
28817     {
28818         if(!this.el){
28819             return;
28820         }
28821         
28822         this.el.show();
28823         
28824         this.resize();
28825         
28826         this.fireEvent('show', this);
28827     },
28828     
28829     hide: function()
28830     {
28831         if(!this.el){
28832             return;
28833         }
28834         
28835         this.el.hide();
28836         
28837         this.fireEvent('hide', this);
28838     }
28839     
28840 });
28841
28842 Roo.apply(Roo.bootstrap.LocationPicker, {
28843     
28844     OverlayView : function(map, options)
28845     {
28846         options = options || {};
28847         
28848         this.setMap(map);
28849     }
28850     
28851     
28852 });/**
28853  * @class Roo.bootstrap.Alert
28854  * @extends Roo.bootstrap.Component
28855  * Bootstrap Alert class - shows an alert area box
28856  * eg
28857  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
28858   Enter a valid email address
28859 </div>
28860  * @licence LGPL
28861  * @cfg {String} title The title of alert
28862  * @cfg {String} html The content of alert
28863  * @cfg {String} weight (  success | info | warning | danger )
28864  * @cfg {String} faicon font-awesomeicon
28865  * 
28866  * @constructor
28867  * Create a new alert
28868  * @param {Object} config The config object
28869  */
28870
28871
28872 Roo.bootstrap.Alert = function(config){
28873     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
28874     
28875 };
28876
28877 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
28878     
28879     title: '',
28880     html: '',
28881     weight: false,
28882     faicon: false,
28883     
28884     getAutoCreate : function()
28885     {
28886         
28887         var cfg = {
28888             tag : 'div',
28889             cls : 'alert',
28890             cn : [
28891                 {
28892                     tag : 'i',
28893                     cls : 'roo-alert-icon'
28894                     
28895                 },
28896                 {
28897                     tag : 'b',
28898                     cls : 'roo-alert-title',
28899                     html : this.title
28900                 },
28901                 {
28902                     tag : 'span',
28903                     cls : 'roo-alert-text',
28904                     html : this.html
28905                 }
28906             ]
28907         };
28908         
28909         if(this.faicon){
28910             cfg.cn[0].cls += ' fa ' + this.faicon;
28911         }
28912         
28913         if(this.weight){
28914             cfg.cls += ' alert-' + this.weight;
28915         }
28916         
28917         return cfg;
28918     },
28919     
28920     initEvents: function() 
28921     {
28922         this.el.setVisibilityMode(Roo.Element.DISPLAY);
28923     },
28924     
28925     setTitle : function(str)
28926     {
28927         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
28928     },
28929     
28930     setText : function(str)
28931     {
28932         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
28933     },
28934     
28935     setWeight : function(weight)
28936     {
28937         if(this.weight){
28938             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
28939         }
28940         
28941         this.weight = weight;
28942         
28943         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
28944     },
28945     
28946     setIcon : function(icon)
28947     {
28948         if(this.faicon){
28949             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
28950         }
28951         
28952         this.faicon = icon;
28953         
28954         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
28955     },
28956     
28957     hide: function() 
28958     {
28959         this.el.hide();   
28960     },
28961     
28962     show: function() 
28963     {  
28964         this.el.show();   
28965     }
28966     
28967 });
28968
28969  
28970 /*
28971 * Licence: LGPL
28972 */
28973
28974 /**
28975  * @class Roo.bootstrap.UploadCropbox
28976  * @extends Roo.bootstrap.Component
28977  * Bootstrap UploadCropbox class
28978  * @cfg {String} emptyText show when image has been loaded
28979  * @cfg {String} rotateNotify show when image too small to rotate
28980  * @cfg {Number} errorTimeout default 3000
28981  * @cfg {Number} minWidth default 300
28982  * @cfg {Number} minHeight default 300
28983  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
28984  * @cfg {Boolean} isDocument (true|false) default false
28985  * @cfg {String} url action url
28986  * @cfg {String} paramName default 'imageUpload'
28987  * @cfg {String} method default POST
28988  * @cfg {Boolean} loadMask (true|false) default true
28989  * @cfg {Boolean} loadingText default 'Loading...'
28990  * 
28991  * @constructor
28992  * Create a new UploadCropbox
28993  * @param {Object} config The config object
28994  */
28995
28996 Roo.bootstrap.UploadCropbox = function(config){
28997     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
28998     
28999     this.addEvents({
29000         /**
29001          * @event beforeselectfile
29002          * Fire before select file
29003          * @param {Roo.bootstrap.UploadCropbox} this
29004          */
29005         "beforeselectfile" : true,
29006         /**
29007          * @event initial
29008          * Fire after initEvent
29009          * @param {Roo.bootstrap.UploadCropbox} this
29010          */
29011         "initial" : true,
29012         /**
29013          * @event crop
29014          * Fire after initEvent
29015          * @param {Roo.bootstrap.UploadCropbox} this
29016          * @param {String} data
29017          */
29018         "crop" : true,
29019         /**
29020          * @event prepare
29021          * Fire when preparing the file data
29022          * @param {Roo.bootstrap.UploadCropbox} this
29023          * @param {Object} file
29024          */
29025         "prepare" : true,
29026         /**
29027          * @event exception
29028          * Fire when get exception
29029          * @param {Roo.bootstrap.UploadCropbox} this
29030          * @param {XMLHttpRequest} xhr
29031          */
29032         "exception" : true,
29033         /**
29034          * @event beforeloadcanvas
29035          * Fire before load the canvas
29036          * @param {Roo.bootstrap.UploadCropbox} this
29037          * @param {String} src
29038          */
29039         "beforeloadcanvas" : true,
29040         /**
29041          * @event trash
29042          * Fire when trash image
29043          * @param {Roo.bootstrap.UploadCropbox} this
29044          */
29045         "trash" : true,
29046         /**
29047          * @event download
29048          * Fire when download the image
29049          * @param {Roo.bootstrap.UploadCropbox} this
29050          */
29051         "download" : true,
29052         /**
29053          * @event footerbuttonclick
29054          * Fire when footerbuttonclick
29055          * @param {Roo.bootstrap.UploadCropbox} this
29056          * @param {String} type
29057          */
29058         "footerbuttonclick" : true,
29059         /**
29060          * @event resize
29061          * Fire when resize
29062          * @param {Roo.bootstrap.UploadCropbox} this
29063          */
29064         "resize" : true,
29065         /**
29066          * @event rotate
29067          * Fire when rotate the image
29068          * @param {Roo.bootstrap.UploadCropbox} this
29069          * @param {String} pos
29070          */
29071         "rotate" : true,
29072         /**
29073          * @event inspect
29074          * Fire when inspect the file
29075          * @param {Roo.bootstrap.UploadCropbox} this
29076          * @param {Object} file
29077          */
29078         "inspect" : true,
29079         /**
29080          * @event upload
29081          * Fire when xhr upload the file
29082          * @param {Roo.bootstrap.UploadCropbox} this
29083          * @param {Object} data
29084          */
29085         "upload" : true,
29086         /**
29087          * @event arrange
29088          * Fire when arrange the file data
29089          * @param {Roo.bootstrap.UploadCropbox} this
29090          * @param {Object} formData
29091          */
29092         "arrange" : true
29093     });
29094     
29095     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29096 };
29097
29098 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
29099     
29100     emptyText : 'Click to upload image',
29101     rotateNotify : 'Image is too small to rotate',
29102     errorTimeout : 3000,
29103     scale : 0,
29104     baseScale : 1,
29105     rotate : 0,
29106     dragable : false,
29107     pinching : false,
29108     mouseX : 0,
29109     mouseY : 0,
29110     cropData : false,
29111     minWidth : 300,
29112     minHeight : 300,
29113     file : false,
29114     exif : {},
29115     baseRotate : 1,
29116     cropType : 'image/jpeg',
29117     buttons : false,
29118     canvasLoaded : false,
29119     isDocument : false,
29120     method : 'POST',
29121     paramName : 'imageUpload',
29122     loadMask : true,
29123     loadingText : 'Loading...',
29124     maskEl : false,
29125     
29126     getAutoCreate : function()
29127     {
29128         var cfg = {
29129             tag : 'div',
29130             cls : 'roo-upload-cropbox',
29131             cn : [
29132                 {
29133                     tag : 'input',
29134                     cls : 'roo-upload-cropbox-selector',
29135                     type : 'file'
29136                 },
29137                 {
29138                     tag : 'div',
29139                     cls : 'roo-upload-cropbox-body',
29140                     style : 'cursor:pointer',
29141                     cn : [
29142                         {
29143                             tag : 'div',
29144                             cls : 'roo-upload-cropbox-preview'
29145                         },
29146                         {
29147                             tag : 'div',
29148                             cls : 'roo-upload-cropbox-thumb'
29149                         },
29150                         {
29151                             tag : 'div',
29152                             cls : 'roo-upload-cropbox-empty-notify',
29153                             html : this.emptyText
29154                         },
29155                         {
29156                             tag : 'div',
29157                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29158                             html : this.rotateNotify
29159                         }
29160                     ]
29161                 },
29162                 {
29163                     tag : 'div',
29164                     cls : 'roo-upload-cropbox-footer',
29165                     cn : {
29166                         tag : 'div',
29167                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29168                         cn : []
29169                     }
29170                 }
29171             ]
29172         };
29173         
29174         return cfg;
29175     },
29176     
29177     onRender : function(ct, position)
29178     {
29179         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29180         
29181         if (this.buttons.length) {
29182             
29183             Roo.each(this.buttons, function(bb) {
29184                 
29185                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29186                 
29187                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29188                 
29189             }, this);
29190         }
29191         
29192         if(this.loadMask){
29193             this.maskEl = this.el;
29194         }
29195     },
29196     
29197     initEvents : function()
29198     {
29199         this.urlAPI = (window.createObjectURL && window) || 
29200                                 (window.URL && URL.revokeObjectURL && URL) || 
29201                                 (window.webkitURL && webkitURL);
29202                         
29203         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29204         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29205         
29206         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29207         this.selectorEl.hide();
29208         
29209         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29210         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29211         
29212         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29213         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29214         this.thumbEl.hide();
29215         
29216         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29217         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29218         
29219         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29220         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29221         this.errorEl.hide();
29222         
29223         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29224         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29225         this.footerEl.hide();
29226         
29227         this.setThumbBoxSize();
29228         
29229         this.bind();
29230         
29231         this.resize();
29232         
29233         this.fireEvent('initial', this);
29234     },
29235
29236     bind : function()
29237     {
29238         var _this = this;
29239         
29240         window.addEventListener("resize", function() { _this.resize(); } );
29241         
29242         this.bodyEl.on('click', this.beforeSelectFile, this);
29243         
29244         if(Roo.isTouch){
29245             this.bodyEl.on('touchstart', this.onTouchStart, this);
29246             this.bodyEl.on('touchmove', this.onTouchMove, this);
29247             this.bodyEl.on('touchend', this.onTouchEnd, this);
29248         }
29249         
29250         if(!Roo.isTouch){
29251             this.bodyEl.on('mousedown', this.onMouseDown, this);
29252             this.bodyEl.on('mousemove', this.onMouseMove, this);
29253             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29254             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29255             Roo.get(document).on('mouseup', this.onMouseUp, this);
29256         }
29257         
29258         this.selectorEl.on('change', this.onFileSelected, this);
29259     },
29260     
29261     reset : function()
29262     {    
29263         this.scale = 0;
29264         this.baseScale = 1;
29265         this.rotate = 0;
29266         this.baseRotate = 1;
29267         this.dragable = false;
29268         this.pinching = false;
29269         this.mouseX = 0;
29270         this.mouseY = 0;
29271         this.cropData = false;
29272         this.notifyEl.dom.innerHTML = this.emptyText;
29273         
29274         this.selectorEl.dom.value = '';
29275         
29276     },
29277     
29278     resize : function()
29279     {
29280         if(this.fireEvent('resize', this) != false){
29281             this.setThumbBoxPosition();
29282             this.setCanvasPosition();
29283         }
29284     },
29285     
29286     onFooterButtonClick : function(e, el, o, type)
29287     {
29288         switch (type) {
29289             case 'rotate-left' :
29290                 this.onRotateLeft(e);
29291                 break;
29292             case 'rotate-right' :
29293                 this.onRotateRight(e);
29294                 break;
29295             case 'picture' :
29296                 this.beforeSelectFile(e);
29297                 break;
29298             case 'trash' :
29299                 this.trash(e);
29300                 break;
29301             case 'crop' :
29302                 this.crop(e);
29303                 break;
29304             case 'download' :
29305                 this.download(e);
29306                 break;
29307             default :
29308                 break;
29309         }
29310         
29311         this.fireEvent('footerbuttonclick', this, type);
29312     },
29313     
29314     beforeSelectFile : function(e)
29315     {
29316         e.preventDefault();
29317         
29318         if(this.fireEvent('beforeselectfile', this) != false){
29319             this.selectorEl.dom.click();
29320         }
29321     },
29322     
29323     onFileSelected : function(e)
29324     {
29325         e.preventDefault();
29326         
29327         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29328             return;
29329         }
29330         
29331         var file = this.selectorEl.dom.files[0];
29332         
29333         if(this.fireEvent('inspect', this, file) != false){
29334             this.prepare(file);
29335         }
29336         
29337     },
29338     
29339     trash : function(e)
29340     {
29341         this.fireEvent('trash', this);
29342     },
29343     
29344     download : function(e)
29345     {
29346         this.fireEvent('download', this);
29347     },
29348     
29349     loadCanvas : function(src)
29350     {   
29351         if(this.fireEvent('beforeloadcanvas', this, src) != false){
29352             
29353             this.reset();
29354             
29355             this.imageEl = document.createElement('img');
29356             
29357             var _this = this;
29358             
29359             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29360             
29361             this.imageEl.src = src;
29362         }
29363     },
29364     
29365     onLoadCanvas : function()
29366     {   
29367         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29368         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29369         
29370         this.bodyEl.un('click', this.beforeSelectFile, this);
29371         
29372         this.notifyEl.hide();
29373         this.thumbEl.show();
29374         this.footerEl.show();
29375         
29376         this.baseRotateLevel();
29377         
29378         if(this.isDocument){
29379             this.setThumbBoxSize();
29380         }
29381         
29382         this.setThumbBoxPosition();
29383         
29384         this.baseScaleLevel();
29385         
29386         this.draw();
29387         
29388         this.resize();
29389         
29390         this.canvasLoaded = true;
29391         
29392         if(this.loadMask){
29393             this.maskEl.unmask();
29394         }
29395         
29396     },
29397     
29398     setCanvasPosition : function()
29399     {   
29400         if(!this.canvasEl){
29401             return;
29402         }
29403         
29404         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
29405         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
29406         
29407         this.previewEl.setLeft(pw);
29408         this.previewEl.setTop(ph);
29409         
29410     },
29411     
29412     onMouseDown : function(e)
29413     {   
29414         e.stopEvent();
29415         
29416         this.dragable = true;
29417         this.pinching = false;
29418         
29419         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
29420             this.dragable = false;
29421             return;
29422         }
29423         
29424         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29425         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29426         
29427     },
29428     
29429     onMouseMove : function(e)
29430     {   
29431         e.stopEvent();
29432         
29433         if(!this.canvasLoaded){
29434             return;
29435         }
29436         
29437         if (!this.dragable){
29438             return;
29439         }
29440         
29441         var minX = Math.ceil(this.thumbEl.getLeft(true));
29442         var minY = Math.ceil(this.thumbEl.getTop(true));
29443         
29444         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
29445         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
29446         
29447         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29448         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29449         
29450         x = x - this.mouseX;
29451         y = y - this.mouseY;
29452         
29453         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
29454         var bgY = Math.ceil(y + this.previewEl.getTop(true));
29455         
29456         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
29457         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
29458         
29459         this.previewEl.setLeft(bgX);
29460         this.previewEl.setTop(bgY);
29461         
29462         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29463         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29464     },
29465     
29466     onMouseUp : function(e)
29467     {   
29468         e.stopEvent();
29469         
29470         this.dragable = false;
29471     },
29472     
29473     onMouseWheel : function(e)
29474     {   
29475         e.stopEvent();
29476         
29477         this.startScale = this.scale;
29478         
29479         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
29480         
29481         if(!this.zoomable()){
29482             this.scale = this.startScale;
29483             return;
29484         }
29485         
29486         this.draw();
29487         
29488         return;
29489     },
29490     
29491     zoomable : function()
29492     {
29493         var minScale = this.thumbEl.getWidth() / this.minWidth;
29494         
29495         if(this.minWidth < this.minHeight){
29496             minScale = this.thumbEl.getHeight() / this.minHeight;
29497         }
29498         
29499         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
29500         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
29501         
29502         if(
29503                 this.isDocument &&
29504                 (this.rotate == 0 || this.rotate == 180) && 
29505                 (
29506                     width > this.imageEl.OriginWidth || 
29507                     height > this.imageEl.OriginHeight ||
29508                     (width < this.minWidth && height < this.minHeight)
29509                 )
29510         ){
29511             return false;
29512         }
29513         
29514         if(
29515                 this.isDocument &&
29516                 (this.rotate == 90 || this.rotate == 270) && 
29517                 (
29518                     width > this.imageEl.OriginWidth || 
29519                     height > this.imageEl.OriginHeight ||
29520                     (width < this.minHeight && height < this.minWidth)
29521                 )
29522         ){
29523             return false;
29524         }
29525         
29526         if(
29527                 !this.isDocument &&
29528                 (this.rotate == 0 || this.rotate == 180) && 
29529                 (
29530                     width < this.minWidth || 
29531                     width > this.imageEl.OriginWidth || 
29532                     height < this.minHeight || 
29533                     height > this.imageEl.OriginHeight
29534                 )
29535         ){
29536             return false;
29537         }
29538         
29539         if(
29540                 !this.isDocument &&
29541                 (this.rotate == 90 || this.rotate == 270) && 
29542                 (
29543                     width < this.minHeight || 
29544                     width > this.imageEl.OriginWidth || 
29545                     height < this.minWidth || 
29546                     height > this.imageEl.OriginHeight
29547                 )
29548         ){
29549             return false;
29550         }
29551         
29552         return true;
29553         
29554     },
29555     
29556     onRotateLeft : function(e)
29557     {   
29558         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29559             
29560             var minScale = this.thumbEl.getWidth() / this.minWidth;
29561             
29562             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29563             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29564             
29565             this.startScale = this.scale;
29566             
29567             while (this.getScaleLevel() < minScale){
29568             
29569                 this.scale = this.scale + 1;
29570                 
29571                 if(!this.zoomable()){
29572                     break;
29573                 }
29574                 
29575                 if(
29576                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29577                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29578                 ){
29579                     continue;
29580                 }
29581                 
29582                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29583
29584                 this.draw();
29585                 
29586                 return;
29587             }
29588             
29589             this.scale = this.startScale;
29590             
29591             this.onRotateFail();
29592             
29593             return false;
29594         }
29595         
29596         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29597
29598         if(this.isDocument){
29599             this.setThumbBoxSize();
29600             this.setThumbBoxPosition();
29601             this.setCanvasPosition();
29602         }
29603         
29604         this.draw();
29605         
29606         this.fireEvent('rotate', this, 'left');
29607         
29608     },
29609     
29610     onRotateRight : function(e)
29611     {
29612         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29613             
29614             var minScale = this.thumbEl.getWidth() / this.minWidth;
29615         
29616             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29617             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29618             
29619             this.startScale = this.scale;
29620             
29621             while (this.getScaleLevel() < minScale){
29622             
29623                 this.scale = this.scale + 1;
29624                 
29625                 if(!this.zoomable()){
29626                     break;
29627                 }
29628                 
29629                 if(
29630                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29631                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29632                 ){
29633                     continue;
29634                 }
29635                 
29636                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29637
29638                 this.draw();
29639                 
29640                 return;
29641             }
29642             
29643             this.scale = this.startScale;
29644             
29645             this.onRotateFail();
29646             
29647             return false;
29648         }
29649         
29650         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29651
29652         if(this.isDocument){
29653             this.setThumbBoxSize();
29654             this.setThumbBoxPosition();
29655             this.setCanvasPosition();
29656         }
29657         
29658         this.draw();
29659         
29660         this.fireEvent('rotate', this, 'right');
29661     },
29662     
29663     onRotateFail : function()
29664     {
29665         this.errorEl.show(true);
29666         
29667         var _this = this;
29668         
29669         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
29670     },
29671     
29672     draw : function()
29673     {
29674         this.previewEl.dom.innerHTML = '';
29675         
29676         var canvasEl = document.createElement("canvas");
29677         
29678         var contextEl = canvasEl.getContext("2d");
29679         
29680         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29681         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29682         var center = this.imageEl.OriginWidth / 2;
29683         
29684         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
29685             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29686             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29687             center = this.imageEl.OriginHeight / 2;
29688         }
29689         
29690         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
29691         
29692         contextEl.translate(center, center);
29693         contextEl.rotate(this.rotate * Math.PI / 180);
29694
29695         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
29696         
29697         this.canvasEl = document.createElement("canvas");
29698         
29699         this.contextEl = this.canvasEl.getContext("2d");
29700         
29701         switch (this.rotate) {
29702             case 0 :
29703                 
29704                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29705                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29706                 
29707                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29708                 
29709                 break;
29710             case 90 : 
29711                 
29712                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29713                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29714                 
29715                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29716                     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);
29717                     break;
29718                 }
29719                 
29720                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29721                 
29722                 break;
29723             case 180 :
29724                 
29725                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29726                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29727                 
29728                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29729                     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);
29730                     break;
29731                 }
29732                 
29733                 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);
29734                 
29735                 break;
29736             case 270 :
29737                 
29738                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29739                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29740         
29741                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29742                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29743                     break;
29744                 }
29745                 
29746                 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);
29747                 
29748                 break;
29749             default : 
29750                 break;
29751         }
29752         
29753         this.previewEl.appendChild(this.canvasEl);
29754         
29755         this.setCanvasPosition();
29756     },
29757     
29758     crop : function()
29759     {
29760         if(!this.canvasLoaded){
29761             return;
29762         }
29763         
29764         var imageCanvas = document.createElement("canvas");
29765         
29766         var imageContext = imageCanvas.getContext("2d");
29767         
29768         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
29769         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
29770         
29771         var center = imageCanvas.width / 2;
29772         
29773         imageContext.translate(center, center);
29774         
29775         imageContext.rotate(this.rotate * Math.PI / 180);
29776         
29777         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
29778         
29779         var canvas = document.createElement("canvas");
29780         
29781         var context = canvas.getContext("2d");
29782                 
29783         canvas.width = this.minWidth;
29784         canvas.height = this.minHeight;
29785
29786         switch (this.rotate) {
29787             case 0 :
29788                 
29789                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
29790                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
29791                 
29792                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29793                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29794                 
29795                 var targetWidth = this.minWidth - 2 * x;
29796                 var targetHeight = this.minHeight - 2 * y;
29797                 
29798                 var scale = 1;
29799                 
29800                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29801                     scale = targetWidth / width;
29802                 }
29803                 
29804                 if(x > 0 && y == 0){
29805                     scale = targetHeight / height;
29806                 }
29807                 
29808                 if(x > 0 && y > 0){
29809                     scale = targetWidth / width;
29810                     
29811                     if(width < height){
29812                         scale = targetHeight / height;
29813                     }
29814                 }
29815                 
29816                 context.scale(scale, scale);
29817                 
29818                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29819                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29820
29821                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29822                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29823
29824                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29825                 
29826                 break;
29827             case 90 : 
29828                 
29829                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
29830                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
29831                 
29832                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29833                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29834                 
29835                 var targetWidth = this.minWidth - 2 * x;
29836                 var targetHeight = this.minHeight - 2 * y;
29837                 
29838                 var scale = 1;
29839                 
29840                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29841                     scale = targetWidth / width;
29842                 }
29843                 
29844                 if(x > 0 && y == 0){
29845                     scale = targetHeight / height;
29846                 }
29847                 
29848                 if(x > 0 && y > 0){
29849                     scale = targetWidth / width;
29850                     
29851                     if(width < height){
29852                         scale = targetHeight / height;
29853                     }
29854                 }
29855                 
29856                 context.scale(scale, scale);
29857                 
29858                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29859                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29860
29861                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29862                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29863                 
29864                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
29865                 
29866                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29867                 
29868                 break;
29869             case 180 :
29870                 
29871                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
29872                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
29873                 
29874                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29875                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29876                 
29877                 var targetWidth = this.minWidth - 2 * x;
29878                 var targetHeight = this.minHeight - 2 * y;
29879                 
29880                 var scale = 1;
29881                 
29882                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29883                     scale = targetWidth / width;
29884                 }
29885                 
29886                 if(x > 0 && y == 0){
29887                     scale = targetHeight / height;
29888                 }
29889                 
29890                 if(x > 0 && y > 0){
29891                     scale = targetWidth / width;
29892                     
29893                     if(width < height){
29894                         scale = targetHeight / height;
29895                     }
29896                 }
29897                 
29898                 context.scale(scale, scale);
29899                 
29900                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29901                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29902
29903                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29904                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29905
29906                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
29907                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
29908                 
29909                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29910                 
29911                 break;
29912             case 270 :
29913                 
29914                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
29915                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
29916                 
29917                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29918                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29919                 
29920                 var targetWidth = this.minWidth - 2 * x;
29921                 var targetHeight = this.minHeight - 2 * y;
29922                 
29923                 var scale = 1;
29924                 
29925                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29926                     scale = targetWidth / width;
29927                 }
29928                 
29929                 if(x > 0 && y == 0){
29930                     scale = targetHeight / height;
29931                 }
29932                 
29933                 if(x > 0 && y > 0){
29934                     scale = targetWidth / width;
29935                     
29936                     if(width < height){
29937                         scale = targetHeight / height;
29938                     }
29939                 }
29940                 
29941                 context.scale(scale, scale);
29942                 
29943                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29944                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29945
29946                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29947                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29948                 
29949                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
29950                 
29951                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29952                 
29953                 break;
29954             default : 
29955                 break;
29956         }
29957         
29958         this.cropData = canvas.toDataURL(this.cropType);
29959         
29960         if(this.fireEvent('crop', this, this.cropData) !== false){
29961             this.process(this.file, this.cropData);
29962         }
29963         
29964         return;
29965         
29966     },
29967     
29968     setThumbBoxSize : function()
29969     {
29970         var width, height;
29971         
29972         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
29973             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
29974             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
29975             
29976             this.minWidth = width;
29977             this.minHeight = height;
29978             
29979             if(this.rotate == 90 || this.rotate == 270){
29980                 this.minWidth = height;
29981                 this.minHeight = width;
29982             }
29983         }
29984         
29985         height = 300;
29986         width = Math.ceil(this.minWidth * height / this.minHeight);
29987         
29988         if(this.minWidth > this.minHeight){
29989             width = 300;
29990             height = Math.ceil(this.minHeight * width / this.minWidth);
29991         }
29992         
29993         this.thumbEl.setStyle({
29994             width : width + 'px',
29995             height : height + 'px'
29996         });
29997
29998         return;
29999             
30000     },
30001     
30002     setThumbBoxPosition : function()
30003     {
30004         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30005         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30006         
30007         this.thumbEl.setLeft(x);
30008         this.thumbEl.setTop(y);
30009         
30010     },
30011     
30012     baseRotateLevel : function()
30013     {
30014         this.baseRotate = 1;
30015         
30016         if(
30017                 typeof(this.exif) != 'undefined' &&
30018                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30019                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30020         ){
30021             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30022         }
30023         
30024         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30025         
30026     },
30027     
30028     baseScaleLevel : function()
30029     {
30030         var width, height;
30031         
30032         if(this.isDocument){
30033             
30034             if(this.baseRotate == 6 || this.baseRotate == 8){
30035             
30036                 height = this.thumbEl.getHeight();
30037                 this.baseScale = height / this.imageEl.OriginWidth;
30038
30039                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30040                     width = this.thumbEl.getWidth();
30041                     this.baseScale = width / this.imageEl.OriginHeight;
30042                 }
30043
30044                 return;
30045             }
30046
30047             height = this.thumbEl.getHeight();
30048             this.baseScale = height / this.imageEl.OriginHeight;
30049
30050             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30051                 width = this.thumbEl.getWidth();
30052                 this.baseScale = width / this.imageEl.OriginWidth;
30053             }
30054
30055             return;
30056         }
30057         
30058         if(this.baseRotate == 6 || this.baseRotate == 8){
30059             
30060             width = this.thumbEl.getHeight();
30061             this.baseScale = width / this.imageEl.OriginHeight;
30062             
30063             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30064                 height = this.thumbEl.getWidth();
30065                 this.baseScale = height / this.imageEl.OriginHeight;
30066             }
30067             
30068             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30069                 height = this.thumbEl.getWidth();
30070                 this.baseScale = height / this.imageEl.OriginHeight;
30071                 
30072                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30073                     width = this.thumbEl.getHeight();
30074                     this.baseScale = width / this.imageEl.OriginWidth;
30075                 }
30076             }
30077             
30078             return;
30079         }
30080         
30081         width = this.thumbEl.getWidth();
30082         this.baseScale = width / this.imageEl.OriginWidth;
30083         
30084         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30085             height = this.thumbEl.getHeight();
30086             this.baseScale = height / this.imageEl.OriginHeight;
30087         }
30088         
30089         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30090             
30091             height = this.thumbEl.getHeight();
30092             this.baseScale = height / this.imageEl.OriginHeight;
30093             
30094             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30095                 width = this.thumbEl.getWidth();
30096                 this.baseScale = width / this.imageEl.OriginWidth;
30097             }
30098             
30099         }
30100         
30101         return;
30102     },
30103     
30104     getScaleLevel : function()
30105     {
30106         return this.baseScale * Math.pow(1.1, this.scale);
30107     },
30108     
30109     onTouchStart : function(e)
30110     {
30111         if(!this.canvasLoaded){
30112             this.beforeSelectFile(e);
30113             return;
30114         }
30115         
30116         var touches = e.browserEvent.touches;
30117         
30118         if(!touches){
30119             return;
30120         }
30121         
30122         if(touches.length == 1){
30123             this.onMouseDown(e);
30124             return;
30125         }
30126         
30127         if(touches.length != 2){
30128             return;
30129         }
30130         
30131         var coords = [];
30132         
30133         for(var i = 0, finger; finger = touches[i]; i++){
30134             coords.push(finger.pageX, finger.pageY);
30135         }
30136         
30137         var x = Math.pow(coords[0] - coords[2], 2);
30138         var y = Math.pow(coords[1] - coords[3], 2);
30139         
30140         this.startDistance = Math.sqrt(x + y);
30141         
30142         this.startScale = this.scale;
30143         
30144         this.pinching = true;
30145         this.dragable = false;
30146         
30147     },
30148     
30149     onTouchMove : function(e)
30150     {
30151         if(!this.pinching && !this.dragable){
30152             return;
30153         }
30154         
30155         var touches = e.browserEvent.touches;
30156         
30157         if(!touches){
30158             return;
30159         }
30160         
30161         if(this.dragable){
30162             this.onMouseMove(e);
30163             return;
30164         }
30165         
30166         var coords = [];
30167         
30168         for(var i = 0, finger; finger = touches[i]; i++){
30169             coords.push(finger.pageX, finger.pageY);
30170         }
30171         
30172         var x = Math.pow(coords[0] - coords[2], 2);
30173         var y = Math.pow(coords[1] - coords[3], 2);
30174         
30175         this.endDistance = Math.sqrt(x + y);
30176         
30177         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30178         
30179         if(!this.zoomable()){
30180             this.scale = this.startScale;
30181             return;
30182         }
30183         
30184         this.draw();
30185         
30186     },
30187     
30188     onTouchEnd : function(e)
30189     {
30190         this.pinching = false;
30191         this.dragable = false;
30192         
30193     },
30194     
30195     process : function(file, crop)
30196     {
30197         if(this.loadMask){
30198             this.maskEl.mask(this.loadingText);
30199         }
30200         
30201         this.xhr = new XMLHttpRequest();
30202         
30203         file.xhr = this.xhr;
30204
30205         this.xhr.open(this.method, this.url, true);
30206         
30207         var headers = {
30208             "Accept": "application/json",
30209             "Cache-Control": "no-cache",
30210             "X-Requested-With": "XMLHttpRequest"
30211         };
30212         
30213         for (var headerName in headers) {
30214             var headerValue = headers[headerName];
30215             if (headerValue) {
30216                 this.xhr.setRequestHeader(headerName, headerValue);
30217             }
30218         }
30219         
30220         var _this = this;
30221         
30222         this.xhr.onload = function()
30223         {
30224             _this.xhrOnLoad(_this.xhr);
30225         }
30226         
30227         this.xhr.onerror = function()
30228         {
30229             _this.xhrOnError(_this.xhr);
30230         }
30231         
30232         var formData = new FormData();
30233
30234         formData.append('returnHTML', 'NO');
30235         
30236         if(crop){
30237             formData.append('crop', crop);
30238         }
30239         
30240         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30241             formData.append(this.paramName, file, file.name);
30242         }
30243         
30244         if(typeof(file.filename) != 'undefined'){
30245             formData.append('filename', file.filename);
30246         }
30247         
30248         if(typeof(file.mimetype) != 'undefined'){
30249             formData.append('mimetype', file.mimetype);
30250         }
30251         
30252         if(this.fireEvent('arrange', this, formData) != false){
30253             this.xhr.send(formData);
30254         };
30255     },
30256     
30257     xhrOnLoad : function(xhr)
30258     {
30259         if(this.loadMask){
30260             this.maskEl.unmask();
30261         }
30262         
30263         if (xhr.readyState !== 4) {
30264             this.fireEvent('exception', this, xhr);
30265             return;
30266         }
30267
30268         var response = Roo.decode(xhr.responseText);
30269         
30270         if(!response.success){
30271             this.fireEvent('exception', this, xhr);
30272             return;
30273         }
30274         
30275         var response = Roo.decode(xhr.responseText);
30276         
30277         this.fireEvent('upload', this, response);
30278         
30279     },
30280     
30281     xhrOnError : function()
30282     {
30283         if(this.loadMask){
30284             this.maskEl.unmask();
30285         }
30286         
30287         Roo.log('xhr on error');
30288         
30289         var response = Roo.decode(xhr.responseText);
30290           
30291         Roo.log(response);
30292         
30293     },
30294     
30295     prepare : function(file)
30296     {   
30297         if(this.loadMask){
30298             this.maskEl.mask(this.loadingText);
30299         }
30300         
30301         this.file = false;
30302         this.exif = {};
30303         
30304         if(typeof(file) === 'string'){
30305             this.loadCanvas(file);
30306             return;
30307         }
30308         
30309         if(!file || !this.urlAPI){
30310             return;
30311         }
30312         
30313         this.file = file;
30314         this.cropType = file.type;
30315         
30316         var _this = this;
30317         
30318         if(this.fireEvent('prepare', this, this.file) != false){
30319             
30320             var reader = new FileReader();
30321             
30322             reader.onload = function (e) {
30323                 if (e.target.error) {
30324                     Roo.log(e.target.error);
30325                     return;
30326                 }
30327                 
30328                 var buffer = e.target.result,
30329                     dataView = new DataView(buffer),
30330                     offset = 2,
30331                     maxOffset = dataView.byteLength - 4,
30332                     markerBytes,
30333                     markerLength;
30334                 
30335                 if (dataView.getUint16(0) === 0xffd8) {
30336                     while (offset < maxOffset) {
30337                         markerBytes = dataView.getUint16(offset);
30338                         
30339                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30340                             markerLength = dataView.getUint16(offset + 2) + 2;
30341                             if (offset + markerLength > dataView.byteLength) {
30342                                 Roo.log('Invalid meta data: Invalid segment size.');
30343                                 break;
30344                             }
30345                             
30346                             if(markerBytes == 0xffe1){
30347                                 _this.parseExifData(
30348                                     dataView,
30349                                     offset,
30350                                     markerLength
30351                                 );
30352                             }
30353                             
30354                             offset += markerLength;
30355                             
30356                             continue;
30357                         }
30358                         
30359                         break;
30360                     }
30361                     
30362                 }
30363                 
30364                 var url = _this.urlAPI.createObjectURL(_this.file);
30365                 
30366                 _this.loadCanvas(url);
30367                 
30368                 return;
30369             }
30370             
30371             reader.readAsArrayBuffer(this.file);
30372             
30373         }
30374         
30375     },
30376     
30377     parseExifData : function(dataView, offset, length)
30378     {
30379         var tiffOffset = offset + 10,
30380             littleEndian,
30381             dirOffset;
30382     
30383         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30384             // No Exif data, might be XMP data instead
30385             return;
30386         }
30387         
30388         // Check for the ASCII code for "Exif" (0x45786966):
30389         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30390             // No Exif data, might be XMP data instead
30391             return;
30392         }
30393         if (tiffOffset + 8 > dataView.byteLength) {
30394             Roo.log('Invalid Exif data: Invalid segment size.');
30395             return;
30396         }
30397         // Check for the two null bytes:
30398         if (dataView.getUint16(offset + 8) !== 0x0000) {
30399             Roo.log('Invalid Exif data: Missing byte alignment offset.');
30400             return;
30401         }
30402         // Check the byte alignment:
30403         switch (dataView.getUint16(tiffOffset)) {
30404         case 0x4949:
30405             littleEndian = true;
30406             break;
30407         case 0x4D4D:
30408             littleEndian = false;
30409             break;
30410         default:
30411             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
30412             return;
30413         }
30414         // Check for the TIFF tag marker (0x002A):
30415         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
30416             Roo.log('Invalid Exif data: Missing TIFF marker.');
30417             return;
30418         }
30419         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
30420         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
30421         
30422         this.parseExifTags(
30423             dataView,
30424             tiffOffset,
30425             tiffOffset + dirOffset,
30426             littleEndian
30427         );
30428     },
30429     
30430     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
30431     {
30432         var tagsNumber,
30433             dirEndOffset,
30434             i;
30435         if (dirOffset + 6 > dataView.byteLength) {
30436             Roo.log('Invalid Exif data: Invalid directory offset.');
30437             return;
30438         }
30439         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
30440         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
30441         if (dirEndOffset + 4 > dataView.byteLength) {
30442             Roo.log('Invalid Exif data: Invalid directory size.');
30443             return;
30444         }
30445         for (i = 0; i < tagsNumber; i += 1) {
30446             this.parseExifTag(
30447                 dataView,
30448                 tiffOffset,
30449                 dirOffset + 2 + 12 * i, // tag offset
30450                 littleEndian
30451             );
30452         }
30453         // Return the offset to the next directory:
30454         return dataView.getUint32(dirEndOffset, littleEndian);
30455     },
30456     
30457     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
30458     {
30459         var tag = dataView.getUint16(offset, littleEndian);
30460         
30461         this.exif[tag] = this.getExifValue(
30462             dataView,
30463             tiffOffset,
30464             offset,
30465             dataView.getUint16(offset + 2, littleEndian), // tag type
30466             dataView.getUint32(offset + 4, littleEndian), // tag length
30467             littleEndian
30468         );
30469     },
30470     
30471     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
30472     {
30473         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
30474             tagSize,
30475             dataOffset,
30476             values,
30477             i,
30478             str,
30479             c;
30480     
30481         if (!tagType) {
30482             Roo.log('Invalid Exif data: Invalid tag type.');
30483             return;
30484         }
30485         
30486         tagSize = tagType.size * length;
30487         // Determine if the value is contained in the dataOffset bytes,
30488         // or if the value at the dataOffset is a pointer to the actual data:
30489         dataOffset = tagSize > 4 ?
30490                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
30491         if (dataOffset + tagSize > dataView.byteLength) {
30492             Roo.log('Invalid Exif data: Invalid data offset.');
30493             return;
30494         }
30495         if (length === 1) {
30496             return tagType.getValue(dataView, dataOffset, littleEndian);
30497         }
30498         values = [];
30499         for (i = 0; i < length; i += 1) {
30500             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
30501         }
30502         
30503         if (tagType.ascii) {
30504             str = '';
30505             // Concatenate the chars:
30506             for (i = 0; i < values.length; i += 1) {
30507                 c = values[i];
30508                 // Ignore the terminating NULL byte(s):
30509                 if (c === '\u0000') {
30510                     break;
30511                 }
30512                 str += c;
30513             }
30514             return str;
30515         }
30516         return values;
30517     }
30518     
30519 });
30520
30521 Roo.apply(Roo.bootstrap.UploadCropbox, {
30522     tags : {
30523         'Orientation': 0x0112
30524     },
30525     
30526     Orientation: {
30527             1: 0, //'top-left',
30528 //            2: 'top-right',
30529             3: 180, //'bottom-right',
30530 //            4: 'bottom-left',
30531 //            5: 'left-top',
30532             6: 90, //'right-top',
30533 //            7: 'right-bottom',
30534             8: 270 //'left-bottom'
30535     },
30536     
30537     exifTagTypes : {
30538         // byte, 8-bit unsigned int:
30539         1: {
30540             getValue: function (dataView, dataOffset) {
30541                 return dataView.getUint8(dataOffset);
30542             },
30543             size: 1
30544         },
30545         // ascii, 8-bit byte:
30546         2: {
30547             getValue: function (dataView, dataOffset) {
30548                 return String.fromCharCode(dataView.getUint8(dataOffset));
30549             },
30550             size: 1,
30551             ascii: true
30552         },
30553         // short, 16 bit int:
30554         3: {
30555             getValue: function (dataView, dataOffset, littleEndian) {
30556                 return dataView.getUint16(dataOffset, littleEndian);
30557             },
30558             size: 2
30559         },
30560         // long, 32 bit int:
30561         4: {
30562             getValue: function (dataView, dataOffset, littleEndian) {
30563                 return dataView.getUint32(dataOffset, littleEndian);
30564             },
30565             size: 4
30566         },
30567         // rational = two long values, first is numerator, second is denominator:
30568         5: {
30569             getValue: function (dataView, dataOffset, littleEndian) {
30570                 return dataView.getUint32(dataOffset, littleEndian) /
30571                     dataView.getUint32(dataOffset + 4, littleEndian);
30572             },
30573             size: 8
30574         },
30575         // slong, 32 bit signed int:
30576         9: {
30577             getValue: function (dataView, dataOffset, littleEndian) {
30578                 return dataView.getInt32(dataOffset, littleEndian);
30579             },
30580             size: 4
30581         },
30582         // srational, two slongs, first is numerator, second is denominator:
30583         10: {
30584             getValue: function (dataView, dataOffset, littleEndian) {
30585                 return dataView.getInt32(dataOffset, littleEndian) /
30586                     dataView.getInt32(dataOffset + 4, littleEndian);
30587             },
30588             size: 8
30589         }
30590     },
30591     
30592     footer : {
30593         STANDARD : [
30594             {
30595                 tag : 'div',
30596                 cls : 'btn-group roo-upload-cropbox-rotate-left',
30597                 action : 'rotate-left',
30598                 cn : [
30599                     {
30600                         tag : 'button',
30601                         cls : 'btn btn-default',
30602                         html : '<i class="fa fa-undo"></i>'
30603                     }
30604                 ]
30605             },
30606             {
30607                 tag : 'div',
30608                 cls : 'btn-group roo-upload-cropbox-picture',
30609                 action : 'picture',
30610                 cn : [
30611                     {
30612                         tag : 'button',
30613                         cls : 'btn btn-default',
30614                         html : '<i class="fa fa-picture-o"></i>'
30615                     }
30616                 ]
30617             },
30618             {
30619                 tag : 'div',
30620                 cls : 'btn-group roo-upload-cropbox-rotate-right',
30621                 action : 'rotate-right',
30622                 cn : [
30623                     {
30624                         tag : 'button',
30625                         cls : 'btn btn-default',
30626                         html : '<i class="fa fa-repeat"></i>'
30627                     }
30628                 ]
30629             }
30630         ],
30631         DOCUMENT : [
30632             {
30633                 tag : 'div',
30634                 cls : 'btn-group roo-upload-cropbox-rotate-left',
30635                 action : 'rotate-left',
30636                 cn : [
30637                     {
30638                         tag : 'button',
30639                         cls : 'btn btn-default',
30640                         html : '<i class="fa fa-undo"></i>'
30641                     }
30642                 ]
30643             },
30644             {
30645                 tag : 'div',
30646                 cls : 'btn-group roo-upload-cropbox-download',
30647                 action : 'download',
30648                 cn : [
30649                     {
30650                         tag : 'button',
30651                         cls : 'btn btn-default',
30652                         html : '<i class="fa fa-download"></i>'
30653                     }
30654                 ]
30655             },
30656             {
30657                 tag : 'div',
30658                 cls : 'btn-group roo-upload-cropbox-crop',
30659                 action : 'crop',
30660                 cn : [
30661                     {
30662                         tag : 'button',
30663                         cls : 'btn btn-default',
30664                         html : '<i class="fa fa-crop"></i>'
30665                     }
30666                 ]
30667             },
30668             {
30669                 tag : 'div',
30670                 cls : 'btn-group roo-upload-cropbox-trash',
30671                 action : 'trash',
30672                 cn : [
30673                     {
30674                         tag : 'button',
30675                         cls : 'btn btn-default',
30676                         html : '<i class="fa fa-trash"></i>'
30677                     }
30678                 ]
30679             },
30680             {
30681                 tag : 'div',
30682                 cls : 'btn-group roo-upload-cropbox-rotate-right',
30683                 action : 'rotate-right',
30684                 cn : [
30685                     {
30686                         tag : 'button',
30687                         cls : 'btn btn-default',
30688                         html : '<i class="fa fa-repeat"></i>'
30689                     }
30690                 ]
30691             }
30692         ],
30693         ROTATOR : [
30694             {
30695                 tag : 'div',
30696                 cls : 'btn-group roo-upload-cropbox-rotate-left',
30697                 action : 'rotate-left',
30698                 cn : [
30699                     {
30700                         tag : 'button',
30701                         cls : 'btn btn-default',
30702                         html : '<i class="fa fa-undo"></i>'
30703                     }
30704                 ]
30705             },
30706             {
30707                 tag : 'div',
30708                 cls : 'btn-group roo-upload-cropbox-rotate-right',
30709                 action : 'rotate-right',
30710                 cn : [
30711                     {
30712                         tag : 'button',
30713                         cls : 'btn btn-default',
30714                         html : '<i class="fa fa-repeat"></i>'
30715                     }
30716                 ]
30717             }
30718         ]
30719     }
30720 });
30721
30722 /*
30723 * Licence: LGPL
30724 */
30725
30726 /**
30727  * @class Roo.bootstrap.DocumentManager
30728  * @extends Roo.bootstrap.Component
30729  * Bootstrap DocumentManager class
30730  * @cfg {String} paramName default 'imageUpload'
30731  * @cfg {String} toolTipName default 'filename'
30732  * @cfg {String} method default POST
30733  * @cfg {String} url action url
30734  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
30735  * @cfg {Boolean} multiple multiple upload default true
30736  * @cfg {Number} thumbSize default 300
30737  * @cfg {String} fieldLabel
30738  * @cfg {Number} labelWidth default 4
30739  * @cfg {String} labelAlign (left|top) default left
30740  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
30741 * @cfg {Number} labellg set the width of label (1-12)
30742  * @cfg {Number} labelmd set the width of label (1-12)
30743  * @cfg {Number} labelsm set the width of label (1-12)
30744  * @cfg {Number} labelxs set the width of label (1-12)
30745  * 
30746  * @constructor
30747  * Create a new DocumentManager
30748  * @param {Object} config The config object
30749  */
30750
30751 Roo.bootstrap.DocumentManager = function(config){
30752     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
30753     
30754     this.files = [];
30755     this.delegates = [];
30756     
30757     this.addEvents({
30758         /**
30759          * @event initial
30760          * Fire when initial the DocumentManager
30761          * @param {Roo.bootstrap.DocumentManager} this
30762          */
30763         "initial" : true,
30764         /**
30765          * @event inspect
30766          * inspect selected file
30767          * @param {Roo.bootstrap.DocumentManager} this
30768          * @param {File} file
30769          */
30770         "inspect" : true,
30771         /**
30772          * @event exception
30773          * Fire when xhr load exception
30774          * @param {Roo.bootstrap.DocumentManager} this
30775          * @param {XMLHttpRequest} xhr
30776          */
30777         "exception" : true,
30778         /**
30779          * @event afterupload
30780          * Fire when xhr load exception
30781          * @param {Roo.bootstrap.DocumentManager} this
30782          * @param {XMLHttpRequest} xhr
30783          */
30784         "afterupload" : true,
30785         /**
30786          * @event prepare
30787          * prepare the form data
30788          * @param {Roo.bootstrap.DocumentManager} this
30789          * @param {Object} formData
30790          */
30791         "prepare" : true,
30792         /**
30793          * @event remove
30794          * Fire when remove the file
30795          * @param {Roo.bootstrap.DocumentManager} this
30796          * @param {Object} file
30797          */
30798         "remove" : true,
30799         /**
30800          * @event refresh
30801          * Fire after refresh the file
30802          * @param {Roo.bootstrap.DocumentManager} this
30803          */
30804         "refresh" : true,
30805         /**
30806          * @event click
30807          * Fire after click the image
30808          * @param {Roo.bootstrap.DocumentManager} this
30809          * @param {Object} file
30810          */
30811         "click" : true,
30812         /**
30813          * @event edit
30814          * Fire when upload a image and editable set to true
30815          * @param {Roo.bootstrap.DocumentManager} this
30816          * @param {Object} file
30817          */
30818         "edit" : true,
30819         /**
30820          * @event beforeselectfile
30821          * Fire before select file
30822          * @param {Roo.bootstrap.DocumentManager} this
30823          */
30824         "beforeselectfile" : true,
30825         /**
30826          * @event process
30827          * Fire before process file
30828          * @param {Roo.bootstrap.DocumentManager} this
30829          * @param {Object} file
30830          */
30831         "process" : true,
30832         /**
30833          * @event previewrendered
30834          * Fire when preview rendered
30835          * @param {Roo.bootstrap.DocumentManager} this
30836          * @param {Object} file
30837          */
30838         "previewrendered" : true,
30839         /**
30840          */
30841         "previewResize" : true
30842         
30843     });
30844 };
30845
30846 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
30847     
30848     boxes : 0,
30849     inputName : '',
30850     thumbSize : 300,
30851     multiple : true,
30852     files : false,
30853     method : 'POST',
30854     url : '',
30855     paramName : 'imageUpload',
30856     toolTipName : 'filename',
30857     fieldLabel : '',
30858     labelWidth : 4,
30859     labelAlign : 'left',
30860     editable : true,
30861     delegates : false,
30862     xhr : false, 
30863     
30864     labellg : 0,
30865     labelmd : 0,
30866     labelsm : 0,
30867     labelxs : 0,
30868     
30869     getAutoCreate : function()
30870     {   
30871         var managerWidget = {
30872             tag : 'div',
30873             cls : 'roo-document-manager',
30874             cn : [
30875                 {
30876                     tag : 'input',
30877                     cls : 'roo-document-manager-selector',
30878                     type : 'file'
30879                 },
30880                 {
30881                     tag : 'div',
30882                     cls : 'roo-document-manager-uploader',
30883                     cn : [
30884                         {
30885                             tag : 'div',
30886                             cls : 'roo-document-manager-upload-btn',
30887                             html : '<i class="fa fa-plus"></i>'
30888                         }
30889                     ]
30890                     
30891                 }
30892             ]
30893         };
30894         
30895         var content = [
30896             {
30897                 tag : 'div',
30898                 cls : 'column col-md-12',
30899                 cn : managerWidget
30900             }
30901         ];
30902         
30903         if(this.fieldLabel.length){
30904             
30905             content = [
30906                 {
30907                     tag : 'div',
30908                     cls : 'column col-md-12',
30909                     html : this.fieldLabel
30910                 },
30911                 {
30912                     tag : 'div',
30913                     cls : 'column col-md-12',
30914                     cn : managerWidget
30915                 }
30916             ];
30917
30918             if(this.labelAlign == 'left'){
30919                 content = [
30920                     {
30921                         tag : 'div',
30922                         cls : 'column',
30923                         html : this.fieldLabel
30924                     },
30925                     {
30926                         tag : 'div',
30927                         cls : 'column',
30928                         cn : managerWidget
30929                     }
30930                 ];
30931                 
30932                 if(this.labelWidth > 12){
30933                     content[0].style = "width: " + this.labelWidth + 'px';
30934                 }
30935
30936                 if(this.labelWidth < 13 && this.labelmd == 0){
30937                     this.labelmd = this.labelWidth;
30938                 }
30939
30940                 if(this.labellg > 0){
30941                     content[0].cls += ' col-lg-' + this.labellg;
30942                     content[1].cls += ' col-lg-' + (12 - this.labellg);
30943                 }
30944
30945                 if(this.labelmd > 0){
30946                     content[0].cls += ' col-md-' + this.labelmd;
30947                     content[1].cls += ' col-md-' + (12 - this.labelmd);
30948                 }
30949
30950                 if(this.labelsm > 0){
30951                     content[0].cls += ' col-sm-' + this.labelsm;
30952                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
30953                 }
30954
30955                 if(this.labelxs > 0){
30956                     content[0].cls += ' col-xs-' + this.labelxs;
30957                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
30958                 }
30959                 
30960             }
30961         }
30962         
30963         var cfg = {
30964             tag : 'div',
30965             cls : 'row clearfix',
30966             cn : content
30967         };
30968         
30969         return cfg;
30970         
30971     },
30972     
30973     initEvents : function()
30974     {
30975         this.managerEl = this.el.select('.roo-document-manager', true).first();
30976         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30977         
30978         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
30979         this.selectorEl.hide();
30980         
30981         if(this.multiple){
30982             this.selectorEl.attr('multiple', 'multiple');
30983         }
30984         
30985         this.selectorEl.on('change', this.onFileSelected, this);
30986         
30987         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
30988         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30989         
30990         this.uploader.on('click', this.onUploaderClick, this);
30991         
30992         this.renderProgressDialog();
30993         
30994         var _this = this;
30995         
30996         window.addEventListener("resize", function() { _this.refresh(); } );
30997         
30998         this.fireEvent('initial', this);
30999     },
31000     
31001     renderProgressDialog : function()
31002     {
31003         var _this = this;
31004         
31005         this.progressDialog = new Roo.bootstrap.Modal({
31006             cls : 'roo-document-manager-progress-dialog',
31007             allow_close : false,
31008             animate : false,
31009             title : '',
31010             buttons : [
31011                 {
31012                     name  :'cancel',
31013                     weight : 'danger',
31014                     html : 'Cancel'
31015                 }
31016             ], 
31017             listeners : { 
31018                 btnclick : function() {
31019                     _this.uploadCancel();
31020                     this.hide();
31021                 }
31022             }
31023         });
31024          
31025         this.progressDialog.render(Roo.get(document.body));
31026          
31027         this.progress = new Roo.bootstrap.Progress({
31028             cls : 'roo-document-manager-progress',
31029             active : true,
31030             striped : true
31031         });
31032         
31033         this.progress.render(this.progressDialog.getChildContainer());
31034         
31035         this.progressBar = new Roo.bootstrap.ProgressBar({
31036             cls : 'roo-document-manager-progress-bar',
31037             aria_valuenow : 0,
31038             aria_valuemin : 0,
31039             aria_valuemax : 12,
31040             panel : 'success'
31041         });
31042         
31043         this.progressBar.render(this.progress.getChildContainer());
31044     },
31045     
31046     onUploaderClick : function(e)
31047     {
31048         e.preventDefault();
31049      
31050         if(this.fireEvent('beforeselectfile', this) != false){
31051             this.selectorEl.dom.click();
31052         }
31053         
31054     },
31055     
31056     onFileSelected : function(e)
31057     {
31058         e.preventDefault();
31059         
31060         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31061             return;
31062         }
31063         
31064         Roo.each(this.selectorEl.dom.files, function(file){
31065             if(this.fireEvent('inspect', this, file) != false){
31066                 this.files.push(file);
31067             }
31068         }, this);
31069         
31070         this.queue();
31071         
31072     },
31073     
31074     queue : function()
31075     {
31076         this.selectorEl.dom.value = '';
31077         
31078         if(!this.files || !this.files.length){
31079             return;
31080         }
31081         
31082         if(this.boxes > 0 && this.files.length > this.boxes){
31083             this.files = this.files.slice(0, this.boxes);
31084         }
31085         
31086         this.uploader.show();
31087         
31088         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31089             this.uploader.hide();
31090         }
31091         
31092         var _this = this;
31093         
31094         var files = [];
31095         
31096         var docs = [];
31097         
31098         Roo.each(this.files, function(file){
31099             
31100             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31101                 var f = this.renderPreview(file);
31102                 files.push(f);
31103                 return;
31104             }
31105             
31106             if(file.type.indexOf('image') != -1){
31107                 this.delegates.push(
31108                     (function(){
31109                         _this.process(file);
31110                     }).createDelegate(this)
31111                 );
31112         
31113                 return;
31114             }
31115             
31116             docs.push(
31117                 (function(){
31118                     _this.process(file);
31119                 }).createDelegate(this)
31120             );
31121             
31122         }, this);
31123         
31124         this.files = files;
31125         
31126         this.delegates = this.delegates.concat(docs);
31127         
31128         if(!this.delegates.length){
31129             this.refresh();
31130             return;
31131         }
31132         
31133         this.progressBar.aria_valuemax = this.delegates.length;
31134         
31135         this.arrange();
31136         
31137         return;
31138     },
31139     
31140     arrange : function()
31141     {
31142         if(!this.delegates.length){
31143             this.progressDialog.hide();
31144             this.refresh();
31145             return;
31146         }
31147         
31148         var delegate = this.delegates.shift();
31149         
31150         this.progressDialog.show();
31151         
31152         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31153         
31154         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31155         
31156         delegate();
31157     },
31158     
31159     refresh : function()
31160     {
31161         this.uploader.show();
31162         
31163         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31164             this.uploader.hide();
31165         }
31166         
31167         Roo.isTouch ? this.closable(false) : this.closable(true);
31168         
31169         this.fireEvent('refresh', this);
31170     },
31171     
31172     onRemove : function(e, el, o)
31173     {
31174         e.preventDefault();
31175         
31176         this.fireEvent('remove', this, o);
31177         
31178     },
31179     
31180     remove : function(o)
31181     {
31182         var files = [];
31183         
31184         Roo.each(this.files, function(file){
31185             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31186                 files.push(file);
31187                 return;
31188             }
31189
31190             o.target.remove();
31191
31192         }, this);
31193         
31194         this.files = files;
31195         
31196         this.refresh();
31197     },
31198     
31199     clear : function()
31200     {
31201         Roo.each(this.files, function(file){
31202             if(!file.target){
31203                 return;
31204             }
31205             
31206             file.target.remove();
31207
31208         }, this);
31209         
31210         this.files = [];
31211         
31212         this.refresh();
31213     },
31214     
31215     onClick : function(e, el, o)
31216     {
31217         e.preventDefault();
31218         
31219         this.fireEvent('click', this, o);
31220         
31221     },
31222     
31223     closable : function(closable)
31224     {
31225         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31226             
31227             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31228             
31229             if(closable){
31230                 el.show();
31231                 return;
31232             }
31233             
31234             el.hide();
31235             
31236         }, this);
31237     },
31238     
31239     xhrOnLoad : function(xhr)
31240     {
31241         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31242             el.remove();
31243         }, this);
31244         
31245         if (xhr.readyState !== 4) {
31246             this.arrange();
31247             this.fireEvent('exception', this, xhr);
31248             return;
31249         }
31250
31251         var response = Roo.decode(xhr.responseText);
31252         
31253         if(!response.success){
31254             this.arrange();
31255             this.fireEvent('exception', this, xhr);
31256             return;
31257         }
31258         
31259         var file = this.renderPreview(response.data);
31260         
31261         this.files.push(file);
31262         
31263         this.arrange();
31264         
31265         this.fireEvent('afterupload', this, xhr);
31266         
31267     },
31268     
31269     xhrOnError : function(xhr)
31270     {
31271         Roo.log('xhr on error');
31272         
31273         var response = Roo.decode(xhr.responseText);
31274           
31275         Roo.log(response);
31276         
31277         this.arrange();
31278     },
31279     
31280     process : function(file)
31281     {
31282         if(this.fireEvent('process', this, file) !== false){
31283             if(this.editable && file.type.indexOf('image') != -1){
31284                 this.fireEvent('edit', this, file);
31285                 return;
31286             }
31287
31288             this.uploadStart(file, false);
31289
31290             return;
31291         }
31292         
31293     },
31294     
31295     uploadStart : function(file, crop)
31296     {
31297         this.xhr = new XMLHttpRequest();
31298         
31299         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31300             this.arrange();
31301             return;
31302         }
31303         
31304         file.xhr = this.xhr;
31305             
31306         this.managerEl.createChild({
31307             tag : 'div',
31308             cls : 'roo-document-manager-loading',
31309             cn : [
31310                 {
31311                     tag : 'div',
31312                     tooltip : file.name,
31313                     cls : 'roo-document-manager-thumb',
31314                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31315                 }
31316             ]
31317
31318         });
31319
31320         this.xhr.open(this.method, this.url, true);
31321         
31322         var headers = {
31323             "Accept": "application/json",
31324             "Cache-Control": "no-cache",
31325             "X-Requested-With": "XMLHttpRequest"
31326         };
31327         
31328         for (var headerName in headers) {
31329             var headerValue = headers[headerName];
31330             if (headerValue) {
31331                 this.xhr.setRequestHeader(headerName, headerValue);
31332             }
31333         }
31334         
31335         var _this = this;
31336         
31337         this.xhr.onload = function()
31338         {
31339             _this.xhrOnLoad(_this.xhr);
31340         }
31341         
31342         this.xhr.onerror = function()
31343         {
31344             _this.xhrOnError(_this.xhr);
31345         }
31346         
31347         var formData = new FormData();
31348
31349         formData.append('returnHTML', 'NO');
31350         
31351         if(crop){
31352             formData.append('crop', crop);
31353         }
31354         
31355         formData.append(this.paramName, file, file.name);
31356         
31357         var options = {
31358             file : file, 
31359             manually : false
31360         };
31361         
31362         if(this.fireEvent('prepare', this, formData, options) != false){
31363             
31364             if(options.manually){
31365                 return;
31366             }
31367             
31368             this.xhr.send(formData);
31369             return;
31370         };
31371         
31372         this.uploadCancel();
31373     },
31374     
31375     uploadCancel : function()
31376     {
31377         if (this.xhr) {
31378             this.xhr.abort();
31379         }
31380         
31381         this.delegates = [];
31382         
31383         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31384             el.remove();
31385         }, this);
31386         
31387         this.arrange();
31388     },
31389     
31390     renderPreview : function(file)
31391     {
31392         if(typeof(file.target) != 'undefined' && file.target){
31393             return file;
31394         }
31395         
31396         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
31397         
31398         var previewEl = this.managerEl.createChild({
31399             tag : 'div',
31400             cls : 'roo-document-manager-preview',
31401             cn : [
31402                 {
31403                     tag : 'div',
31404                     tooltip : file[this.toolTipName],
31405                     cls : 'roo-document-manager-thumb',
31406                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
31407                 },
31408                 {
31409                     tag : 'button',
31410                     cls : 'close',
31411                     html : '<i class="fa fa-times-circle"></i>'
31412                 }
31413             ]
31414         });
31415
31416         var close = previewEl.select('button.close', true).first();
31417
31418         close.on('click', this.onRemove, this, file);
31419
31420         file.target = previewEl;
31421
31422         var image = previewEl.select('img', true).first();
31423         
31424         var _this = this;
31425         
31426         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
31427         
31428         image.on('click', this.onClick, this, file);
31429         
31430         this.fireEvent('previewrendered', this, file);
31431         
31432         return file;
31433         
31434     },
31435     
31436     onPreviewLoad : function(file, image)
31437     {
31438         if(typeof(file.target) == 'undefined' || !file.target){
31439             return;
31440         }
31441         
31442         var width = image.dom.naturalWidth || image.dom.width;
31443         var height = image.dom.naturalHeight || image.dom.height;
31444         
31445         if(!this.previewResize) {
31446             return;
31447         }
31448         
31449         if(width > height){
31450             file.target.addClass('wide');
31451             return;
31452         }
31453         
31454         file.target.addClass('tall');
31455         return;
31456         
31457     },
31458     
31459     uploadFromSource : function(file, crop)
31460     {
31461         this.xhr = new XMLHttpRequest();
31462         
31463         this.managerEl.createChild({
31464             tag : 'div',
31465             cls : 'roo-document-manager-loading',
31466             cn : [
31467                 {
31468                     tag : 'div',
31469                     tooltip : file.name,
31470                     cls : 'roo-document-manager-thumb',
31471                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31472                 }
31473             ]
31474
31475         });
31476
31477         this.xhr.open(this.method, this.url, true);
31478         
31479         var headers = {
31480             "Accept": "application/json",
31481             "Cache-Control": "no-cache",
31482             "X-Requested-With": "XMLHttpRequest"
31483         };
31484         
31485         for (var headerName in headers) {
31486             var headerValue = headers[headerName];
31487             if (headerValue) {
31488                 this.xhr.setRequestHeader(headerName, headerValue);
31489             }
31490         }
31491         
31492         var _this = this;
31493         
31494         this.xhr.onload = function()
31495         {
31496             _this.xhrOnLoad(_this.xhr);
31497         }
31498         
31499         this.xhr.onerror = function()
31500         {
31501             _this.xhrOnError(_this.xhr);
31502         }
31503         
31504         var formData = new FormData();
31505
31506         formData.append('returnHTML', 'NO');
31507         
31508         formData.append('crop', crop);
31509         
31510         if(typeof(file.filename) != 'undefined'){
31511             formData.append('filename', file.filename);
31512         }
31513         
31514         if(typeof(file.mimetype) != 'undefined'){
31515             formData.append('mimetype', file.mimetype);
31516         }
31517         
31518         Roo.log(formData);
31519         
31520         if(this.fireEvent('prepare', this, formData) != false){
31521             this.xhr.send(formData);
31522         };
31523     }
31524 });
31525
31526 /*
31527 * Licence: LGPL
31528 */
31529
31530 /**
31531  * @class Roo.bootstrap.DocumentViewer
31532  * @extends Roo.bootstrap.Component
31533  * Bootstrap DocumentViewer class
31534  * @cfg {Boolean} showDownload (true|false) show download button (default true)
31535  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
31536  * 
31537  * @constructor
31538  * Create a new DocumentViewer
31539  * @param {Object} config The config object
31540  */
31541
31542 Roo.bootstrap.DocumentViewer = function(config){
31543     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
31544     
31545     this.addEvents({
31546         /**
31547          * @event initial
31548          * Fire after initEvent
31549          * @param {Roo.bootstrap.DocumentViewer} this
31550          */
31551         "initial" : true,
31552         /**
31553          * @event click
31554          * Fire after click
31555          * @param {Roo.bootstrap.DocumentViewer} this
31556          */
31557         "click" : true,
31558         /**
31559          * @event download
31560          * Fire after download button
31561          * @param {Roo.bootstrap.DocumentViewer} this
31562          */
31563         "download" : true,
31564         /**
31565          * @event trash
31566          * Fire after trash button
31567          * @param {Roo.bootstrap.DocumentViewer} this
31568          */
31569         "trash" : true
31570         
31571     });
31572 };
31573
31574 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
31575     
31576     showDownload : true,
31577     
31578     showTrash : true,
31579     
31580     getAutoCreate : function()
31581     {
31582         var cfg = {
31583             tag : 'div',
31584             cls : 'roo-document-viewer',
31585             cn : [
31586                 {
31587                     tag : 'div',
31588                     cls : 'roo-document-viewer-body',
31589                     cn : [
31590                         {
31591                             tag : 'div',
31592                             cls : 'roo-document-viewer-thumb',
31593                             cn : [
31594                                 {
31595                                     tag : 'img',
31596                                     cls : 'roo-document-viewer-image'
31597                                 }
31598                             ]
31599                         }
31600                     ]
31601                 },
31602                 {
31603                     tag : 'div',
31604                     cls : 'roo-document-viewer-footer',
31605                     cn : {
31606                         tag : 'div',
31607                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
31608                         cn : [
31609                             {
31610                                 tag : 'div',
31611                                 cls : 'btn-group roo-document-viewer-download',
31612                                 cn : [
31613                                     {
31614                                         tag : 'button',
31615                                         cls : 'btn btn-default',
31616                                         html : '<i class="fa fa-download"></i>'
31617                                     }
31618                                 ]
31619                             },
31620                             {
31621                                 tag : 'div',
31622                                 cls : 'btn-group roo-document-viewer-trash',
31623                                 cn : [
31624                                     {
31625                                         tag : 'button',
31626                                         cls : 'btn btn-default',
31627                                         html : '<i class="fa fa-trash"></i>'
31628                                     }
31629                                 ]
31630                             }
31631                         ]
31632                     }
31633                 }
31634             ]
31635         };
31636         
31637         return cfg;
31638     },
31639     
31640     initEvents : function()
31641     {
31642         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
31643         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
31644         
31645         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
31646         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
31647         
31648         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
31649         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
31650         
31651         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
31652         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
31653         
31654         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
31655         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
31656         
31657         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
31658         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
31659         
31660         this.bodyEl.on('click', this.onClick, this);
31661         this.downloadBtn.on('click', this.onDownload, this);
31662         this.trashBtn.on('click', this.onTrash, this);
31663         
31664         this.downloadBtn.hide();
31665         this.trashBtn.hide();
31666         
31667         if(this.showDownload){
31668             this.downloadBtn.show();
31669         }
31670         
31671         if(this.showTrash){
31672             this.trashBtn.show();
31673         }
31674         
31675         if(!this.showDownload && !this.showTrash) {
31676             this.footerEl.hide();
31677         }
31678         
31679     },
31680     
31681     initial : function()
31682     {
31683         this.fireEvent('initial', this);
31684         
31685     },
31686     
31687     onClick : function(e)
31688     {
31689         e.preventDefault();
31690         
31691         this.fireEvent('click', this);
31692     },
31693     
31694     onDownload : function(e)
31695     {
31696         e.preventDefault();
31697         
31698         this.fireEvent('download', this);
31699     },
31700     
31701     onTrash : function(e)
31702     {
31703         e.preventDefault();
31704         
31705         this.fireEvent('trash', this);
31706     }
31707     
31708 });
31709 /*
31710  * - LGPL
31711  *
31712  * nav progress bar
31713  * 
31714  */
31715
31716 /**
31717  * @class Roo.bootstrap.NavProgressBar
31718  * @extends Roo.bootstrap.Component
31719  * Bootstrap NavProgressBar class
31720  * 
31721  * @constructor
31722  * Create a new nav progress bar
31723  * @param {Object} config The config object
31724  */
31725
31726 Roo.bootstrap.NavProgressBar = function(config){
31727     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
31728
31729     this.bullets = this.bullets || [];
31730    
31731 //    Roo.bootstrap.NavProgressBar.register(this);
31732      this.addEvents({
31733         /**
31734              * @event changed
31735              * Fires when the active item changes
31736              * @param {Roo.bootstrap.NavProgressBar} this
31737              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
31738              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
31739          */
31740         'changed': true
31741      });
31742     
31743 };
31744
31745 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
31746     
31747     bullets : [],
31748     barItems : [],
31749     
31750     getAutoCreate : function()
31751     {
31752         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
31753         
31754         cfg = {
31755             tag : 'div',
31756             cls : 'roo-navigation-bar-group',
31757             cn : [
31758                 {
31759                     tag : 'div',
31760                     cls : 'roo-navigation-top-bar'
31761                 },
31762                 {
31763                     tag : 'div',
31764                     cls : 'roo-navigation-bullets-bar',
31765                     cn : [
31766                         {
31767                             tag : 'ul',
31768                             cls : 'roo-navigation-bar'
31769                         }
31770                     ]
31771                 },
31772                 
31773                 {
31774                     tag : 'div',
31775                     cls : 'roo-navigation-bottom-bar'
31776                 }
31777             ]
31778             
31779         };
31780         
31781         return cfg;
31782         
31783     },
31784     
31785     initEvents: function() 
31786     {
31787         
31788     },
31789     
31790     onRender : function(ct, position) 
31791     {
31792         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
31793         
31794         if(this.bullets.length){
31795             Roo.each(this.bullets, function(b){
31796                this.addItem(b);
31797             }, this);
31798         }
31799         
31800         this.format();
31801         
31802     },
31803     
31804     addItem : function(cfg)
31805     {
31806         var item = new Roo.bootstrap.NavProgressItem(cfg);
31807         
31808         item.parentId = this.id;
31809         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
31810         
31811         if(cfg.html){
31812             var top = new Roo.bootstrap.Element({
31813                 tag : 'div',
31814                 cls : 'roo-navigation-bar-text'
31815             });
31816             
31817             var bottom = new Roo.bootstrap.Element({
31818                 tag : 'div',
31819                 cls : 'roo-navigation-bar-text'
31820             });
31821             
31822             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
31823             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
31824             
31825             var topText = new Roo.bootstrap.Element({
31826                 tag : 'span',
31827                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
31828             });
31829             
31830             var bottomText = new Roo.bootstrap.Element({
31831                 tag : 'span',
31832                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
31833             });
31834             
31835             topText.onRender(top.el, null);
31836             bottomText.onRender(bottom.el, null);
31837             
31838             item.topEl = top;
31839             item.bottomEl = bottom;
31840         }
31841         
31842         this.barItems.push(item);
31843         
31844         return item;
31845     },
31846     
31847     getActive : function()
31848     {
31849         var active = false;
31850         
31851         Roo.each(this.barItems, function(v){
31852             
31853             if (!v.isActive()) {
31854                 return;
31855             }
31856             
31857             active = v;
31858             return false;
31859             
31860         });
31861         
31862         return active;
31863     },
31864     
31865     setActiveItem : function(item)
31866     {
31867         var prev = false;
31868         
31869         Roo.each(this.barItems, function(v){
31870             if (v.rid == item.rid) {
31871                 return ;
31872             }
31873             
31874             if (v.isActive()) {
31875                 v.setActive(false);
31876                 prev = v;
31877             }
31878         });
31879
31880         item.setActive(true);
31881         
31882         this.fireEvent('changed', this, item, prev);
31883     },
31884     
31885     getBarItem: function(rid)
31886     {
31887         var ret = false;
31888         
31889         Roo.each(this.barItems, function(e) {
31890             if (e.rid != rid) {
31891                 return;
31892             }
31893             
31894             ret =  e;
31895             return false;
31896         });
31897         
31898         return ret;
31899     },
31900     
31901     indexOfItem : function(item)
31902     {
31903         var index = false;
31904         
31905         Roo.each(this.barItems, function(v, i){
31906             
31907             if (v.rid != item.rid) {
31908                 return;
31909             }
31910             
31911             index = i;
31912             return false
31913         });
31914         
31915         return index;
31916     },
31917     
31918     setActiveNext : function()
31919     {
31920         var i = this.indexOfItem(this.getActive());
31921         
31922         if (i > this.barItems.length) {
31923             return;
31924         }
31925         
31926         this.setActiveItem(this.barItems[i+1]);
31927     },
31928     
31929     setActivePrev : function()
31930     {
31931         var i = this.indexOfItem(this.getActive());
31932         
31933         if (i  < 1) {
31934             return;
31935         }
31936         
31937         this.setActiveItem(this.barItems[i-1]);
31938     },
31939     
31940     format : function()
31941     {
31942         if(!this.barItems.length){
31943             return;
31944         }
31945      
31946         var width = 100 / this.barItems.length;
31947         
31948         Roo.each(this.barItems, function(i){
31949             i.el.setStyle('width', width + '%');
31950             i.topEl.el.setStyle('width', width + '%');
31951             i.bottomEl.el.setStyle('width', width + '%');
31952         }, this);
31953         
31954     }
31955     
31956 });
31957 /*
31958  * - LGPL
31959  *
31960  * Nav Progress Item
31961  * 
31962  */
31963
31964 /**
31965  * @class Roo.bootstrap.NavProgressItem
31966  * @extends Roo.bootstrap.Component
31967  * Bootstrap NavProgressItem class
31968  * @cfg {String} rid the reference id
31969  * @cfg {Boolean} active (true|false) Is item active default false
31970  * @cfg {Boolean} disabled (true|false) Is item active default false
31971  * @cfg {String} html
31972  * @cfg {String} position (top|bottom) text position default bottom
31973  * @cfg {String} icon show icon instead of number
31974  * 
31975  * @constructor
31976  * Create a new NavProgressItem
31977  * @param {Object} config The config object
31978  */
31979 Roo.bootstrap.NavProgressItem = function(config){
31980     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
31981     this.addEvents({
31982         // raw events
31983         /**
31984          * @event click
31985          * The raw click event for the entire grid.
31986          * @param {Roo.bootstrap.NavProgressItem} this
31987          * @param {Roo.EventObject} e
31988          */
31989         "click" : true
31990     });
31991    
31992 };
31993
31994 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
31995     
31996     rid : '',
31997     active : false,
31998     disabled : false,
31999     html : '',
32000     position : 'bottom',
32001     icon : false,
32002     
32003     getAutoCreate : function()
32004     {
32005         var iconCls = 'roo-navigation-bar-item-icon';
32006         
32007         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32008         
32009         var cfg = {
32010             tag: 'li',
32011             cls: 'roo-navigation-bar-item',
32012             cn : [
32013                 {
32014                     tag : 'i',
32015                     cls : iconCls
32016                 }
32017             ]
32018         };
32019         
32020         if(this.active){
32021             cfg.cls += ' active';
32022         }
32023         if(this.disabled){
32024             cfg.cls += ' disabled';
32025         }
32026         
32027         return cfg;
32028     },
32029     
32030     disable : function()
32031     {
32032         this.setDisabled(true);
32033     },
32034     
32035     enable : function()
32036     {
32037         this.setDisabled(false);
32038     },
32039     
32040     initEvents: function() 
32041     {
32042         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32043         
32044         this.iconEl.on('click', this.onClick, this);
32045     },
32046     
32047     onClick : function(e)
32048     {
32049         e.preventDefault();
32050         
32051         if(this.disabled){
32052             return;
32053         }
32054         
32055         if(this.fireEvent('click', this, e) === false){
32056             return;
32057         };
32058         
32059         this.parent().setActiveItem(this);
32060     },
32061     
32062     isActive: function () 
32063     {
32064         return this.active;
32065     },
32066     
32067     setActive : function(state)
32068     {
32069         if(this.active == state){
32070             return;
32071         }
32072         
32073         this.active = state;
32074         
32075         if (state) {
32076             this.el.addClass('active');
32077             return;
32078         }
32079         
32080         this.el.removeClass('active');
32081         
32082         return;
32083     },
32084     
32085     setDisabled : function(state)
32086     {
32087         if(this.disabled == state){
32088             return;
32089         }
32090         
32091         this.disabled = state;
32092         
32093         if (state) {
32094             this.el.addClass('disabled');
32095             return;
32096         }
32097         
32098         this.el.removeClass('disabled');
32099     },
32100     
32101     tooltipEl : function()
32102     {
32103         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32104     }
32105 });
32106  
32107
32108  /*
32109  * - LGPL
32110  *
32111  * FieldLabel
32112  * 
32113  */
32114
32115 /**
32116  * @class Roo.bootstrap.FieldLabel
32117  * @extends Roo.bootstrap.Component
32118  * Bootstrap FieldLabel class
32119  * @cfg {String} html contents of the element
32120  * @cfg {String} tag tag of the element default label
32121  * @cfg {String} cls class of the element
32122  * @cfg {String} target label target 
32123  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32124  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32125  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32126  * @cfg {String} iconTooltip default "This field is required"
32127  * @cfg {String} indicatorpos (left|right) default left
32128  * 
32129  * @constructor
32130  * Create a new FieldLabel
32131  * @param {Object} config The config object
32132  */
32133
32134 Roo.bootstrap.FieldLabel = function(config){
32135     Roo.bootstrap.Element.superclass.constructor.call(this, config);
32136     
32137     this.addEvents({
32138             /**
32139              * @event invalid
32140              * Fires after the field has been marked as invalid.
32141              * @param {Roo.form.FieldLabel} this
32142              * @param {String} msg The validation message
32143              */
32144             invalid : true,
32145             /**
32146              * @event valid
32147              * Fires after the field has been validated with no errors.
32148              * @param {Roo.form.FieldLabel} this
32149              */
32150             valid : true
32151         });
32152 };
32153
32154 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
32155     
32156     tag: 'label',
32157     cls: '',
32158     html: '',
32159     target: '',
32160     allowBlank : true,
32161     invalidClass : 'has-warning',
32162     validClass : 'has-success',
32163     iconTooltip : 'This field is required',
32164     indicatorpos : 'left',
32165     
32166     getAutoCreate : function(){
32167         
32168         var cls = "";
32169         if (!this.allowBlank) {
32170             cls  = "visible";
32171         }
32172         
32173         var cfg = {
32174             tag : this.tag,
32175             cls : 'roo-bootstrap-field-label ' + this.cls,
32176             for : this.target,
32177             cn : [
32178                 {
32179                     tag : 'i',
32180                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32181                     tooltip : this.iconTooltip
32182                 },
32183                 {
32184                     tag : 'span',
32185                     html : this.html
32186                 }
32187             ] 
32188         };
32189         
32190         if(this.indicatorpos == 'right'){
32191             var cfg = {
32192                 tag : this.tag,
32193                 cls : 'roo-bootstrap-field-label ' + this.cls,
32194                 for : this.target,
32195                 cn : [
32196                     {
32197                         tag : 'span',
32198                         html : this.html
32199                     },
32200                     {
32201                         tag : 'i',
32202                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32203                         tooltip : this.iconTooltip
32204                     }
32205                 ] 
32206             };
32207         }
32208         
32209         return cfg;
32210     },
32211     
32212     initEvents: function() 
32213     {
32214         Roo.bootstrap.Element.superclass.initEvents.call(this);
32215         
32216         this.indicator = this.indicatorEl();
32217         
32218         if(this.indicator){
32219             this.indicator.removeClass('visible');
32220             this.indicator.addClass('invisible');
32221         }
32222         
32223         Roo.bootstrap.FieldLabel.register(this);
32224     },
32225     
32226     indicatorEl : function()
32227     {
32228         var indicator = this.el.select('i.roo-required-indicator',true).first();
32229         
32230         if(!indicator){
32231             return false;
32232         }
32233         
32234         return indicator;
32235         
32236     },
32237     
32238     /**
32239      * Mark this field as valid
32240      */
32241     markValid : function()
32242     {
32243         if(this.indicator){
32244             this.indicator.removeClass('visible');
32245             this.indicator.addClass('invisible');
32246         }
32247         if (Roo.bootstrap.version == 3) {
32248             this.el.removeClass(this.invalidClass);
32249             this.el.addClass(this.validClass);
32250         } else {
32251             this.el.removeClass('is-invalid');
32252             this.el.addClass('is-valid');
32253         }
32254         
32255         
32256         this.fireEvent('valid', this);
32257     },
32258     
32259     /**
32260      * Mark this field as invalid
32261      * @param {String} msg The validation message
32262      */
32263     markInvalid : function(msg)
32264     {
32265         if(this.indicator){
32266             this.indicator.removeClass('invisible');
32267             this.indicator.addClass('visible');
32268         }
32269           if (Roo.bootstrap.version == 3) {
32270             this.el.removeClass(this.validClass);
32271             this.el.addClass(this.invalidClass);
32272         } else {
32273             this.el.removeClass('is-valid');
32274             this.el.addClass('is-invalid');
32275         }
32276         
32277         
32278         this.fireEvent('invalid', this, msg);
32279     }
32280     
32281    
32282 });
32283
32284 Roo.apply(Roo.bootstrap.FieldLabel, {
32285     
32286     groups: {},
32287     
32288      /**
32289     * register a FieldLabel Group
32290     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32291     */
32292     register : function(label)
32293     {
32294         if(this.groups.hasOwnProperty(label.target)){
32295             return;
32296         }
32297      
32298         this.groups[label.target] = label;
32299         
32300     },
32301     /**
32302     * fetch a FieldLabel Group based on the target
32303     * @param {string} target
32304     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32305     */
32306     get: function(target) {
32307         if (typeof(this.groups[target]) == 'undefined') {
32308             return false;
32309         }
32310         
32311         return this.groups[target] ;
32312     }
32313 });
32314
32315  
32316
32317  /*
32318  * - LGPL
32319  *
32320  * page DateSplitField.
32321  * 
32322  */
32323
32324
32325 /**
32326  * @class Roo.bootstrap.DateSplitField
32327  * @extends Roo.bootstrap.Component
32328  * Bootstrap DateSplitField class
32329  * @cfg {string} fieldLabel - the label associated
32330  * @cfg {Number} labelWidth set the width of label (0-12)
32331  * @cfg {String} labelAlign (top|left)
32332  * @cfg {Boolean} dayAllowBlank (true|false) default false
32333  * @cfg {Boolean} monthAllowBlank (true|false) default false
32334  * @cfg {Boolean} yearAllowBlank (true|false) default false
32335  * @cfg {string} dayPlaceholder 
32336  * @cfg {string} monthPlaceholder
32337  * @cfg {string} yearPlaceholder
32338  * @cfg {string} dayFormat default 'd'
32339  * @cfg {string} monthFormat default 'm'
32340  * @cfg {string} yearFormat default 'Y'
32341  * @cfg {Number} labellg set the width of label (1-12)
32342  * @cfg {Number} labelmd set the width of label (1-12)
32343  * @cfg {Number} labelsm set the width of label (1-12)
32344  * @cfg {Number} labelxs set the width of label (1-12)
32345
32346  *     
32347  * @constructor
32348  * Create a new DateSplitField
32349  * @param {Object} config The config object
32350  */
32351
32352 Roo.bootstrap.DateSplitField = function(config){
32353     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32354     
32355     this.addEvents({
32356         // raw events
32357          /**
32358          * @event years
32359          * getting the data of years
32360          * @param {Roo.bootstrap.DateSplitField} this
32361          * @param {Object} years
32362          */
32363         "years" : true,
32364         /**
32365          * @event days
32366          * getting the data of days
32367          * @param {Roo.bootstrap.DateSplitField} this
32368          * @param {Object} days
32369          */
32370         "days" : true,
32371         /**
32372          * @event invalid
32373          * Fires after the field has been marked as invalid.
32374          * @param {Roo.form.Field} this
32375          * @param {String} msg The validation message
32376          */
32377         invalid : true,
32378        /**
32379          * @event valid
32380          * Fires after the field has been validated with no errors.
32381          * @param {Roo.form.Field} this
32382          */
32383         valid : true
32384     });
32385 };
32386
32387 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
32388     
32389     fieldLabel : '',
32390     labelAlign : 'top',
32391     labelWidth : 3,
32392     dayAllowBlank : false,
32393     monthAllowBlank : false,
32394     yearAllowBlank : false,
32395     dayPlaceholder : '',
32396     monthPlaceholder : '',
32397     yearPlaceholder : '',
32398     dayFormat : 'd',
32399     monthFormat : 'm',
32400     yearFormat : 'Y',
32401     isFormField : true,
32402     labellg : 0,
32403     labelmd : 0,
32404     labelsm : 0,
32405     labelxs : 0,
32406     
32407     getAutoCreate : function()
32408     {
32409         var cfg = {
32410             tag : 'div',
32411             cls : 'row roo-date-split-field-group',
32412             cn : [
32413                 {
32414                     tag : 'input',
32415                     type : 'hidden',
32416                     cls : 'form-hidden-field roo-date-split-field-group-value',
32417                     name : this.name
32418                 }
32419             ]
32420         };
32421         
32422         var labelCls = 'col-md-12';
32423         var contentCls = 'col-md-4';
32424         
32425         if(this.fieldLabel){
32426             
32427             var label = {
32428                 tag : 'div',
32429                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
32430                 cn : [
32431                     {
32432                         tag : 'label',
32433                         html : this.fieldLabel
32434                     }
32435                 ]
32436             };
32437             
32438             if(this.labelAlign == 'left'){
32439             
32440                 if(this.labelWidth > 12){
32441                     label.style = "width: " + this.labelWidth + 'px';
32442                 }
32443
32444                 if(this.labelWidth < 13 && this.labelmd == 0){
32445                     this.labelmd = this.labelWidth;
32446                 }
32447
32448                 if(this.labellg > 0){
32449                     labelCls = ' col-lg-' + this.labellg;
32450                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
32451                 }
32452
32453                 if(this.labelmd > 0){
32454                     labelCls = ' col-md-' + this.labelmd;
32455                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
32456                 }
32457
32458                 if(this.labelsm > 0){
32459                     labelCls = ' col-sm-' + this.labelsm;
32460                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
32461                 }
32462
32463                 if(this.labelxs > 0){
32464                     labelCls = ' col-xs-' + this.labelxs;
32465                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
32466                 }
32467             }
32468             
32469             label.cls += ' ' + labelCls;
32470             
32471             cfg.cn.push(label);
32472         }
32473         
32474         Roo.each(['day', 'month', 'year'], function(t){
32475             cfg.cn.push({
32476                 tag : 'div',
32477                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
32478             });
32479         }, this);
32480         
32481         return cfg;
32482     },
32483     
32484     inputEl: function ()
32485     {
32486         return this.el.select('.roo-date-split-field-group-value', true).first();
32487     },
32488     
32489     onRender : function(ct, position) 
32490     {
32491         var _this = this;
32492         
32493         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32494         
32495         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
32496         
32497         this.dayField = new Roo.bootstrap.ComboBox({
32498             allowBlank : this.dayAllowBlank,
32499             alwaysQuery : true,
32500             displayField : 'value',
32501             editable : false,
32502             fieldLabel : '',
32503             forceSelection : true,
32504             mode : 'local',
32505             placeholder : this.dayPlaceholder,
32506             selectOnFocus : true,
32507             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32508             triggerAction : 'all',
32509             typeAhead : true,
32510             valueField : 'value',
32511             store : new Roo.data.SimpleStore({
32512                 data : (function() {    
32513                     var days = [];
32514                     _this.fireEvent('days', _this, days);
32515                     return days;
32516                 })(),
32517                 fields : [ 'value' ]
32518             }),
32519             listeners : {
32520                 select : function (_self, record, index)
32521                 {
32522                     _this.setValue(_this.getValue());
32523                 }
32524             }
32525         });
32526
32527         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
32528         
32529         this.monthField = new Roo.bootstrap.MonthField({
32530             after : '<i class=\"fa fa-calendar\"></i>',
32531             allowBlank : this.monthAllowBlank,
32532             placeholder : this.monthPlaceholder,
32533             readOnly : true,
32534             listeners : {
32535                 render : function (_self)
32536                 {
32537                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
32538                         e.preventDefault();
32539                         _self.focus();
32540                     });
32541                 },
32542                 select : function (_self, oldvalue, newvalue)
32543                 {
32544                     _this.setValue(_this.getValue());
32545                 }
32546             }
32547         });
32548         
32549         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
32550         
32551         this.yearField = new Roo.bootstrap.ComboBox({
32552             allowBlank : this.yearAllowBlank,
32553             alwaysQuery : true,
32554             displayField : 'value',
32555             editable : false,
32556             fieldLabel : '',
32557             forceSelection : true,
32558             mode : 'local',
32559             placeholder : this.yearPlaceholder,
32560             selectOnFocus : true,
32561             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32562             triggerAction : 'all',
32563             typeAhead : true,
32564             valueField : 'value',
32565             store : new Roo.data.SimpleStore({
32566                 data : (function() {
32567                     var years = [];
32568                     _this.fireEvent('years', _this, years);
32569                     return years;
32570                 })(),
32571                 fields : [ 'value' ]
32572             }),
32573             listeners : {
32574                 select : function (_self, record, index)
32575                 {
32576                     _this.setValue(_this.getValue());
32577                 }
32578             }
32579         });
32580
32581         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
32582     },
32583     
32584     setValue : function(v, format)
32585     {
32586         this.inputEl.dom.value = v;
32587         
32588         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
32589         
32590         var d = Date.parseDate(v, f);
32591         
32592         if(!d){
32593             this.validate();
32594             return;
32595         }
32596         
32597         this.setDay(d.format(this.dayFormat));
32598         this.setMonth(d.format(this.monthFormat));
32599         this.setYear(d.format(this.yearFormat));
32600         
32601         this.validate();
32602         
32603         return;
32604     },
32605     
32606     setDay : function(v)
32607     {
32608         this.dayField.setValue(v);
32609         this.inputEl.dom.value = this.getValue();
32610         this.validate();
32611         return;
32612     },
32613     
32614     setMonth : function(v)
32615     {
32616         this.monthField.setValue(v, true);
32617         this.inputEl.dom.value = this.getValue();
32618         this.validate();
32619         return;
32620     },
32621     
32622     setYear : function(v)
32623     {
32624         this.yearField.setValue(v);
32625         this.inputEl.dom.value = this.getValue();
32626         this.validate();
32627         return;
32628     },
32629     
32630     getDay : function()
32631     {
32632         return this.dayField.getValue();
32633     },
32634     
32635     getMonth : function()
32636     {
32637         return this.monthField.getValue();
32638     },
32639     
32640     getYear : function()
32641     {
32642         return this.yearField.getValue();
32643     },
32644     
32645     getValue : function()
32646     {
32647         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
32648         
32649         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
32650         
32651         return date;
32652     },
32653     
32654     reset : function()
32655     {
32656         this.setDay('');
32657         this.setMonth('');
32658         this.setYear('');
32659         this.inputEl.dom.value = '';
32660         this.validate();
32661         return;
32662     },
32663     
32664     validate : function()
32665     {
32666         var d = this.dayField.validate();
32667         var m = this.monthField.validate();
32668         var y = this.yearField.validate();
32669         
32670         var valid = true;
32671         
32672         if(
32673                 (!this.dayAllowBlank && !d) ||
32674                 (!this.monthAllowBlank && !m) ||
32675                 (!this.yearAllowBlank && !y)
32676         ){
32677             valid = false;
32678         }
32679         
32680         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
32681             return valid;
32682         }
32683         
32684         if(valid){
32685             this.markValid();
32686             return valid;
32687         }
32688         
32689         this.markInvalid();
32690         
32691         return valid;
32692     },
32693     
32694     markValid : function()
32695     {
32696         
32697         var label = this.el.select('label', true).first();
32698         var icon = this.el.select('i.fa-star', true).first();
32699
32700         if(label && icon){
32701             icon.remove();
32702         }
32703         
32704         this.fireEvent('valid', this);
32705     },
32706     
32707      /**
32708      * Mark this field as invalid
32709      * @param {String} msg The validation message
32710      */
32711     markInvalid : function(msg)
32712     {
32713         
32714         var label = this.el.select('label', true).first();
32715         var icon = this.el.select('i.fa-star', true).first();
32716
32717         if(label && !icon){
32718             this.el.select('.roo-date-split-field-label', true).createChild({
32719                 tag : 'i',
32720                 cls : 'text-danger fa fa-lg fa-star',
32721                 tooltip : 'This field is required',
32722                 style : 'margin-right:5px;'
32723             }, label, true);
32724         }
32725         
32726         this.fireEvent('invalid', this, msg);
32727     },
32728     
32729     clearInvalid : function()
32730     {
32731         var label = this.el.select('label', true).first();
32732         var icon = this.el.select('i.fa-star', true).first();
32733
32734         if(label && icon){
32735             icon.remove();
32736         }
32737         
32738         this.fireEvent('valid', this);
32739     },
32740     
32741     getName: function()
32742     {
32743         return this.name;
32744     }
32745     
32746 });
32747
32748  /**
32749  *
32750  * This is based on 
32751  * http://masonry.desandro.com
32752  *
32753  * The idea is to render all the bricks based on vertical width...
32754  *
32755  * The original code extends 'outlayer' - we might need to use that....
32756  * 
32757  */
32758
32759
32760 /**
32761  * @class Roo.bootstrap.LayoutMasonry
32762  * @extends Roo.bootstrap.Component
32763  * Bootstrap Layout Masonry class
32764  * 
32765  * @constructor
32766  * Create a new Element
32767  * @param {Object} config The config object
32768  */
32769
32770 Roo.bootstrap.LayoutMasonry = function(config){
32771     
32772     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
32773     
32774     this.bricks = [];
32775     
32776     Roo.bootstrap.LayoutMasonry.register(this);
32777     
32778     this.addEvents({
32779         // raw events
32780         /**
32781          * @event layout
32782          * Fire after layout the items
32783          * @param {Roo.bootstrap.LayoutMasonry} this
32784          * @param {Roo.EventObject} e
32785          */
32786         "layout" : true
32787     });
32788     
32789 };
32790
32791 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
32792     
32793     /**
32794      * @cfg {Boolean} isLayoutInstant = no animation?
32795      */   
32796     isLayoutInstant : false, // needed?
32797    
32798     /**
32799      * @cfg {Number} boxWidth  width of the columns
32800      */   
32801     boxWidth : 450,
32802     
32803       /**
32804      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
32805      */   
32806     boxHeight : 0,
32807     
32808     /**
32809      * @cfg {Number} padWidth padding below box..
32810      */   
32811     padWidth : 10, 
32812     
32813     /**
32814      * @cfg {Number} gutter gutter width..
32815      */   
32816     gutter : 10,
32817     
32818      /**
32819      * @cfg {Number} maxCols maximum number of columns
32820      */   
32821     
32822     maxCols: 0,
32823     
32824     /**
32825      * @cfg {Boolean} isAutoInitial defalut true
32826      */   
32827     isAutoInitial : true, 
32828     
32829     containerWidth: 0,
32830     
32831     /**
32832      * @cfg {Boolean} isHorizontal defalut false
32833      */   
32834     isHorizontal : false, 
32835
32836     currentSize : null,
32837     
32838     tag: 'div',
32839     
32840     cls: '',
32841     
32842     bricks: null, //CompositeElement
32843     
32844     cols : 1,
32845     
32846     _isLayoutInited : false,
32847     
32848 //    isAlternative : false, // only use for vertical layout...
32849     
32850     /**
32851      * @cfg {Number} alternativePadWidth padding below box..
32852      */   
32853     alternativePadWidth : 50,
32854     
32855     selectedBrick : [],
32856     
32857     getAutoCreate : function(){
32858         
32859         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
32860         
32861         var cfg = {
32862             tag: this.tag,
32863             cls: 'blog-masonary-wrapper ' + this.cls,
32864             cn : {
32865                 cls : 'mas-boxes masonary'
32866             }
32867         };
32868         
32869         return cfg;
32870     },
32871     
32872     getChildContainer: function( )
32873     {
32874         if (this.boxesEl) {
32875             return this.boxesEl;
32876         }
32877         
32878         this.boxesEl = this.el.select('.mas-boxes').first();
32879         
32880         return this.boxesEl;
32881     },
32882     
32883     
32884     initEvents : function()
32885     {
32886         var _this = this;
32887         
32888         if(this.isAutoInitial){
32889             Roo.log('hook children rendered');
32890             this.on('childrenrendered', function() {
32891                 Roo.log('children rendered');
32892                 _this.initial();
32893             } ,this);
32894         }
32895     },
32896     
32897     initial : function()
32898     {
32899         this.selectedBrick = [];
32900         
32901         this.currentSize = this.el.getBox(true);
32902         
32903         Roo.EventManager.onWindowResize(this.resize, this); 
32904
32905         if(!this.isAutoInitial){
32906             this.layout();
32907             return;
32908         }
32909         
32910         this.layout();
32911         
32912         return;
32913         //this.layout.defer(500,this);
32914         
32915     },
32916     
32917     resize : function()
32918     {
32919         var cs = this.el.getBox(true);
32920         
32921         if (
32922                 this.currentSize.width == cs.width && 
32923                 this.currentSize.x == cs.x && 
32924                 this.currentSize.height == cs.height && 
32925                 this.currentSize.y == cs.y 
32926         ) {
32927             Roo.log("no change in with or X or Y");
32928             return;
32929         }
32930         
32931         this.currentSize = cs;
32932         
32933         this.layout();
32934         
32935     },
32936     
32937     layout : function()
32938     {   
32939         this._resetLayout();
32940         
32941         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32942         
32943         this.layoutItems( isInstant );
32944       
32945         this._isLayoutInited = true;
32946         
32947         this.fireEvent('layout', this);
32948         
32949     },
32950     
32951     _resetLayout : function()
32952     {
32953         if(this.isHorizontal){
32954             this.horizontalMeasureColumns();
32955             return;
32956         }
32957         
32958         this.verticalMeasureColumns();
32959         
32960     },
32961     
32962     verticalMeasureColumns : function()
32963     {
32964         this.getContainerWidth();
32965         
32966 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
32967 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
32968 //            return;
32969 //        }
32970         
32971         var boxWidth = this.boxWidth + this.padWidth;
32972         
32973         if(this.containerWidth < this.boxWidth){
32974             boxWidth = this.containerWidth
32975         }
32976         
32977         var containerWidth = this.containerWidth;
32978         
32979         var cols = Math.floor(containerWidth / boxWidth);
32980         
32981         this.cols = Math.max( cols, 1 );
32982         
32983         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32984         
32985         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
32986         
32987         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
32988         
32989         this.colWidth = boxWidth + avail - this.padWidth;
32990         
32991         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
32992         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
32993     },
32994     
32995     horizontalMeasureColumns : function()
32996     {
32997         this.getContainerWidth();
32998         
32999         var boxWidth = this.boxWidth;
33000         
33001         if(this.containerWidth < boxWidth){
33002             boxWidth = this.containerWidth;
33003         }
33004         
33005         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33006         
33007         this.el.setHeight(boxWidth);
33008         
33009     },
33010     
33011     getContainerWidth : function()
33012     {
33013         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33014     },
33015     
33016     layoutItems : function( isInstant )
33017     {
33018         Roo.log(this.bricks);
33019         
33020         var items = Roo.apply([], this.bricks);
33021         
33022         if(this.isHorizontal){
33023             this._horizontalLayoutItems( items , isInstant );
33024             return;
33025         }
33026         
33027 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33028 //            this._verticalAlternativeLayoutItems( items , isInstant );
33029 //            return;
33030 //        }
33031         
33032         this._verticalLayoutItems( items , isInstant );
33033         
33034     },
33035     
33036     _verticalLayoutItems : function ( items , isInstant)
33037     {
33038         if ( !items || !items.length ) {
33039             return;
33040         }
33041         
33042         var standard = [
33043             ['xs', 'xs', 'xs', 'tall'],
33044             ['xs', 'xs', 'tall'],
33045             ['xs', 'xs', 'sm'],
33046             ['xs', 'xs', 'xs'],
33047             ['xs', 'tall'],
33048             ['xs', 'sm'],
33049             ['xs', 'xs'],
33050             ['xs'],
33051             
33052             ['sm', 'xs', 'xs'],
33053             ['sm', 'xs'],
33054             ['sm'],
33055             
33056             ['tall', 'xs', 'xs', 'xs'],
33057             ['tall', 'xs', 'xs'],
33058             ['tall', 'xs'],
33059             ['tall']
33060             
33061         ];
33062         
33063         var queue = [];
33064         
33065         var boxes = [];
33066         
33067         var box = [];
33068         
33069         Roo.each(items, function(item, k){
33070             
33071             switch (item.size) {
33072                 // these layouts take up a full box,
33073                 case 'md' :
33074                 case 'md-left' :
33075                 case 'md-right' :
33076                 case 'wide' :
33077                     
33078                     if(box.length){
33079                         boxes.push(box);
33080                         box = [];
33081                     }
33082                     
33083                     boxes.push([item]);
33084                     
33085                     break;
33086                     
33087                 case 'xs' :
33088                 case 'sm' :
33089                 case 'tall' :
33090                     
33091                     box.push(item);
33092                     
33093                     break;
33094                 default :
33095                     break;
33096                     
33097             }
33098             
33099         }, this);
33100         
33101         if(box.length){
33102             boxes.push(box);
33103             box = [];
33104         }
33105         
33106         var filterPattern = function(box, length)
33107         {
33108             if(!box.length){
33109                 return;
33110             }
33111             
33112             var match = false;
33113             
33114             var pattern = box.slice(0, length);
33115             
33116             var format = [];
33117             
33118             Roo.each(pattern, function(i){
33119                 format.push(i.size);
33120             }, this);
33121             
33122             Roo.each(standard, function(s){
33123                 
33124                 if(String(s) != String(format)){
33125                     return;
33126                 }
33127                 
33128                 match = true;
33129                 return false;
33130                 
33131             }, this);
33132             
33133             if(!match && length == 1){
33134                 return;
33135             }
33136             
33137             if(!match){
33138                 filterPattern(box, length - 1);
33139                 return;
33140             }
33141                 
33142             queue.push(pattern);
33143
33144             box = box.slice(length, box.length);
33145
33146             filterPattern(box, 4);
33147
33148             return;
33149             
33150         }
33151         
33152         Roo.each(boxes, function(box, k){
33153             
33154             if(!box.length){
33155                 return;
33156             }
33157             
33158             if(box.length == 1){
33159                 queue.push(box);
33160                 return;
33161             }
33162             
33163             filterPattern(box, 4);
33164             
33165         }, this);
33166         
33167         this._processVerticalLayoutQueue( queue, isInstant );
33168         
33169     },
33170     
33171 //    _verticalAlternativeLayoutItems : function( items , isInstant )
33172 //    {
33173 //        if ( !items || !items.length ) {
33174 //            return;
33175 //        }
33176 //
33177 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
33178 //        
33179 //    },
33180     
33181     _horizontalLayoutItems : function ( items , isInstant)
33182     {
33183         if ( !items || !items.length || items.length < 3) {
33184             return;
33185         }
33186         
33187         items.reverse();
33188         
33189         var eItems = items.slice(0, 3);
33190         
33191         items = items.slice(3, items.length);
33192         
33193         var standard = [
33194             ['xs', 'xs', 'xs', 'wide'],
33195             ['xs', 'xs', 'wide'],
33196             ['xs', 'xs', 'sm'],
33197             ['xs', 'xs', 'xs'],
33198             ['xs', 'wide'],
33199             ['xs', 'sm'],
33200             ['xs', 'xs'],
33201             ['xs'],
33202             
33203             ['sm', 'xs', 'xs'],
33204             ['sm', 'xs'],
33205             ['sm'],
33206             
33207             ['wide', 'xs', 'xs', 'xs'],
33208             ['wide', 'xs', 'xs'],
33209             ['wide', 'xs'],
33210             ['wide'],
33211             
33212             ['wide-thin']
33213         ];
33214         
33215         var queue = [];
33216         
33217         var boxes = [];
33218         
33219         var box = [];
33220         
33221         Roo.each(items, function(item, k){
33222             
33223             switch (item.size) {
33224                 case 'md' :
33225                 case 'md-left' :
33226                 case 'md-right' :
33227                 case 'tall' :
33228                     
33229                     if(box.length){
33230                         boxes.push(box);
33231                         box = [];
33232                     }
33233                     
33234                     boxes.push([item]);
33235                     
33236                     break;
33237                     
33238                 case 'xs' :
33239                 case 'sm' :
33240                 case 'wide' :
33241                 case 'wide-thin' :
33242                     
33243                     box.push(item);
33244                     
33245                     break;
33246                 default :
33247                     break;
33248                     
33249             }
33250             
33251         }, this);
33252         
33253         if(box.length){
33254             boxes.push(box);
33255             box = [];
33256         }
33257         
33258         var filterPattern = function(box, length)
33259         {
33260             if(!box.length){
33261                 return;
33262             }
33263             
33264             var match = false;
33265             
33266             var pattern = box.slice(0, length);
33267             
33268             var format = [];
33269             
33270             Roo.each(pattern, function(i){
33271                 format.push(i.size);
33272             }, this);
33273             
33274             Roo.each(standard, function(s){
33275                 
33276                 if(String(s) != String(format)){
33277                     return;
33278                 }
33279                 
33280                 match = true;
33281                 return false;
33282                 
33283             }, this);
33284             
33285             if(!match && length == 1){
33286                 return;
33287             }
33288             
33289             if(!match){
33290                 filterPattern(box, length - 1);
33291                 return;
33292             }
33293                 
33294             queue.push(pattern);
33295
33296             box = box.slice(length, box.length);
33297
33298             filterPattern(box, 4);
33299
33300             return;
33301             
33302         }
33303         
33304         Roo.each(boxes, function(box, k){
33305             
33306             if(!box.length){
33307                 return;
33308             }
33309             
33310             if(box.length == 1){
33311                 queue.push(box);
33312                 return;
33313             }
33314             
33315             filterPattern(box, 4);
33316             
33317         }, this);
33318         
33319         
33320         var prune = [];
33321         
33322         var pos = this.el.getBox(true);
33323         
33324         var minX = pos.x;
33325         
33326         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33327         
33328         var hit_end = false;
33329         
33330         Roo.each(queue, function(box){
33331             
33332             if(hit_end){
33333                 
33334                 Roo.each(box, function(b){
33335                 
33336                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33337                     b.el.hide();
33338
33339                 }, this);
33340
33341                 return;
33342             }
33343             
33344             var mx = 0;
33345             
33346             Roo.each(box, function(b){
33347                 
33348                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33349                 b.el.show();
33350
33351                 mx = Math.max(mx, b.x);
33352                 
33353             }, this);
33354             
33355             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33356             
33357             if(maxX < minX){
33358                 
33359                 Roo.each(box, function(b){
33360                 
33361                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33362                     b.el.hide();
33363                     
33364                 }, this);
33365                 
33366                 hit_end = true;
33367                 
33368                 return;
33369             }
33370             
33371             prune.push(box);
33372             
33373         }, this);
33374         
33375         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
33376     },
33377     
33378     /** Sets position of item in DOM
33379     * @param {Element} item
33380     * @param {Number} x - horizontal position
33381     * @param {Number} y - vertical position
33382     * @param {Boolean} isInstant - disables transitions
33383     */
33384     _processVerticalLayoutQueue : function( queue, isInstant )
33385     {
33386         var pos = this.el.getBox(true);
33387         var x = pos.x;
33388         var y = pos.y;
33389         var maxY = [];
33390         
33391         for (var i = 0; i < this.cols; i++){
33392             maxY[i] = pos.y;
33393         }
33394         
33395         Roo.each(queue, function(box, k){
33396             
33397             var col = k % this.cols;
33398             
33399             Roo.each(box, function(b,kk){
33400                 
33401                 b.el.position('absolute');
33402                 
33403                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33404                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33405                 
33406                 if(b.size == 'md-left' || b.size == 'md-right'){
33407                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33408                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33409                 }
33410                 
33411                 b.el.setWidth(width);
33412                 b.el.setHeight(height);
33413                 // iframe?
33414                 b.el.select('iframe',true).setSize(width,height);
33415                 
33416             }, this);
33417             
33418             for (var i = 0; i < this.cols; i++){
33419                 
33420                 if(maxY[i] < maxY[col]){
33421                     col = i;
33422                     continue;
33423                 }
33424                 
33425                 col = Math.min(col, i);
33426                 
33427             }
33428             
33429             x = pos.x + col * (this.colWidth + this.padWidth);
33430             
33431             y = maxY[col];
33432             
33433             var positions = [];
33434             
33435             switch (box.length){
33436                 case 1 :
33437                     positions = this.getVerticalOneBoxColPositions(x, y, box);
33438                     break;
33439                 case 2 :
33440                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
33441                     break;
33442                 case 3 :
33443                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
33444                     break;
33445                 case 4 :
33446                     positions = this.getVerticalFourBoxColPositions(x, y, box);
33447                     break;
33448                 default :
33449                     break;
33450             }
33451             
33452             Roo.each(box, function(b,kk){
33453                 
33454                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33455                 
33456                 var sz = b.el.getSize();
33457                 
33458                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
33459                 
33460             }, this);
33461             
33462         }, this);
33463         
33464         var mY = 0;
33465         
33466         for (var i = 0; i < this.cols; i++){
33467             mY = Math.max(mY, maxY[i]);
33468         }
33469         
33470         this.el.setHeight(mY - pos.y);
33471         
33472     },
33473     
33474 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
33475 //    {
33476 //        var pos = this.el.getBox(true);
33477 //        var x = pos.x;
33478 //        var y = pos.y;
33479 //        var maxX = pos.right;
33480 //        
33481 //        var maxHeight = 0;
33482 //        
33483 //        Roo.each(items, function(item, k){
33484 //            
33485 //            var c = k % 2;
33486 //            
33487 //            item.el.position('absolute');
33488 //                
33489 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
33490 //
33491 //            item.el.setWidth(width);
33492 //
33493 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
33494 //
33495 //            item.el.setHeight(height);
33496 //            
33497 //            if(c == 0){
33498 //                item.el.setXY([x, y], isInstant ? false : true);
33499 //            } else {
33500 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
33501 //            }
33502 //            
33503 //            y = y + height + this.alternativePadWidth;
33504 //            
33505 //            maxHeight = maxHeight + height + this.alternativePadWidth;
33506 //            
33507 //        }, this);
33508 //        
33509 //        this.el.setHeight(maxHeight);
33510 //        
33511 //    },
33512     
33513     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
33514     {
33515         var pos = this.el.getBox(true);
33516         
33517         var minX = pos.x;
33518         var minY = pos.y;
33519         
33520         var maxX = pos.right;
33521         
33522         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
33523         
33524         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33525         
33526         Roo.each(queue, function(box, k){
33527             
33528             Roo.each(box, function(b, kk){
33529                 
33530                 b.el.position('absolute');
33531                 
33532                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33533                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33534                 
33535                 if(b.size == 'md-left' || b.size == 'md-right'){
33536                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33537                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33538                 }
33539                 
33540                 b.el.setWidth(width);
33541                 b.el.setHeight(height);
33542                 
33543             }, this);
33544             
33545             if(!box.length){
33546                 return;
33547             }
33548             
33549             var positions = [];
33550             
33551             switch (box.length){
33552                 case 1 :
33553                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
33554                     break;
33555                 case 2 :
33556                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
33557                     break;
33558                 case 3 :
33559                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
33560                     break;
33561                 case 4 :
33562                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
33563                     break;
33564                 default :
33565                     break;
33566             }
33567             
33568             Roo.each(box, function(b,kk){
33569                 
33570                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33571                 
33572                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
33573                 
33574             }, this);
33575             
33576         }, this);
33577         
33578     },
33579     
33580     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
33581     {
33582         Roo.each(eItems, function(b,k){
33583             
33584             b.size = (k == 0) ? 'sm' : 'xs';
33585             b.x = (k == 0) ? 2 : 1;
33586             b.y = (k == 0) ? 2 : 1;
33587             
33588             b.el.position('absolute');
33589             
33590             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33591                 
33592             b.el.setWidth(width);
33593             
33594             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33595             
33596             b.el.setHeight(height);
33597             
33598         }, this);
33599
33600         var positions = [];
33601         
33602         positions.push({
33603             x : maxX - this.unitWidth * 2 - this.gutter,
33604             y : minY
33605         });
33606         
33607         positions.push({
33608             x : maxX - this.unitWidth,
33609             y : minY + (this.unitWidth + this.gutter) * 2
33610         });
33611         
33612         positions.push({
33613             x : maxX - this.unitWidth * 3 - this.gutter * 2,
33614             y : minY
33615         });
33616         
33617         Roo.each(eItems, function(b,k){
33618             
33619             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
33620
33621         }, this);
33622         
33623     },
33624     
33625     getVerticalOneBoxColPositions : function(x, y, box)
33626     {
33627         var pos = [];
33628         
33629         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
33630         
33631         if(box[0].size == 'md-left'){
33632             rand = 0;
33633         }
33634         
33635         if(box[0].size == 'md-right'){
33636             rand = 1;
33637         }
33638         
33639         pos.push({
33640             x : x + (this.unitWidth + this.gutter) * rand,
33641             y : y
33642         });
33643         
33644         return pos;
33645     },
33646     
33647     getVerticalTwoBoxColPositions : function(x, y, box)
33648     {
33649         var pos = [];
33650         
33651         if(box[0].size == 'xs'){
33652             
33653             pos.push({
33654                 x : x,
33655                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
33656             });
33657
33658             pos.push({
33659                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
33660                 y : y
33661             });
33662             
33663             return pos;
33664             
33665         }
33666         
33667         pos.push({
33668             x : x,
33669             y : y
33670         });
33671
33672         pos.push({
33673             x : x + (this.unitWidth + this.gutter) * 2,
33674             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
33675         });
33676         
33677         return pos;
33678         
33679     },
33680     
33681     getVerticalThreeBoxColPositions : function(x, y, box)
33682     {
33683         var pos = [];
33684         
33685         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
33686             
33687             pos.push({
33688                 x : x,
33689                 y : y
33690             });
33691
33692             pos.push({
33693                 x : x + (this.unitWidth + this.gutter) * 1,
33694                 y : y
33695             });
33696             
33697             pos.push({
33698                 x : x + (this.unitWidth + this.gutter) * 2,
33699                 y : y
33700             });
33701             
33702             return pos;
33703             
33704         }
33705         
33706         if(box[0].size == 'xs' && box[1].size == 'xs'){
33707             
33708             pos.push({
33709                 x : x,
33710                 y : y
33711             });
33712
33713             pos.push({
33714                 x : x,
33715                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
33716             });
33717             
33718             pos.push({
33719                 x : x + (this.unitWidth + this.gutter) * 1,
33720                 y : y
33721             });
33722             
33723             return pos;
33724             
33725         }
33726         
33727         pos.push({
33728             x : x,
33729             y : y
33730         });
33731
33732         pos.push({
33733             x : x + (this.unitWidth + this.gutter) * 2,
33734             y : y
33735         });
33736
33737         pos.push({
33738             x : x + (this.unitWidth + this.gutter) * 2,
33739             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
33740         });
33741             
33742         return pos;
33743         
33744     },
33745     
33746     getVerticalFourBoxColPositions : function(x, y, box)
33747     {
33748         var pos = [];
33749         
33750         if(box[0].size == 'xs'){
33751             
33752             pos.push({
33753                 x : x,
33754                 y : y
33755             });
33756
33757             pos.push({
33758                 x : x,
33759                 y : y + (this.unitHeight + this.gutter) * 1
33760             });
33761             
33762             pos.push({
33763                 x : x,
33764                 y : y + (this.unitHeight + this.gutter) * 2
33765             });
33766             
33767             pos.push({
33768                 x : x + (this.unitWidth + this.gutter) * 1,
33769                 y : y
33770             });
33771             
33772             return pos;
33773             
33774         }
33775         
33776         pos.push({
33777             x : x,
33778             y : y
33779         });
33780
33781         pos.push({
33782             x : x + (this.unitWidth + this.gutter) * 2,
33783             y : y
33784         });
33785
33786         pos.push({
33787             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
33788             y : y + (this.unitHeight + this.gutter) * 1
33789         });
33790
33791         pos.push({
33792             x : x + (this.unitWidth + this.gutter) * 2,
33793             y : y + (this.unitWidth + this.gutter) * 2
33794         });
33795
33796         return pos;
33797         
33798     },
33799     
33800     getHorizontalOneBoxColPositions : function(maxX, minY, box)
33801     {
33802         var pos = [];
33803         
33804         if(box[0].size == 'md-left'){
33805             pos.push({
33806                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
33807                 y : minY
33808             });
33809             
33810             return pos;
33811         }
33812         
33813         if(box[0].size == 'md-right'){
33814             pos.push({
33815                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
33816                 y : minY + (this.unitWidth + this.gutter) * 1
33817             });
33818             
33819             return pos;
33820         }
33821         
33822         var rand = Math.floor(Math.random() * (4 - box[0].y));
33823         
33824         pos.push({
33825             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33826             y : minY + (this.unitWidth + this.gutter) * rand
33827         });
33828         
33829         return pos;
33830         
33831     },
33832     
33833     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
33834     {
33835         var pos = [];
33836         
33837         if(box[0].size == 'xs'){
33838             
33839             pos.push({
33840                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33841                 y : minY
33842             });
33843
33844             pos.push({
33845                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33846                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
33847             });
33848             
33849             return pos;
33850             
33851         }
33852         
33853         pos.push({
33854             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33855             y : minY
33856         });
33857
33858         pos.push({
33859             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33860             y : minY + (this.unitWidth + this.gutter) * 2
33861         });
33862         
33863         return pos;
33864         
33865     },
33866     
33867     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
33868     {
33869         var pos = [];
33870         
33871         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
33872             
33873             pos.push({
33874                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33875                 y : minY
33876             });
33877
33878             pos.push({
33879                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33880                 y : minY + (this.unitWidth + this.gutter) * 1
33881             });
33882             
33883             pos.push({
33884                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33885                 y : minY + (this.unitWidth + this.gutter) * 2
33886             });
33887             
33888             return pos;
33889             
33890         }
33891         
33892         if(box[0].size == 'xs' && box[1].size == 'xs'){
33893             
33894             pos.push({
33895                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33896                 y : minY
33897             });
33898
33899             pos.push({
33900                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33901                 y : minY
33902             });
33903             
33904             pos.push({
33905                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33906                 y : minY + (this.unitWidth + this.gutter) * 1
33907             });
33908             
33909             return pos;
33910             
33911         }
33912         
33913         pos.push({
33914             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33915             y : minY
33916         });
33917
33918         pos.push({
33919             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33920             y : minY + (this.unitWidth + this.gutter) * 2
33921         });
33922
33923         pos.push({
33924             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33925             y : minY + (this.unitWidth + this.gutter) * 2
33926         });
33927             
33928         return pos;
33929         
33930     },
33931     
33932     getHorizontalFourBoxColPositions : function(maxX, minY, box)
33933     {
33934         var pos = [];
33935         
33936         if(box[0].size == 'xs'){
33937             
33938             pos.push({
33939                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33940                 y : minY
33941             });
33942
33943             pos.push({
33944                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33945                 y : minY
33946             });
33947             
33948             pos.push({
33949                 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),
33950                 y : minY
33951             });
33952             
33953             pos.push({
33954                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
33955                 y : minY + (this.unitWidth + this.gutter) * 1
33956             });
33957             
33958             return pos;
33959             
33960         }
33961         
33962         pos.push({
33963             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33964             y : minY
33965         });
33966         
33967         pos.push({
33968             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33969             y : minY + (this.unitWidth + this.gutter) * 2
33970         });
33971         
33972         pos.push({
33973             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33974             y : minY + (this.unitWidth + this.gutter) * 2
33975         });
33976         
33977         pos.push({
33978             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),
33979             y : minY + (this.unitWidth + this.gutter) * 2
33980         });
33981
33982         return pos;
33983         
33984     },
33985     
33986     /**
33987     * remove a Masonry Brick
33988     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
33989     */
33990     removeBrick : function(brick_id)
33991     {
33992         if (!brick_id) {
33993             return;
33994         }
33995         
33996         for (var i = 0; i<this.bricks.length; i++) {
33997             if (this.bricks[i].id == brick_id) {
33998                 this.bricks.splice(i,1);
33999                 this.el.dom.removeChild(Roo.get(brick_id).dom);
34000                 this.initial();
34001             }
34002         }
34003     },
34004     
34005     /**
34006     * adds a Masonry Brick
34007     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34008     */
34009     addBrick : function(cfg)
34010     {
34011         var cn = new Roo.bootstrap.MasonryBrick(cfg);
34012         //this.register(cn);
34013         cn.parentId = this.id;
34014         cn.render(this.el);
34015         return cn;
34016     },
34017     
34018     /**
34019     * register a Masonry Brick
34020     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34021     */
34022     
34023     register : function(brick)
34024     {
34025         this.bricks.push(brick);
34026         brick.masonryId = this.id;
34027     },
34028     
34029     /**
34030     * clear all the Masonry Brick
34031     */
34032     clearAll : function()
34033     {
34034         this.bricks = [];
34035         //this.getChildContainer().dom.innerHTML = "";
34036         this.el.dom.innerHTML = '';
34037     },
34038     
34039     getSelected : function()
34040     {
34041         if (!this.selectedBrick) {
34042             return false;
34043         }
34044         
34045         return this.selectedBrick;
34046     }
34047 });
34048
34049 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34050     
34051     groups: {},
34052      /**
34053     * register a Masonry Layout
34054     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34055     */
34056     
34057     register : function(layout)
34058     {
34059         this.groups[layout.id] = layout;
34060     },
34061     /**
34062     * fetch a  Masonry Layout based on the masonry layout ID
34063     * @param {string} the masonry layout to add
34064     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34065     */
34066     
34067     get: function(layout_id) {
34068         if (typeof(this.groups[layout_id]) == 'undefined') {
34069             return false;
34070         }
34071         return this.groups[layout_id] ;
34072     }
34073     
34074     
34075     
34076 });
34077
34078  
34079
34080  /**
34081  *
34082  * This is based on 
34083  * http://masonry.desandro.com
34084  *
34085  * The idea is to render all the bricks based on vertical width...
34086  *
34087  * The original code extends 'outlayer' - we might need to use that....
34088  * 
34089  */
34090
34091
34092 /**
34093  * @class Roo.bootstrap.LayoutMasonryAuto
34094  * @extends Roo.bootstrap.Component
34095  * Bootstrap Layout Masonry class
34096  * 
34097  * @constructor
34098  * Create a new Element
34099  * @param {Object} config The config object
34100  */
34101
34102 Roo.bootstrap.LayoutMasonryAuto = function(config){
34103     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34104 };
34105
34106 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
34107     
34108       /**
34109      * @cfg {Boolean} isFitWidth  - resize the width..
34110      */   
34111     isFitWidth : false,  // options..
34112     /**
34113      * @cfg {Boolean} isOriginLeft = left align?
34114      */   
34115     isOriginLeft : true,
34116     /**
34117      * @cfg {Boolean} isOriginTop = top align?
34118      */   
34119     isOriginTop : false,
34120     /**
34121      * @cfg {Boolean} isLayoutInstant = no animation?
34122      */   
34123     isLayoutInstant : false, // needed?
34124     /**
34125      * @cfg {Boolean} isResizingContainer = not sure if this is used..
34126      */   
34127     isResizingContainer : true,
34128     /**
34129      * @cfg {Number} columnWidth  width of the columns 
34130      */   
34131     
34132     columnWidth : 0,
34133     
34134     /**
34135      * @cfg {Number} maxCols maximum number of columns
34136      */   
34137     
34138     maxCols: 0,
34139     /**
34140      * @cfg {Number} padHeight padding below box..
34141      */   
34142     
34143     padHeight : 10, 
34144     
34145     /**
34146      * @cfg {Boolean} isAutoInitial defalut true
34147      */   
34148     
34149     isAutoInitial : true, 
34150     
34151     // private?
34152     gutter : 0,
34153     
34154     containerWidth: 0,
34155     initialColumnWidth : 0,
34156     currentSize : null,
34157     
34158     colYs : null, // array.
34159     maxY : 0,
34160     padWidth: 10,
34161     
34162     
34163     tag: 'div',
34164     cls: '',
34165     bricks: null, //CompositeElement
34166     cols : 0, // array?
34167     // element : null, // wrapped now this.el
34168     _isLayoutInited : null, 
34169     
34170     
34171     getAutoCreate : function(){
34172         
34173         var cfg = {
34174             tag: this.tag,
34175             cls: 'blog-masonary-wrapper ' + this.cls,
34176             cn : {
34177                 cls : 'mas-boxes masonary'
34178             }
34179         };
34180         
34181         return cfg;
34182     },
34183     
34184     getChildContainer: function( )
34185     {
34186         if (this.boxesEl) {
34187             return this.boxesEl;
34188         }
34189         
34190         this.boxesEl = this.el.select('.mas-boxes').first();
34191         
34192         return this.boxesEl;
34193     },
34194     
34195     
34196     initEvents : function()
34197     {
34198         var _this = this;
34199         
34200         if(this.isAutoInitial){
34201             Roo.log('hook children rendered');
34202             this.on('childrenrendered', function() {
34203                 Roo.log('children rendered');
34204                 _this.initial();
34205             } ,this);
34206         }
34207         
34208     },
34209     
34210     initial : function()
34211     {
34212         this.reloadItems();
34213
34214         this.currentSize = this.el.getBox(true);
34215
34216         /// was window resize... - let's see if this works..
34217         Roo.EventManager.onWindowResize(this.resize, this); 
34218
34219         if(!this.isAutoInitial){
34220             this.layout();
34221             return;
34222         }
34223         
34224         this.layout.defer(500,this);
34225     },
34226     
34227     reloadItems: function()
34228     {
34229         this.bricks = this.el.select('.masonry-brick', true);
34230         
34231         this.bricks.each(function(b) {
34232             //Roo.log(b.getSize());
34233             if (!b.attr('originalwidth')) {
34234                 b.attr('originalwidth',  b.getSize().width);
34235             }
34236             
34237         });
34238         
34239         Roo.log(this.bricks.elements.length);
34240     },
34241     
34242     resize : function()
34243     {
34244         Roo.log('resize');
34245         var cs = this.el.getBox(true);
34246         
34247         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34248             Roo.log("no change in with or X");
34249             return;
34250         }
34251         this.currentSize = cs;
34252         this.layout();
34253     },
34254     
34255     layout : function()
34256     {
34257          Roo.log('layout');
34258         this._resetLayout();
34259         //this._manageStamps();
34260       
34261         // don't animate first layout
34262         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34263         this.layoutItems( isInstant );
34264       
34265         // flag for initalized
34266         this._isLayoutInited = true;
34267     },
34268     
34269     layoutItems : function( isInstant )
34270     {
34271         //var items = this._getItemsForLayout( this.items );
34272         // original code supports filtering layout items.. we just ignore it..
34273         
34274         this._layoutItems( this.bricks , isInstant );
34275       
34276         this._postLayout();
34277     },
34278     _layoutItems : function ( items , isInstant)
34279     {
34280        //this.fireEvent( 'layout', this, items );
34281     
34282
34283         if ( !items || !items.elements.length ) {
34284           // no items, emit event with empty array
34285             return;
34286         }
34287
34288         var queue = [];
34289         items.each(function(item) {
34290             Roo.log("layout item");
34291             Roo.log(item);
34292             // get x/y object from method
34293             var position = this._getItemLayoutPosition( item );
34294             // enqueue
34295             position.item = item;
34296             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34297             queue.push( position );
34298         }, this);
34299       
34300         this._processLayoutQueue( queue );
34301     },
34302     /** Sets position of item in DOM
34303     * @param {Element} item
34304     * @param {Number} x - horizontal position
34305     * @param {Number} y - vertical position
34306     * @param {Boolean} isInstant - disables transitions
34307     */
34308     _processLayoutQueue : function( queue )
34309     {
34310         for ( var i=0, len = queue.length; i < len; i++ ) {
34311             var obj = queue[i];
34312             obj.item.position('absolute');
34313             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34314         }
34315     },
34316       
34317     
34318     /**
34319     * Any logic you want to do after each layout,
34320     * i.e. size the container
34321     */
34322     _postLayout : function()
34323     {
34324         this.resizeContainer();
34325     },
34326     
34327     resizeContainer : function()
34328     {
34329         if ( !this.isResizingContainer ) {
34330             return;
34331         }
34332         var size = this._getContainerSize();
34333         if ( size ) {
34334             this.el.setSize(size.width,size.height);
34335             this.boxesEl.setSize(size.width,size.height);
34336         }
34337     },
34338     
34339     
34340     
34341     _resetLayout : function()
34342     {
34343         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34344         this.colWidth = this.el.getWidth();
34345         //this.gutter = this.el.getWidth(); 
34346         
34347         this.measureColumns();
34348
34349         // reset column Y
34350         var i = this.cols;
34351         this.colYs = [];
34352         while (i--) {
34353             this.colYs.push( 0 );
34354         }
34355     
34356         this.maxY = 0;
34357     },
34358
34359     measureColumns : function()
34360     {
34361         this.getContainerWidth();
34362       // if columnWidth is 0, default to outerWidth of first item
34363         if ( !this.columnWidth ) {
34364             var firstItem = this.bricks.first();
34365             Roo.log(firstItem);
34366             this.columnWidth  = this.containerWidth;
34367             if (firstItem && firstItem.attr('originalwidth') ) {
34368                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34369             }
34370             // columnWidth fall back to item of first element
34371             Roo.log("set column width?");
34372                         this.initialColumnWidth = this.columnWidth  ;
34373
34374             // if first elem has no width, default to size of container
34375             
34376         }
34377         
34378         
34379         if (this.initialColumnWidth) {
34380             this.columnWidth = this.initialColumnWidth;
34381         }
34382         
34383         
34384             
34385         // column width is fixed at the top - however if container width get's smaller we should
34386         // reduce it...
34387         
34388         // this bit calcs how man columns..
34389             
34390         var columnWidth = this.columnWidth += this.gutter;
34391       
34392         // calculate columns
34393         var containerWidth = this.containerWidth + this.gutter;
34394         
34395         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
34396         // fix rounding errors, typically with gutters
34397         var excess = columnWidth - containerWidth % columnWidth;
34398         
34399         
34400         // if overshoot is less than a pixel, round up, otherwise floor it
34401         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
34402         cols = Math[ mathMethod ]( cols );
34403         this.cols = Math.max( cols, 1 );
34404         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34405         
34406          // padding positioning..
34407         var totalColWidth = this.cols * this.columnWidth;
34408         var padavail = this.containerWidth - totalColWidth;
34409         // so for 2 columns - we need 3 'pads'
34410         
34411         var padNeeded = (1+this.cols) * this.padWidth;
34412         
34413         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
34414         
34415         this.columnWidth += padExtra
34416         //this.padWidth = Math.floor(padavail /  ( this.cols));
34417         
34418         // adjust colum width so that padding is fixed??
34419         
34420         // we have 3 columns ... total = width * 3
34421         // we have X left over... that should be used by 
34422         
34423         //if (this.expandC) {
34424             
34425         //}
34426         
34427         
34428         
34429     },
34430     
34431     getContainerWidth : function()
34432     {
34433        /* // container is parent if fit width
34434         var container = this.isFitWidth ? this.element.parentNode : this.element;
34435         // check that this.size and size are there
34436         // IE8 triggers resize on body size change, so they might not be
34437         
34438         var size = getSize( container );  //FIXME
34439         this.containerWidth = size && size.innerWidth; //FIXME
34440         */
34441          
34442         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34443         
34444     },
34445     
34446     _getItemLayoutPosition : function( item )  // what is item?
34447     {
34448         // we resize the item to our columnWidth..
34449       
34450         item.setWidth(this.columnWidth);
34451         item.autoBoxAdjust  = false;
34452         
34453         var sz = item.getSize();
34454  
34455         // how many columns does this brick span
34456         var remainder = this.containerWidth % this.columnWidth;
34457         
34458         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
34459         // round if off by 1 pixel, otherwise use ceil
34460         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
34461         colSpan = Math.min( colSpan, this.cols );
34462         
34463         // normally this should be '1' as we dont' currently allow multi width columns..
34464         
34465         var colGroup = this._getColGroup( colSpan );
34466         // get the minimum Y value from the columns
34467         var minimumY = Math.min.apply( Math, colGroup );
34468         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
34469         
34470         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
34471          
34472         // position the brick
34473         var position = {
34474             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
34475             y: this.currentSize.y + minimumY + this.padHeight
34476         };
34477         
34478         Roo.log(position);
34479         // apply setHeight to necessary columns
34480         var setHeight = minimumY + sz.height + this.padHeight;
34481         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
34482         
34483         var setSpan = this.cols + 1 - colGroup.length;
34484         for ( var i = 0; i < setSpan; i++ ) {
34485           this.colYs[ shortColIndex + i ] = setHeight ;
34486         }
34487       
34488         return position;
34489     },
34490     
34491     /**
34492      * @param {Number} colSpan - number of columns the element spans
34493      * @returns {Array} colGroup
34494      */
34495     _getColGroup : function( colSpan )
34496     {
34497         if ( colSpan < 2 ) {
34498           // if brick spans only one column, use all the column Ys
34499           return this.colYs;
34500         }
34501       
34502         var colGroup = [];
34503         // how many different places could this brick fit horizontally
34504         var groupCount = this.cols + 1 - colSpan;
34505         // for each group potential horizontal position
34506         for ( var i = 0; i < groupCount; i++ ) {
34507           // make an array of colY values for that one group
34508           var groupColYs = this.colYs.slice( i, i + colSpan );
34509           // and get the max value of the array
34510           colGroup[i] = Math.max.apply( Math, groupColYs );
34511         }
34512         return colGroup;
34513     },
34514     /*
34515     _manageStamp : function( stamp )
34516     {
34517         var stampSize =  stamp.getSize();
34518         var offset = stamp.getBox();
34519         // get the columns that this stamp affects
34520         var firstX = this.isOriginLeft ? offset.x : offset.right;
34521         var lastX = firstX + stampSize.width;
34522         var firstCol = Math.floor( firstX / this.columnWidth );
34523         firstCol = Math.max( 0, firstCol );
34524         
34525         var lastCol = Math.floor( lastX / this.columnWidth );
34526         // lastCol should not go over if multiple of columnWidth #425
34527         lastCol -= lastX % this.columnWidth ? 0 : 1;
34528         lastCol = Math.min( this.cols - 1, lastCol );
34529         
34530         // set colYs to bottom of the stamp
34531         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
34532             stampSize.height;
34533             
34534         for ( var i = firstCol; i <= lastCol; i++ ) {
34535           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
34536         }
34537     },
34538     */
34539     
34540     _getContainerSize : function()
34541     {
34542         this.maxY = Math.max.apply( Math, this.colYs );
34543         var size = {
34544             height: this.maxY
34545         };
34546       
34547         if ( this.isFitWidth ) {
34548             size.width = this._getContainerFitWidth();
34549         }
34550       
34551         return size;
34552     },
34553     
34554     _getContainerFitWidth : function()
34555     {
34556         var unusedCols = 0;
34557         // count unused columns
34558         var i = this.cols;
34559         while ( --i ) {
34560           if ( this.colYs[i] !== 0 ) {
34561             break;
34562           }
34563           unusedCols++;
34564         }
34565         // fit container to columns that have been used
34566         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
34567     },
34568     
34569     needsResizeLayout : function()
34570     {
34571         var previousWidth = this.containerWidth;
34572         this.getContainerWidth();
34573         return previousWidth !== this.containerWidth;
34574     }
34575  
34576 });
34577
34578  
34579
34580  /*
34581  * - LGPL
34582  *
34583  * element
34584  * 
34585  */
34586
34587 /**
34588  * @class Roo.bootstrap.MasonryBrick
34589  * @extends Roo.bootstrap.Component
34590  * Bootstrap MasonryBrick class
34591  * 
34592  * @constructor
34593  * Create a new MasonryBrick
34594  * @param {Object} config The config object
34595  */
34596
34597 Roo.bootstrap.MasonryBrick = function(config){
34598     
34599     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
34600     
34601     Roo.bootstrap.MasonryBrick.register(this);
34602     
34603     this.addEvents({
34604         // raw events
34605         /**
34606          * @event click
34607          * When a MasonryBrick is clcik
34608          * @param {Roo.bootstrap.MasonryBrick} this
34609          * @param {Roo.EventObject} e
34610          */
34611         "click" : true
34612     });
34613 };
34614
34615 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
34616     
34617     /**
34618      * @cfg {String} title
34619      */   
34620     title : '',
34621     /**
34622      * @cfg {String} html
34623      */   
34624     html : '',
34625     /**
34626      * @cfg {String} bgimage
34627      */   
34628     bgimage : '',
34629     /**
34630      * @cfg {String} videourl
34631      */   
34632     videourl : '',
34633     /**
34634      * @cfg {String} cls
34635      */   
34636     cls : '',
34637     /**
34638      * @cfg {String} href
34639      */   
34640     href : '',
34641     /**
34642      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
34643      */   
34644     size : 'xs',
34645     
34646     /**
34647      * @cfg {String} placetitle (center|bottom)
34648      */   
34649     placetitle : '',
34650     
34651     /**
34652      * @cfg {Boolean} isFitContainer defalut true
34653      */   
34654     isFitContainer : true, 
34655     
34656     /**
34657      * @cfg {Boolean} preventDefault defalut false
34658      */   
34659     preventDefault : false, 
34660     
34661     /**
34662      * @cfg {Boolean} inverse defalut false
34663      */   
34664     maskInverse : false, 
34665     
34666     getAutoCreate : function()
34667     {
34668         if(!this.isFitContainer){
34669             return this.getSplitAutoCreate();
34670         }
34671         
34672         var cls = 'masonry-brick masonry-brick-full';
34673         
34674         if(this.href.length){
34675             cls += ' masonry-brick-link';
34676         }
34677         
34678         if(this.bgimage.length){
34679             cls += ' masonry-brick-image';
34680         }
34681         
34682         if(this.maskInverse){
34683             cls += ' mask-inverse';
34684         }
34685         
34686         if(!this.html.length && !this.maskInverse && !this.videourl.length){
34687             cls += ' enable-mask';
34688         }
34689         
34690         if(this.size){
34691             cls += ' masonry-' + this.size + '-brick';
34692         }
34693         
34694         if(this.placetitle.length){
34695             
34696             switch (this.placetitle) {
34697                 case 'center' :
34698                     cls += ' masonry-center-title';
34699                     break;
34700                 case 'bottom' :
34701                     cls += ' masonry-bottom-title';
34702                     break;
34703                 default:
34704                     break;
34705             }
34706             
34707         } else {
34708             if(!this.html.length && !this.bgimage.length){
34709                 cls += ' masonry-center-title';
34710             }
34711
34712             if(!this.html.length && this.bgimage.length){
34713                 cls += ' masonry-bottom-title';
34714             }
34715         }
34716         
34717         if(this.cls){
34718             cls += ' ' + this.cls;
34719         }
34720         
34721         var cfg = {
34722             tag: (this.href.length) ? 'a' : 'div',
34723             cls: cls,
34724             cn: [
34725                 {
34726                     tag: 'div',
34727                     cls: 'masonry-brick-mask'
34728                 },
34729                 {
34730                     tag: 'div',
34731                     cls: 'masonry-brick-paragraph',
34732                     cn: []
34733                 }
34734             ]
34735         };
34736         
34737         if(this.href.length){
34738             cfg.href = this.href;
34739         }
34740         
34741         var cn = cfg.cn[1].cn;
34742         
34743         if(this.title.length){
34744             cn.push({
34745                 tag: 'h4',
34746                 cls: 'masonry-brick-title',
34747                 html: this.title
34748             });
34749         }
34750         
34751         if(this.html.length){
34752             cn.push({
34753                 tag: 'p',
34754                 cls: 'masonry-brick-text',
34755                 html: this.html
34756             });
34757         }
34758         
34759         if (!this.title.length && !this.html.length) {
34760             cfg.cn[1].cls += ' hide';
34761         }
34762         
34763         if(this.bgimage.length){
34764             cfg.cn.push({
34765                 tag: 'img',
34766                 cls: 'masonry-brick-image-view',
34767                 src: this.bgimage
34768             });
34769         }
34770         
34771         if(this.videourl.length){
34772             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
34773             // youtube support only?
34774             cfg.cn.push({
34775                 tag: 'iframe',
34776                 cls: 'masonry-brick-image-view',
34777                 src: vurl,
34778                 frameborder : 0,
34779                 allowfullscreen : true
34780             });
34781         }
34782         
34783         return cfg;
34784         
34785     },
34786     
34787     getSplitAutoCreate : function()
34788     {
34789         var cls = 'masonry-brick masonry-brick-split';
34790         
34791         if(this.href.length){
34792             cls += ' masonry-brick-link';
34793         }
34794         
34795         if(this.bgimage.length){
34796             cls += ' masonry-brick-image';
34797         }
34798         
34799         if(this.size){
34800             cls += ' masonry-' + this.size + '-brick';
34801         }
34802         
34803         switch (this.placetitle) {
34804             case 'center' :
34805                 cls += ' masonry-center-title';
34806                 break;
34807             case 'bottom' :
34808                 cls += ' masonry-bottom-title';
34809                 break;
34810             default:
34811                 if(!this.bgimage.length){
34812                     cls += ' masonry-center-title';
34813                 }
34814
34815                 if(this.bgimage.length){
34816                     cls += ' masonry-bottom-title';
34817                 }
34818                 break;
34819         }
34820         
34821         if(this.cls){
34822             cls += ' ' + this.cls;
34823         }
34824         
34825         var cfg = {
34826             tag: (this.href.length) ? 'a' : 'div',
34827             cls: cls,
34828             cn: [
34829                 {
34830                     tag: 'div',
34831                     cls: 'masonry-brick-split-head',
34832                     cn: [
34833                         {
34834                             tag: 'div',
34835                             cls: 'masonry-brick-paragraph',
34836                             cn: []
34837                         }
34838                     ]
34839                 },
34840                 {
34841                     tag: 'div',
34842                     cls: 'masonry-brick-split-body',
34843                     cn: []
34844                 }
34845             ]
34846         };
34847         
34848         if(this.href.length){
34849             cfg.href = this.href;
34850         }
34851         
34852         if(this.title.length){
34853             cfg.cn[0].cn[0].cn.push({
34854                 tag: 'h4',
34855                 cls: 'masonry-brick-title',
34856                 html: this.title
34857             });
34858         }
34859         
34860         if(this.html.length){
34861             cfg.cn[1].cn.push({
34862                 tag: 'p',
34863                 cls: 'masonry-brick-text',
34864                 html: this.html
34865             });
34866         }
34867
34868         if(this.bgimage.length){
34869             cfg.cn[0].cn.push({
34870                 tag: 'img',
34871                 cls: 'masonry-brick-image-view',
34872                 src: this.bgimage
34873             });
34874         }
34875         
34876         if(this.videourl.length){
34877             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
34878             // youtube support only?
34879             cfg.cn[0].cn.cn.push({
34880                 tag: 'iframe',
34881                 cls: 'masonry-brick-image-view',
34882                 src: vurl,
34883                 frameborder : 0,
34884                 allowfullscreen : true
34885             });
34886         }
34887         
34888         return cfg;
34889     },
34890     
34891     initEvents: function() 
34892     {
34893         switch (this.size) {
34894             case 'xs' :
34895                 this.x = 1;
34896                 this.y = 1;
34897                 break;
34898             case 'sm' :
34899                 this.x = 2;
34900                 this.y = 2;
34901                 break;
34902             case 'md' :
34903             case 'md-left' :
34904             case 'md-right' :
34905                 this.x = 3;
34906                 this.y = 3;
34907                 break;
34908             case 'tall' :
34909                 this.x = 2;
34910                 this.y = 3;
34911                 break;
34912             case 'wide' :
34913                 this.x = 3;
34914                 this.y = 2;
34915                 break;
34916             case 'wide-thin' :
34917                 this.x = 3;
34918                 this.y = 1;
34919                 break;
34920                         
34921             default :
34922                 break;
34923         }
34924         
34925         if(Roo.isTouch){
34926             this.el.on('touchstart', this.onTouchStart, this);
34927             this.el.on('touchmove', this.onTouchMove, this);
34928             this.el.on('touchend', this.onTouchEnd, this);
34929             this.el.on('contextmenu', this.onContextMenu, this);
34930         } else {
34931             this.el.on('mouseenter'  ,this.enter, this);
34932             this.el.on('mouseleave', this.leave, this);
34933             this.el.on('click', this.onClick, this);
34934         }
34935         
34936         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
34937             this.parent().bricks.push(this);   
34938         }
34939         
34940     },
34941     
34942     onClick: function(e, el)
34943     {
34944         var time = this.endTimer - this.startTimer;
34945         // Roo.log(e.preventDefault());
34946         if(Roo.isTouch){
34947             if(time > 1000){
34948                 e.preventDefault();
34949                 return;
34950             }
34951         }
34952         
34953         if(!this.preventDefault){
34954             return;
34955         }
34956         
34957         e.preventDefault();
34958         
34959         if (this.activeClass != '') {
34960             this.selectBrick();
34961         }
34962         
34963         this.fireEvent('click', this, e);
34964     },
34965     
34966     enter: function(e, el)
34967     {
34968         e.preventDefault();
34969         
34970         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
34971             return;
34972         }
34973         
34974         if(this.bgimage.length && this.html.length){
34975             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
34976         }
34977     },
34978     
34979     leave: function(e, el)
34980     {
34981         e.preventDefault();
34982         
34983         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
34984             return;
34985         }
34986         
34987         if(this.bgimage.length && this.html.length){
34988             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
34989         }
34990     },
34991     
34992     onTouchStart: function(e, el)
34993     {
34994 //        e.preventDefault();
34995         
34996         this.touchmoved = false;
34997         
34998         if(!this.isFitContainer){
34999             return;
35000         }
35001         
35002         if(!this.bgimage.length || !this.html.length){
35003             return;
35004         }
35005         
35006         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35007         
35008         this.timer = new Date().getTime();
35009         
35010     },
35011     
35012     onTouchMove: function(e, el)
35013     {
35014         this.touchmoved = true;
35015     },
35016     
35017     onContextMenu : function(e,el)
35018     {
35019         e.preventDefault();
35020         e.stopPropagation();
35021         return false;
35022     },
35023     
35024     onTouchEnd: function(e, el)
35025     {
35026 //        e.preventDefault();
35027         
35028         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35029         
35030             this.leave(e,el);
35031             
35032             return;
35033         }
35034         
35035         if(!this.bgimage.length || !this.html.length){
35036             
35037             if(this.href.length){
35038                 window.location.href = this.href;
35039             }
35040             
35041             return;
35042         }
35043         
35044         if(!this.isFitContainer){
35045             return;
35046         }
35047         
35048         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35049         
35050         window.location.href = this.href;
35051     },
35052     
35053     //selection on single brick only
35054     selectBrick : function() {
35055         
35056         if (!this.parentId) {
35057             return;
35058         }
35059         
35060         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35061         var index = m.selectedBrick.indexOf(this.id);
35062         
35063         if ( index > -1) {
35064             m.selectedBrick.splice(index,1);
35065             this.el.removeClass(this.activeClass);
35066             return;
35067         }
35068         
35069         for(var i = 0; i < m.selectedBrick.length; i++) {
35070             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35071             b.el.removeClass(b.activeClass);
35072         }
35073         
35074         m.selectedBrick = [];
35075         
35076         m.selectedBrick.push(this.id);
35077         this.el.addClass(this.activeClass);
35078         return;
35079     },
35080     
35081     isSelected : function(){
35082         return this.el.hasClass(this.activeClass);
35083         
35084     }
35085 });
35086
35087 Roo.apply(Roo.bootstrap.MasonryBrick, {
35088     
35089     //groups: {},
35090     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35091      /**
35092     * register a Masonry Brick
35093     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35094     */
35095     
35096     register : function(brick)
35097     {
35098         //this.groups[brick.id] = brick;
35099         this.groups.add(brick.id, brick);
35100     },
35101     /**
35102     * fetch a  masonry brick based on the masonry brick ID
35103     * @param {string} the masonry brick to add
35104     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35105     */
35106     
35107     get: function(brick_id) 
35108     {
35109         // if (typeof(this.groups[brick_id]) == 'undefined') {
35110         //     return false;
35111         // }
35112         // return this.groups[brick_id] ;
35113         
35114         if(this.groups.key(brick_id)) {
35115             return this.groups.key(brick_id);
35116         }
35117         
35118         return false;
35119     }
35120     
35121     
35122     
35123 });
35124
35125  /*
35126  * - LGPL
35127  *
35128  * element
35129  * 
35130  */
35131
35132 /**
35133  * @class Roo.bootstrap.Brick
35134  * @extends Roo.bootstrap.Component
35135  * Bootstrap Brick class
35136  * 
35137  * @constructor
35138  * Create a new Brick
35139  * @param {Object} config The config object
35140  */
35141
35142 Roo.bootstrap.Brick = function(config){
35143     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35144     
35145     this.addEvents({
35146         // raw events
35147         /**
35148          * @event click
35149          * When a Brick is click
35150          * @param {Roo.bootstrap.Brick} this
35151          * @param {Roo.EventObject} e
35152          */
35153         "click" : true
35154     });
35155 };
35156
35157 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
35158     
35159     /**
35160      * @cfg {String} title
35161      */   
35162     title : '',
35163     /**
35164      * @cfg {String} html
35165      */   
35166     html : '',
35167     /**
35168      * @cfg {String} bgimage
35169      */   
35170     bgimage : '',
35171     /**
35172      * @cfg {String} cls
35173      */   
35174     cls : '',
35175     /**
35176      * @cfg {String} href
35177      */   
35178     href : '',
35179     /**
35180      * @cfg {String} video
35181      */   
35182     video : '',
35183     /**
35184      * @cfg {Boolean} square
35185      */   
35186     square : true,
35187     
35188     getAutoCreate : function()
35189     {
35190         var cls = 'roo-brick';
35191         
35192         if(this.href.length){
35193             cls += ' roo-brick-link';
35194         }
35195         
35196         if(this.bgimage.length){
35197             cls += ' roo-brick-image';
35198         }
35199         
35200         if(!this.html.length && !this.bgimage.length){
35201             cls += ' roo-brick-center-title';
35202         }
35203         
35204         if(!this.html.length && this.bgimage.length){
35205             cls += ' roo-brick-bottom-title';
35206         }
35207         
35208         if(this.cls){
35209             cls += ' ' + this.cls;
35210         }
35211         
35212         var cfg = {
35213             tag: (this.href.length) ? 'a' : 'div',
35214             cls: cls,
35215             cn: [
35216                 {
35217                     tag: 'div',
35218                     cls: 'roo-brick-paragraph',
35219                     cn: []
35220                 }
35221             ]
35222         };
35223         
35224         if(this.href.length){
35225             cfg.href = this.href;
35226         }
35227         
35228         var cn = cfg.cn[0].cn;
35229         
35230         if(this.title.length){
35231             cn.push({
35232                 tag: 'h4',
35233                 cls: 'roo-brick-title',
35234                 html: this.title
35235             });
35236         }
35237         
35238         if(this.html.length){
35239             cn.push({
35240                 tag: 'p',
35241                 cls: 'roo-brick-text',
35242                 html: this.html
35243             });
35244         } else {
35245             cn.cls += ' hide';
35246         }
35247         
35248         if(this.bgimage.length){
35249             cfg.cn.push({
35250                 tag: 'img',
35251                 cls: 'roo-brick-image-view',
35252                 src: this.bgimage
35253             });
35254         }
35255         
35256         return cfg;
35257     },
35258     
35259     initEvents: function() 
35260     {
35261         if(this.title.length || this.html.length){
35262             this.el.on('mouseenter'  ,this.enter, this);
35263             this.el.on('mouseleave', this.leave, this);
35264         }
35265         
35266         Roo.EventManager.onWindowResize(this.resize, this); 
35267         
35268         if(this.bgimage.length){
35269             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35270             this.imageEl.on('load', this.onImageLoad, this);
35271             return;
35272         }
35273         
35274         this.resize();
35275     },
35276     
35277     onImageLoad : function()
35278     {
35279         this.resize();
35280     },
35281     
35282     resize : function()
35283     {
35284         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35285         
35286         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35287         
35288         if(this.bgimage.length){
35289             var image = this.el.select('.roo-brick-image-view', true).first();
35290             
35291             image.setWidth(paragraph.getWidth());
35292             
35293             if(this.square){
35294                 image.setHeight(paragraph.getWidth());
35295             }
35296             
35297             this.el.setHeight(image.getHeight());
35298             paragraph.setHeight(image.getHeight());
35299             
35300         }
35301         
35302     },
35303     
35304     enter: function(e, el)
35305     {
35306         e.preventDefault();
35307         
35308         if(this.bgimage.length){
35309             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35310             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35311         }
35312     },
35313     
35314     leave: function(e, el)
35315     {
35316         e.preventDefault();
35317         
35318         if(this.bgimage.length){
35319             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35320             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35321         }
35322     }
35323     
35324 });
35325
35326  
35327
35328  /*
35329  * - LGPL
35330  *
35331  * Number field 
35332  */
35333
35334 /**
35335  * @class Roo.bootstrap.NumberField
35336  * @extends Roo.bootstrap.Input
35337  * Bootstrap NumberField class
35338  * 
35339  * 
35340  * 
35341  * 
35342  * @constructor
35343  * Create a new NumberField
35344  * @param {Object} config The config object
35345  */
35346
35347 Roo.bootstrap.NumberField = function(config){
35348     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35349 };
35350
35351 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35352     
35353     /**
35354      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35355      */
35356     allowDecimals : true,
35357     /**
35358      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35359      */
35360     decimalSeparator : ".",
35361     /**
35362      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35363      */
35364     decimalPrecision : 2,
35365     /**
35366      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35367      */
35368     allowNegative : true,
35369     
35370     /**
35371      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
35372      */
35373     allowZero: true,
35374     /**
35375      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
35376      */
35377     minValue : Number.NEGATIVE_INFINITY,
35378     /**
35379      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
35380      */
35381     maxValue : Number.MAX_VALUE,
35382     /**
35383      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
35384      */
35385     minText : "The minimum value for this field is {0}",
35386     /**
35387      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
35388      */
35389     maxText : "The maximum value for this field is {0}",
35390     /**
35391      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
35392      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
35393      */
35394     nanText : "{0} is not a valid number",
35395     /**
35396      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
35397      */
35398     thousandsDelimiter : false,
35399     /**
35400      * @cfg {String} valueAlign alignment of value
35401      */
35402     valueAlign : "left",
35403
35404     getAutoCreate : function()
35405     {
35406         var hiddenInput = {
35407             tag: 'input',
35408             type: 'hidden',
35409             id: Roo.id(),
35410             cls: 'hidden-number-input'
35411         };
35412         
35413         if (this.name) {
35414             hiddenInput.name = this.name;
35415         }
35416         
35417         this.name = '';
35418         
35419         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
35420         
35421         this.name = hiddenInput.name;
35422         
35423         if(cfg.cn.length > 0) {
35424             cfg.cn.push(hiddenInput);
35425         }
35426         
35427         return cfg;
35428     },
35429
35430     // private
35431     initEvents : function()
35432     {   
35433         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
35434         
35435         var allowed = "0123456789";
35436         
35437         if(this.allowDecimals){
35438             allowed += this.decimalSeparator;
35439         }
35440         
35441         if(this.allowNegative){
35442             allowed += "-";
35443         }
35444         
35445         if(this.thousandsDelimiter) {
35446             allowed += ",";
35447         }
35448         
35449         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
35450         
35451         var keyPress = function(e){
35452             
35453             var k = e.getKey();
35454             
35455             var c = e.getCharCode();
35456             
35457             if(
35458                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
35459                     allowed.indexOf(String.fromCharCode(c)) === -1
35460             ){
35461                 e.stopEvent();
35462                 return;
35463             }
35464             
35465             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
35466                 return;
35467             }
35468             
35469             if(allowed.indexOf(String.fromCharCode(c)) === -1){
35470                 e.stopEvent();
35471             }
35472         };
35473         
35474         this.el.on("keypress", keyPress, this);
35475     },
35476     
35477     validateValue : function(value)
35478     {
35479         
35480         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
35481             return false;
35482         }
35483         
35484         var num = this.parseValue(value);
35485         
35486         if(isNaN(num)){
35487             this.markInvalid(String.format(this.nanText, value));
35488             return false;
35489         }
35490         
35491         if(num < this.minValue){
35492             this.markInvalid(String.format(this.minText, this.minValue));
35493             return false;
35494         }
35495         
35496         if(num > this.maxValue){
35497             this.markInvalid(String.format(this.maxText, this.maxValue));
35498             return false;
35499         }
35500         
35501         return true;
35502     },
35503
35504     getValue : function()
35505     {
35506         var v = this.hiddenEl().getValue();
35507         
35508         return this.fixPrecision(this.parseValue(v));
35509     },
35510
35511     parseValue : function(value)
35512     {
35513         if(this.thousandsDelimiter) {
35514             value += "";
35515             r = new RegExp(",", "g");
35516             value = value.replace(r, "");
35517         }
35518         
35519         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
35520         return isNaN(value) ? '' : value;
35521     },
35522
35523     fixPrecision : function(value)
35524     {
35525         if(this.thousandsDelimiter) {
35526             value += "";
35527             r = new RegExp(",", "g");
35528             value = value.replace(r, "");
35529         }
35530         
35531         var nan = isNaN(value);
35532         
35533         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
35534             return nan ? '' : value;
35535         }
35536         return parseFloat(value).toFixed(this.decimalPrecision);
35537     },
35538
35539     setValue : function(v)
35540     {
35541         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
35542         
35543         this.value = v;
35544         
35545         if(this.rendered){
35546             
35547             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
35548             
35549             this.inputEl().dom.value = (v == '') ? '' :
35550                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
35551             
35552             if(!this.allowZero && v === '0') {
35553                 this.hiddenEl().dom.value = '';
35554                 this.inputEl().dom.value = '';
35555             }
35556             
35557             this.validate();
35558         }
35559     },
35560
35561     decimalPrecisionFcn : function(v)
35562     {
35563         return Math.floor(v);
35564     },
35565
35566     beforeBlur : function()
35567     {
35568         var v = this.parseValue(this.getRawValue());
35569         
35570         if(v || v === 0 || v === ''){
35571             this.setValue(v);
35572         }
35573     },
35574     
35575     hiddenEl : function()
35576     {
35577         return this.el.select('input.hidden-number-input',true).first();
35578     }
35579     
35580 });
35581
35582  
35583
35584 /*
35585 * Licence: LGPL
35586 */
35587
35588 /**
35589  * @class Roo.bootstrap.DocumentSlider
35590  * @extends Roo.bootstrap.Component
35591  * Bootstrap DocumentSlider class
35592  * 
35593  * @constructor
35594  * Create a new DocumentViewer
35595  * @param {Object} config The config object
35596  */
35597
35598 Roo.bootstrap.DocumentSlider = function(config){
35599     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
35600     
35601     this.files = [];
35602     
35603     this.addEvents({
35604         /**
35605          * @event initial
35606          * Fire after initEvent
35607          * @param {Roo.bootstrap.DocumentSlider} this
35608          */
35609         "initial" : true,
35610         /**
35611          * @event update
35612          * Fire after update
35613          * @param {Roo.bootstrap.DocumentSlider} this
35614          */
35615         "update" : true,
35616         /**
35617          * @event click
35618          * Fire after click
35619          * @param {Roo.bootstrap.DocumentSlider} this
35620          */
35621         "click" : true
35622     });
35623 };
35624
35625 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
35626     
35627     files : false,
35628     
35629     indicator : 0,
35630     
35631     getAutoCreate : function()
35632     {
35633         var cfg = {
35634             tag : 'div',
35635             cls : 'roo-document-slider',
35636             cn : [
35637                 {
35638                     tag : 'div',
35639                     cls : 'roo-document-slider-header',
35640                     cn : [
35641                         {
35642                             tag : 'div',
35643                             cls : 'roo-document-slider-header-title'
35644                         }
35645                     ]
35646                 },
35647                 {
35648                     tag : 'div',
35649                     cls : 'roo-document-slider-body',
35650                     cn : [
35651                         {
35652                             tag : 'div',
35653                             cls : 'roo-document-slider-prev',
35654                             cn : [
35655                                 {
35656                                     tag : 'i',
35657                                     cls : 'fa fa-chevron-left'
35658                                 }
35659                             ]
35660                         },
35661                         {
35662                             tag : 'div',
35663                             cls : 'roo-document-slider-thumb',
35664                             cn : [
35665                                 {
35666                                     tag : 'img',
35667                                     cls : 'roo-document-slider-image'
35668                                 }
35669                             ]
35670                         },
35671                         {
35672                             tag : 'div',
35673                             cls : 'roo-document-slider-next',
35674                             cn : [
35675                                 {
35676                                     tag : 'i',
35677                                     cls : 'fa fa-chevron-right'
35678                                 }
35679                             ]
35680                         }
35681                     ]
35682                 }
35683             ]
35684         };
35685         
35686         return cfg;
35687     },
35688     
35689     initEvents : function()
35690     {
35691         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
35692         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
35693         
35694         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
35695         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
35696         
35697         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
35698         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
35699         
35700         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
35701         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
35702         
35703         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
35704         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
35705         
35706         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
35707         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
35708         
35709         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
35710         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
35711         
35712         this.thumbEl.on('click', this.onClick, this);
35713         
35714         this.prevIndicator.on('click', this.prev, this);
35715         
35716         this.nextIndicator.on('click', this.next, this);
35717         
35718     },
35719     
35720     initial : function()
35721     {
35722         if(this.files.length){
35723             this.indicator = 1;
35724             this.update()
35725         }
35726         
35727         this.fireEvent('initial', this);
35728     },
35729     
35730     update : function()
35731     {
35732         this.imageEl.attr('src', this.files[this.indicator - 1]);
35733         
35734         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
35735         
35736         this.prevIndicator.show();
35737         
35738         if(this.indicator == 1){
35739             this.prevIndicator.hide();
35740         }
35741         
35742         this.nextIndicator.show();
35743         
35744         if(this.indicator == this.files.length){
35745             this.nextIndicator.hide();
35746         }
35747         
35748         this.thumbEl.scrollTo('top');
35749         
35750         this.fireEvent('update', this);
35751     },
35752     
35753     onClick : function(e)
35754     {
35755         e.preventDefault();
35756         
35757         this.fireEvent('click', this);
35758     },
35759     
35760     prev : function(e)
35761     {
35762         e.preventDefault();
35763         
35764         this.indicator = Math.max(1, this.indicator - 1);
35765         
35766         this.update();
35767     },
35768     
35769     next : function(e)
35770     {
35771         e.preventDefault();
35772         
35773         this.indicator = Math.min(this.files.length, this.indicator + 1);
35774         
35775         this.update();
35776     }
35777 });
35778 /*
35779  * - LGPL
35780  *
35781  * RadioSet
35782  *
35783  *
35784  */
35785
35786 /**
35787  * @class Roo.bootstrap.RadioSet
35788  * @extends Roo.bootstrap.Input
35789  * Bootstrap RadioSet class
35790  * @cfg {String} indicatorpos (left|right) default left
35791  * @cfg {Boolean} inline (true|false) inline the element (default true)
35792  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
35793  * @constructor
35794  * Create a new RadioSet
35795  * @param {Object} config The config object
35796  */
35797
35798 Roo.bootstrap.RadioSet = function(config){
35799     
35800     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
35801     
35802     this.radioes = [];
35803     
35804     Roo.bootstrap.RadioSet.register(this);
35805     
35806     this.addEvents({
35807         /**
35808         * @event check
35809         * Fires when the element is checked or unchecked.
35810         * @param {Roo.bootstrap.RadioSet} this This radio
35811         * @param {Roo.bootstrap.Radio} item The checked item
35812         */
35813        check : true,
35814        /**
35815         * @event click
35816         * Fires when the element is click.
35817         * @param {Roo.bootstrap.RadioSet} this This radio set
35818         * @param {Roo.bootstrap.Radio} item The checked item
35819         * @param {Roo.EventObject} e The event object
35820         */
35821        click : true
35822     });
35823     
35824 };
35825
35826 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
35827
35828     radioes : false,
35829     
35830     inline : true,
35831     
35832     weight : '',
35833     
35834     indicatorpos : 'left',
35835     
35836     getAutoCreate : function()
35837     {
35838         var label = {
35839             tag : 'label',
35840             cls : 'roo-radio-set-label',
35841             cn : [
35842                 {
35843                     tag : 'span',
35844                     html : this.fieldLabel
35845                 }
35846             ]
35847         };
35848         if (Roo.bootstrap.version == 3) {
35849             
35850             
35851             if(this.indicatorpos == 'left'){
35852                 label.cn.unshift({
35853                     tag : 'i',
35854                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
35855                     tooltip : 'This field is required'
35856                 });
35857             } else {
35858                 label.cn.push({
35859                     tag : 'i',
35860                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
35861                     tooltip : 'This field is required'
35862                 });
35863             }
35864         }
35865         var items = {
35866             tag : 'div',
35867             cls : 'roo-radio-set-items'
35868         };
35869         
35870         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
35871         
35872         if (align === 'left' && this.fieldLabel.length) {
35873             
35874             items = {
35875                 cls : "roo-radio-set-right", 
35876                 cn: [
35877                     items
35878                 ]
35879             };
35880             
35881             if(this.labelWidth > 12){
35882                 label.style = "width: " + this.labelWidth + 'px';
35883             }
35884             
35885             if(this.labelWidth < 13 && this.labelmd == 0){
35886                 this.labelmd = this.labelWidth;
35887             }
35888             
35889             if(this.labellg > 0){
35890                 label.cls += ' col-lg-' + this.labellg;
35891                 items.cls += ' col-lg-' + (12 - this.labellg);
35892             }
35893             
35894             if(this.labelmd > 0){
35895                 label.cls += ' col-md-' + this.labelmd;
35896                 items.cls += ' col-md-' + (12 - this.labelmd);
35897             }
35898             
35899             if(this.labelsm > 0){
35900                 label.cls += ' col-sm-' + this.labelsm;
35901                 items.cls += ' col-sm-' + (12 - this.labelsm);
35902             }
35903             
35904             if(this.labelxs > 0){
35905                 label.cls += ' col-xs-' + this.labelxs;
35906                 items.cls += ' col-xs-' + (12 - this.labelxs);
35907             }
35908         }
35909         
35910         var cfg = {
35911             tag : 'div',
35912             cls : 'roo-radio-set',
35913             cn : [
35914                 {
35915                     tag : 'input',
35916                     cls : 'roo-radio-set-input',
35917                     type : 'hidden',
35918                     name : this.name,
35919                     value : this.value ? this.value :  ''
35920                 },
35921                 label,
35922                 items
35923             ]
35924         };
35925         
35926         if(this.weight.length){
35927             cfg.cls += ' roo-radio-' + this.weight;
35928         }
35929         
35930         if(this.inline) {
35931             cfg.cls += ' roo-radio-set-inline';
35932         }
35933         
35934         var settings=this;
35935         ['xs','sm','md','lg'].map(function(size){
35936             if (settings[size]) {
35937                 cfg.cls += ' col-' + size + '-' + settings[size];
35938             }
35939         });
35940         
35941         return cfg;
35942         
35943     },
35944
35945     initEvents : function()
35946     {
35947         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
35948         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
35949         
35950         if(!this.fieldLabel.length){
35951             this.labelEl.hide();
35952         }
35953         
35954         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
35955         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
35956         
35957         this.indicator = this.indicatorEl();
35958         
35959         if(this.indicator){
35960             this.indicator.addClass('invisible');
35961         }
35962         
35963         this.originalValue = this.getValue();
35964         
35965     },
35966     
35967     inputEl: function ()
35968     {
35969         return this.el.select('.roo-radio-set-input', true).first();
35970     },
35971     
35972     getChildContainer : function()
35973     {
35974         return this.itemsEl;
35975     },
35976     
35977     register : function(item)
35978     {
35979         this.radioes.push(item);
35980         
35981     },
35982     
35983     validate : function()
35984     {   
35985         if(this.getVisibilityEl().hasClass('hidden')){
35986             return true;
35987         }
35988         
35989         var valid = false;
35990         
35991         Roo.each(this.radioes, function(i){
35992             if(!i.checked){
35993                 return;
35994             }
35995             
35996             valid = true;
35997             return false;
35998         });
35999         
36000         if(this.allowBlank) {
36001             return true;
36002         }
36003         
36004         if(this.disabled || valid){
36005             this.markValid();
36006             return true;
36007         }
36008         
36009         this.markInvalid();
36010         return false;
36011         
36012     },
36013     
36014     markValid : function()
36015     {
36016         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36017             this.indicatorEl().removeClass('visible');
36018             this.indicatorEl().addClass('invisible');
36019         }
36020         
36021         
36022         if (Roo.bootstrap.version == 3) {
36023             this.el.removeClass([this.invalidClass, this.validClass]);
36024             this.el.addClass(this.validClass);
36025         } else {
36026             this.el.removeClass(['is-invalid','is-valid']);
36027             this.el.addClass(['is-valid']);
36028         }
36029         this.fireEvent('valid', this);
36030     },
36031     
36032     markInvalid : function(msg)
36033     {
36034         if(this.allowBlank || this.disabled){
36035             return;
36036         }
36037         
36038         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36039             this.indicatorEl().removeClass('invisible');
36040             this.indicatorEl().addClass('visible');
36041         }
36042         if (Roo.bootstrap.version == 3) {
36043             this.el.removeClass([this.invalidClass, this.validClass]);
36044             this.el.addClass(this.invalidClass);
36045         } else {
36046             this.el.removeClass(['is-invalid','is-valid']);
36047             this.el.addClass(['is-invalid']);
36048         }
36049         
36050         this.fireEvent('invalid', this, msg);
36051         
36052     },
36053     
36054     setValue : function(v, suppressEvent)
36055     {   
36056         if(this.value === v){
36057             return;
36058         }
36059         
36060         this.value = v;
36061         
36062         if(this.rendered){
36063             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36064         }
36065         
36066         Roo.each(this.radioes, function(i){
36067             i.checked = false;
36068             i.el.removeClass('checked');
36069         });
36070         
36071         Roo.each(this.radioes, function(i){
36072             
36073             if(i.value === v || i.value.toString() === v.toString()){
36074                 i.checked = true;
36075                 i.el.addClass('checked');
36076                 
36077                 if(suppressEvent !== true){
36078                     this.fireEvent('check', this, i);
36079                 }
36080                 
36081                 return false;
36082             }
36083             
36084         }, this);
36085         
36086         this.validate();
36087     },
36088     
36089     clearInvalid : function(){
36090         
36091         if(!this.el || this.preventMark){
36092             return;
36093         }
36094         
36095         this.el.removeClass([this.invalidClass]);
36096         
36097         this.fireEvent('valid', this);
36098     }
36099     
36100 });
36101
36102 Roo.apply(Roo.bootstrap.RadioSet, {
36103     
36104     groups: {},
36105     
36106     register : function(set)
36107     {
36108         this.groups[set.name] = set;
36109     },
36110     
36111     get: function(name) 
36112     {
36113         if (typeof(this.groups[name]) == 'undefined') {
36114             return false;
36115         }
36116         
36117         return this.groups[name] ;
36118     }
36119     
36120 });
36121 /*
36122  * Based on:
36123  * Ext JS Library 1.1.1
36124  * Copyright(c) 2006-2007, Ext JS, LLC.
36125  *
36126  * Originally Released Under LGPL - original licence link has changed is not relivant.
36127  *
36128  * Fork - LGPL
36129  * <script type="text/javascript">
36130  */
36131
36132
36133 /**
36134  * @class Roo.bootstrap.SplitBar
36135  * @extends Roo.util.Observable
36136  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36137  * <br><br>
36138  * Usage:
36139  * <pre><code>
36140 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36141                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36142 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36143 split.minSize = 100;
36144 split.maxSize = 600;
36145 split.animate = true;
36146 split.on('moved', splitterMoved);
36147 </code></pre>
36148  * @constructor
36149  * Create a new SplitBar
36150  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
36151  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
36152  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36153  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
36154                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36155                         position of the SplitBar).
36156  */
36157 Roo.bootstrap.SplitBar = function(cfg){
36158     
36159     /** @private */
36160     
36161     //{
36162     //  dragElement : elm
36163     //  resizingElement: el,
36164         // optional..
36165     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36166     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
36167         // existingProxy ???
36168     //}
36169     
36170     this.el = Roo.get(cfg.dragElement, true);
36171     this.el.dom.unselectable = "on";
36172     /** @private */
36173     this.resizingEl = Roo.get(cfg.resizingElement, true);
36174
36175     /**
36176      * @private
36177      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36178      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36179      * @type Number
36180      */
36181     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36182     
36183     /**
36184      * The minimum size of the resizing element. (Defaults to 0)
36185      * @type Number
36186      */
36187     this.minSize = 0;
36188     
36189     /**
36190      * The maximum size of the resizing element. (Defaults to 2000)
36191      * @type Number
36192      */
36193     this.maxSize = 2000;
36194     
36195     /**
36196      * Whether to animate the transition to the new size
36197      * @type Boolean
36198      */
36199     this.animate = false;
36200     
36201     /**
36202      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36203      * @type Boolean
36204      */
36205     this.useShim = false;
36206     
36207     /** @private */
36208     this.shim = null;
36209     
36210     if(!cfg.existingProxy){
36211         /** @private */
36212         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36213     }else{
36214         this.proxy = Roo.get(cfg.existingProxy).dom;
36215     }
36216     /** @private */
36217     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36218     
36219     /** @private */
36220     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36221     
36222     /** @private */
36223     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36224     
36225     /** @private */
36226     this.dragSpecs = {};
36227     
36228     /**
36229      * @private The adapter to use to positon and resize elements
36230      */
36231     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36232     this.adapter.init(this);
36233     
36234     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36235         /** @private */
36236         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36237         this.el.addClass("roo-splitbar-h");
36238     }else{
36239         /** @private */
36240         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36241         this.el.addClass("roo-splitbar-v");
36242     }
36243     
36244     this.addEvents({
36245         /**
36246          * @event resize
36247          * Fires when the splitter is moved (alias for {@link #event-moved})
36248          * @param {Roo.bootstrap.SplitBar} this
36249          * @param {Number} newSize the new width or height
36250          */
36251         "resize" : true,
36252         /**
36253          * @event moved
36254          * Fires when the splitter is moved
36255          * @param {Roo.bootstrap.SplitBar} this
36256          * @param {Number} newSize the new width or height
36257          */
36258         "moved" : true,
36259         /**
36260          * @event beforeresize
36261          * Fires before the splitter is dragged
36262          * @param {Roo.bootstrap.SplitBar} this
36263          */
36264         "beforeresize" : true,
36265
36266         "beforeapply" : true
36267     });
36268
36269     Roo.util.Observable.call(this);
36270 };
36271
36272 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36273     onStartProxyDrag : function(x, y){
36274         this.fireEvent("beforeresize", this);
36275         if(!this.overlay){
36276             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
36277             o.unselectable();
36278             o.enableDisplayMode("block");
36279             // all splitbars share the same overlay
36280             Roo.bootstrap.SplitBar.prototype.overlay = o;
36281         }
36282         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36283         this.overlay.show();
36284         Roo.get(this.proxy).setDisplayed("block");
36285         var size = this.adapter.getElementSize(this);
36286         this.activeMinSize = this.getMinimumSize();;
36287         this.activeMaxSize = this.getMaximumSize();;
36288         var c1 = size - this.activeMinSize;
36289         var c2 = Math.max(this.activeMaxSize - size, 0);
36290         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36291             this.dd.resetConstraints();
36292             this.dd.setXConstraint(
36293                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
36294                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36295             );
36296             this.dd.setYConstraint(0, 0);
36297         }else{
36298             this.dd.resetConstraints();
36299             this.dd.setXConstraint(0, 0);
36300             this.dd.setYConstraint(
36301                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
36302                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36303             );
36304          }
36305         this.dragSpecs.startSize = size;
36306         this.dragSpecs.startPoint = [x, y];
36307         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36308     },
36309     
36310     /** 
36311      * @private Called after the drag operation by the DDProxy
36312      */
36313     onEndProxyDrag : function(e){
36314         Roo.get(this.proxy).setDisplayed(false);
36315         var endPoint = Roo.lib.Event.getXY(e);
36316         if(this.overlay){
36317             this.overlay.hide();
36318         }
36319         var newSize;
36320         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36321             newSize = this.dragSpecs.startSize + 
36322                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36323                     endPoint[0] - this.dragSpecs.startPoint[0] :
36324                     this.dragSpecs.startPoint[0] - endPoint[0]
36325                 );
36326         }else{
36327             newSize = this.dragSpecs.startSize + 
36328                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36329                     endPoint[1] - this.dragSpecs.startPoint[1] :
36330                     this.dragSpecs.startPoint[1] - endPoint[1]
36331                 );
36332         }
36333         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36334         if(newSize != this.dragSpecs.startSize){
36335             if(this.fireEvent('beforeapply', this, newSize) !== false){
36336                 this.adapter.setElementSize(this, newSize);
36337                 this.fireEvent("moved", this, newSize);
36338                 this.fireEvent("resize", this, newSize);
36339             }
36340         }
36341     },
36342     
36343     /**
36344      * Get the adapter this SplitBar uses
36345      * @return The adapter object
36346      */
36347     getAdapter : function(){
36348         return this.adapter;
36349     },
36350     
36351     /**
36352      * Set the adapter this SplitBar uses
36353      * @param {Object} adapter A SplitBar adapter object
36354      */
36355     setAdapter : function(adapter){
36356         this.adapter = adapter;
36357         this.adapter.init(this);
36358     },
36359     
36360     /**
36361      * Gets the minimum size for the resizing element
36362      * @return {Number} The minimum size
36363      */
36364     getMinimumSize : function(){
36365         return this.minSize;
36366     },
36367     
36368     /**
36369      * Sets the minimum size for the resizing element
36370      * @param {Number} minSize The minimum size
36371      */
36372     setMinimumSize : function(minSize){
36373         this.minSize = minSize;
36374     },
36375     
36376     /**
36377      * Gets the maximum size for the resizing element
36378      * @return {Number} The maximum size
36379      */
36380     getMaximumSize : function(){
36381         return this.maxSize;
36382     },
36383     
36384     /**
36385      * Sets the maximum size for the resizing element
36386      * @param {Number} maxSize The maximum size
36387      */
36388     setMaximumSize : function(maxSize){
36389         this.maxSize = maxSize;
36390     },
36391     
36392     /**
36393      * Sets the initialize size for the resizing element
36394      * @param {Number} size The initial size
36395      */
36396     setCurrentSize : function(size){
36397         var oldAnimate = this.animate;
36398         this.animate = false;
36399         this.adapter.setElementSize(this, size);
36400         this.animate = oldAnimate;
36401     },
36402     
36403     /**
36404      * Destroy this splitbar. 
36405      * @param {Boolean} removeEl True to remove the element
36406      */
36407     destroy : function(removeEl){
36408         if(this.shim){
36409             this.shim.remove();
36410         }
36411         this.dd.unreg();
36412         this.proxy.parentNode.removeChild(this.proxy);
36413         if(removeEl){
36414             this.el.remove();
36415         }
36416     }
36417 });
36418
36419 /**
36420  * @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.
36421  */
36422 Roo.bootstrap.SplitBar.createProxy = function(dir){
36423     var proxy = new Roo.Element(document.createElement("div"));
36424     proxy.unselectable();
36425     var cls = 'roo-splitbar-proxy';
36426     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
36427     document.body.appendChild(proxy.dom);
36428     return proxy.dom;
36429 };
36430
36431 /** 
36432  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
36433  * Default Adapter. It assumes the splitter and resizing element are not positioned
36434  * elements and only gets/sets the width of the element. Generally used for table based layouts.
36435  */
36436 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
36437 };
36438
36439 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
36440     // do nothing for now
36441     init : function(s){
36442     
36443     },
36444     /**
36445      * Called before drag operations to get the current size of the resizing element. 
36446      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36447      */
36448      getElementSize : function(s){
36449         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36450             return s.resizingEl.getWidth();
36451         }else{
36452             return s.resizingEl.getHeight();
36453         }
36454     },
36455     
36456     /**
36457      * Called after drag operations to set the size of the resizing element.
36458      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36459      * @param {Number} newSize The new size to set
36460      * @param {Function} onComplete A function to be invoked when resizing is complete
36461      */
36462     setElementSize : function(s, newSize, onComplete){
36463         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36464             if(!s.animate){
36465                 s.resizingEl.setWidth(newSize);
36466                 if(onComplete){
36467                     onComplete(s, newSize);
36468                 }
36469             }else{
36470                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
36471             }
36472         }else{
36473             
36474             if(!s.animate){
36475                 s.resizingEl.setHeight(newSize);
36476                 if(onComplete){
36477                     onComplete(s, newSize);
36478                 }
36479             }else{
36480                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
36481             }
36482         }
36483     }
36484 };
36485
36486 /** 
36487  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
36488  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
36489  * Adapter that  moves the splitter element to align with the resized sizing element. 
36490  * Used with an absolute positioned SplitBar.
36491  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
36492  * document.body, make sure you assign an id to the body element.
36493  */
36494 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
36495     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36496     this.container = Roo.get(container);
36497 };
36498
36499 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
36500     init : function(s){
36501         this.basic.init(s);
36502     },
36503     
36504     getElementSize : function(s){
36505         return this.basic.getElementSize(s);
36506     },
36507     
36508     setElementSize : function(s, newSize, onComplete){
36509         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
36510     },
36511     
36512     moveSplitter : function(s){
36513         var yes = Roo.bootstrap.SplitBar;
36514         switch(s.placement){
36515             case yes.LEFT:
36516                 s.el.setX(s.resizingEl.getRight());
36517                 break;
36518             case yes.RIGHT:
36519                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
36520                 break;
36521             case yes.TOP:
36522                 s.el.setY(s.resizingEl.getBottom());
36523                 break;
36524             case yes.BOTTOM:
36525                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
36526                 break;
36527         }
36528     }
36529 };
36530
36531 /**
36532  * Orientation constant - Create a vertical SplitBar
36533  * @static
36534  * @type Number
36535  */
36536 Roo.bootstrap.SplitBar.VERTICAL = 1;
36537
36538 /**
36539  * Orientation constant - Create a horizontal SplitBar
36540  * @static
36541  * @type Number
36542  */
36543 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
36544
36545 /**
36546  * Placement constant - The resizing element is to the left of the splitter element
36547  * @static
36548  * @type Number
36549  */
36550 Roo.bootstrap.SplitBar.LEFT = 1;
36551
36552 /**
36553  * Placement constant - The resizing element is to the right of the splitter element
36554  * @static
36555  * @type Number
36556  */
36557 Roo.bootstrap.SplitBar.RIGHT = 2;
36558
36559 /**
36560  * Placement constant - The resizing element is positioned above the splitter element
36561  * @static
36562  * @type Number
36563  */
36564 Roo.bootstrap.SplitBar.TOP = 3;
36565
36566 /**
36567  * Placement constant - The resizing element is positioned under splitter element
36568  * @static
36569  * @type Number
36570  */
36571 Roo.bootstrap.SplitBar.BOTTOM = 4;
36572 Roo.namespace("Roo.bootstrap.layout");/*
36573  * Based on:
36574  * Ext JS Library 1.1.1
36575  * Copyright(c) 2006-2007, Ext JS, LLC.
36576  *
36577  * Originally Released Under LGPL - original licence link has changed is not relivant.
36578  *
36579  * Fork - LGPL
36580  * <script type="text/javascript">
36581  */
36582
36583 /**
36584  * @class Roo.bootstrap.layout.Manager
36585  * @extends Roo.bootstrap.Component
36586  * Base class for layout managers.
36587  */
36588 Roo.bootstrap.layout.Manager = function(config)
36589 {
36590     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
36591
36592
36593
36594
36595
36596     /** false to disable window resize monitoring @type Boolean */
36597     this.monitorWindowResize = true;
36598     this.regions = {};
36599     this.addEvents({
36600         /**
36601          * @event layout
36602          * Fires when a layout is performed.
36603          * @param {Roo.LayoutManager} this
36604          */
36605         "layout" : true,
36606         /**
36607          * @event regionresized
36608          * Fires when the user resizes a region.
36609          * @param {Roo.LayoutRegion} region The resized region
36610          * @param {Number} newSize The new size (width for east/west, height for north/south)
36611          */
36612         "regionresized" : true,
36613         /**
36614          * @event regioncollapsed
36615          * Fires when a region is collapsed.
36616          * @param {Roo.LayoutRegion} region The collapsed region
36617          */
36618         "regioncollapsed" : true,
36619         /**
36620          * @event regionexpanded
36621          * Fires when a region is expanded.
36622          * @param {Roo.LayoutRegion} region The expanded region
36623          */
36624         "regionexpanded" : true
36625     });
36626     this.updating = false;
36627
36628     if (config.el) {
36629         this.el = Roo.get(config.el);
36630         this.initEvents();
36631     }
36632
36633 };
36634
36635 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
36636
36637
36638     regions : null,
36639
36640     monitorWindowResize : true,
36641
36642
36643     updating : false,
36644
36645
36646     onRender : function(ct, position)
36647     {
36648         if(!this.el){
36649             this.el = Roo.get(ct);
36650             this.initEvents();
36651         }
36652         //this.fireEvent('render',this);
36653     },
36654
36655
36656     initEvents: function()
36657     {
36658
36659
36660         // ie scrollbar fix
36661         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
36662             document.body.scroll = "no";
36663         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
36664             this.el.position('relative');
36665         }
36666         this.id = this.el.id;
36667         this.el.addClass("roo-layout-container");
36668         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
36669         if(this.el.dom != document.body ) {
36670             this.el.on('resize', this.layout,this);
36671             this.el.on('show', this.layout,this);
36672         }
36673
36674     },
36675
36676     /**
36677      * Returns true if this layout is currently being updated
36678      * @return {Boolean}
36679      */
36680     isUpdating : function(){
36681         return this.updating;
36682     },
36683
36684     /**
36685      * Suspend the LayoutManager from doing auto-layouts while
36686      * making multiple add or remove calls
36687      */
36688     beginUpdate : function(){
36689         this.updating = true;
36690     },
36691
36692     /**
36693      * Restore auto-layouts and optionally disable the manager from performing a layout
36694      * @param {Boolean} noLayout true to disable a layout update
36695      */
36696     endUpdate : function(noLayout){
36697         this.updating = false;
36698         if(!noLayout){
36699             this.layout();
36700         }
36701     },
36702
36703     layout: function(){
36704         // abstract...
36705     },
36706
36707     onRegionResized : function(region, newSize){
36708         this.fireEvent("regionresized", region, newSize);
36709         this.layout();
36710     },
36711
36712     onRegionCollapsed : function(region){
36713         this.fireEvent("regioncollapsed", region);
36714     },
36715
36716     onRegionExpanded : function(region){
36717         this.fireEvent("regionexpanded", region);
36718     },
36719
36720     /**
36721      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
36722      * performs box-model adjustments.
36723      * @return {Object} The size as an object {width: (the width), height: (the height)}
36724      */
36725     getViewSize : function()
36726     {
36727         var size;
36728         if(this.el.dom != document.body){
36729             size = this.el.getSize();
36730         }else{
36731             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
36732         }
36733         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
36734         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
36735         return size;
36736     },
36737
36738     /**
36739      * Returns the Element this layout is bound to.
36740      * @return {Roo.Element}
36741      */
36742     getEl : function(){
36743         return this.el;
36744     },
36745
36746     /**
36747      * Returns the specified region.
36748      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
36749      * @return {Roo.LayoutRegion}
36750      */
36751     getRegion : function(target){
36752         return this.regions[target.toLowerCase()];
36753     },
36754
36755     onWindowResize : function(){
36756         if(this.monitorWindowResize){
36757             this.layout();
36758         }
36759     }
36760 });
36761 /*
36762  * Based on:
36763  * Ext JS Library 1.1.1
36764  * Copyright(c) 2006-2007, Ext JS, LLC.
36765  *
36766  * Originally Released Under LGPL - original licence link has changed is not relivant.
36767  *
36768  * Fork - LGPL
36769  * <script type="text/javascript">
36770  */
36771 /**
36772  * @class Roo.bootstrap.layout.Border
36773  * @extends Roo.bootstrap.layout.Manager
36774  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
36775  * please see: examples/bootstrap/nested.html<br><br>
36776  
36777 <b>The container the layout is rendered into can be either the body element or any other element.
36778 If it is not the body element, the container needs to either be an absolute positioned element,
36779 or you will need to add "position:relative" to the css of the container.  You will also need to specify
36780 the container size if it is not the body element.</b>
36781
36782 * @constructor
36783 * Create a new Border
36784 * @param {Object} config Configuration options
36785  */
36786 Roo.bootstrap.layout.Border = function(config){
36787     config = config || {};
36788     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
36789     
36790     
36791     
36792     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
36793         if(config[region]){
36794             config[region].region = region;
36795             this.addRegion(config[region]);
36796         }
36797     },this);
36798     
36799 };
36800
36801 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
36802
36803 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
36804     
36805     parent : false, // this might point to a 'nest' or a ???
36806     
36807     /**
36808      * Creates and adds a new region if it doesn't already exist.
36809      * @param {String} target The target region key (north, south, east, west or center).
36810      * @param {Object} config The regions config object
36811      * @return {BorderLayoutRegion} The new region
36812      */
36813     addRegion : function(config)
36814     {
36815         if(!this.regions[config.region]){
36816             var r = this.factory(config);
36817             this.bindRegion(r);
36818         }
36819         return this.regions[config.region];
36820     },
36821
36822     // private (kinda)
36823     bindRegion : function(r){
36824         this.regions[r.config.region] = r;
36825         
36826         r.on("visibilitychange",    this.layout, this);
36827         r.on("paneladded",          this.layout, this);
36828         r.on("panelremoved",        this.layout, this);
36829         r.on("invalidated",         this.layout, this);
36830         r.on("resized",             this.onRegionResized, this);
36831         r.on("collapsed",           this.onRegionCollapsed, this);
36832         r.on("expanded",            this.onRegionExpanded, this);
36833     },
36834
36835     /**
36836      * Performs a layout update.
36837      */
36838     layout : function()
36839     {
36840         if(this.updating) {
36841             return;
36842         }
36843         
36844         // render all the rebions if they have not been done alreayd?
36845         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
36846             if(this.regions[region] && !this.regions[region].bodyEl){
36847                 this.regions[region].onRender(this.el)
36848             }
36849         },this);
36850         
36851         var size = this.getViewSize();
36852         var w = size.width;
36853         var h = size.height;
36854         var centerW = w;
36855         var centerH = h;
36856         var centerY = 0;
36857         var centerX = 0;
36858         //var x = 0, y = 0;
36859
36860         var rs = this.regions;
36861         var north = rs["north"];
36862         var south = rs["south"]; 
36863         var west = rs["west"];
36864         var east = rs["east"];
36865         var center = rs["center"];
36866         //if(this.hideOnLayout){ // not supported anymore
36867             //c.el.setStyle("display", "none");
36868         //}
36869         if(north && north.isVisible()){
36870             var b = north.getBox();
36871             var m = north.getMargins();
36872             b.width = w - (m.left+m.right);
36873             b.x = m.left;
36874             b.y = m.top;
36875             centerY = b.height + b.y + m.bottom;
36876             centerH -= centerY;
36877             north.updateBox(this.safeBox(b));
36878         }
36879         if(south && south.isVisible()){
36880             var b = south.getBox();
36881             var m = south.getMargins();
36882             b.width = w - (m.left+m.right);
36883             b.x = m.left;
36884             var totalHeight = (b.height + m.top + m.bottom);
36885             b.y = h - totalHeight + m.top;
36886             centerH -= totalHeight;
36887             south.updateBox(this.safeBox(b));
36888         }
36889         if(west && west.isVisible()){
36890             var b = west.getBox();
36891             var m = west.getMargins();
36892             b.height = centerH - (m.top+m.bottom);
36893             b.x = m.left;
36894             b.y = centerY + m.top;
36895             var totalWidth = (b.width + m.left + m.right);
36896             centerX += totalWidth;
36897             centerW -= totalWidth;
36898             west.updateBox(this.safeBox(b));
36899         }
36900         if(east && east.isVisible()){
36901             var b = east.getBox();
36902             var m = east.getMargins();
36903             b.height = centerH - (m.top+m.bottom);
36904             var totalWidth = (b.width + m.left + m.right);
36905             b.x = w - totalWidth + m.left;
36906             b.y = centerY + m.top;
36907             centerW -= totalWidth;
36908             east.updateBox(this.safeBox(b));
36909         }
36910         if(center){
36911             var m = center.getMargins();
36912             var centerBox = {
36913                 x: centerX + m.left,
36914                 y: centerY + m.top,
36915                 width: centerW - (m.left+m.right),
36916                 height: centerH - (m.top+m.bottom)
36917             };
36918             //if(this.hideOnLayout){
36919                 //center.el.setStyle("display", "block");
36920             //}
36921             center.updateBox(this.safeBox(centerBox));
36922         }
36923         this.el.repaint();
36924         this.fireEvent("layout", this);
36925     },
36926
36927     // private
36928     safeBox : function(box){
36929         box.width = Math.max(0, box.width);
36930         box.height = Math.max(0, box.height);
36931         return box;
36932     },
36933
36934     /**
36935      * Adds a ContentPanel (or subclass) to this layout.
36936      * @param {String} target The target region key (north, south, east, west or center).
36937      * @param {Roo.ContentPanel} panel The panel to add
36938      * @return {Roo.ContentPanel} The added panel
36939      */
36940     add : function(target, panel){
36941          
36942         target = target.toLowerCase();
36943         return this.regions[target].add(panel);
36944     },
36945
36946     /**
36947      * Remove a ContentPanel (or subclass) to this layout.
36948      * @param {String} target The target region key (north, south, east, west or center).
36949      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
36950      * @return {Roo.ContentPanel} The removed panel
36951      */
36952     remove : function(target, panel){
36953         target = target.toLowerCase();
36954         return this.regions[target].remove(panel);
36955     },
36956
36957     /**
36958      * Searches all regions for a panel with the specified id
36959      * @param {String} panelId
36960      * @return {Roo.ContentPanel} The panel or null if it wasn't found
36961      */
36962     findPanel : function(panelId){
36963         var rs = this.regions;
36964         for(var target in rs){
36965             if(typeof rs[target] != "function"){
36966                 var p = rs[target].getPanel(panelId);
36967                 if(p){
36968                     return p;
36969                 }
36970             }
36971         }
36972         return null;
36973     },
36974
36975     /**
36976      * Searches all regions for a panel with the specified id and activates (shows) it.
36977      * @param {String/ContentPanel} panelId The panels id or the panel itself
36978      * @return {Roo.ContentPanel} The shown panel or null
36979      */
36980     showPanel : function(panelId) {
36981       var rs = this.regions;
36982       for(var target in rs){
36983          var r = rs[target];
36984          if(typeof r != "function"){
36985             if(r.hasPanel(panelId)){
36986                return r.showPanel(panelId);
36987             }
36988          }
36989       }
36990       return null;
36991    },
36992
36993    /**
36994      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
36995      * @param {Roo.state.Provider} provider (optional) An alternate state provider
36996      */
36997    /*
36998     restoreState : function(provider){
36999         if(!provider){
37000             provider = Roo.state.Manager;
37001         }
37002         var sm = new Roo.LayoutStateManager();
37003         sm.init(this, provider);
37004     },
37005 */
37006  
37007  
37008     /**
37009      * Adds a xtype elements to the layout.
37010      * <pre><code>
37011
37012 layout.addxtype({
37013        xtype : 'ContentPanel',
37014        region: 'west',
37015        items: [ .... ]
37016    }
37017 );
37018
37019 layout.addxtype({
37020         xtype : 'NestedLayoutPanel',
37021         region: 'west',
37022         layout: {
37023            center: { },
37024            west: { }   
37025         },
37026         items : [ ... list of content panels or nested layout panels.. ]
37027    }
37028 );
37029 </code></pre>
37030      * @param {Object} cfg Xtype definition of item to add.
37031      */
37032     addxtype : function(cfg)
37033     {
37034         // basically accepts a pannel...
37035         // can accept a layout region..!?!?
37036         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37037         
37038         
37039         // theory?  children can only be panels??
37040         
37041         //if (!cfg.xtype.match(/Panel$/)) {
37042         //    return false;
37043         //}
37044         var ret = false;
37045         
37046         if (typeof(cfg.region) == 'undefined') {
37047             Roo.log("Failed to add Panel, region was not set");
37048             Roo.log(cfg);
37049             return false;
37050         }
37051         var region = cfg.region;
37052         delete cfg.region;
37053         
37054           
37055         var xitems = [];
37056         if (cfg.items) {
37057             xitems = cfg.items;
37058             delete cfg.items;
37059         }
37060         var nb = false;
37061         
37062         if ( region == 'center') {
37063             Roo.log("Center: " + cfg.title);
37064         }
37065         
37066         
37067         switch(cfg.xtype) 
37068         {
37069             case 'Content':  // ContentPanel (el, cfg)
37070             case 'Scroll':  // ContentPanel (el, cfg)
37071             case 'View': 
37072                 cfg.autoCreate = cfg.autoCreate || true;
37073                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37074                 //} else {
37075                 //    var el = this.el.createChild();
37076                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37077                 //}
37078                 
37079                 this.add(region, ret);
37080                 break;
37081             
37082             /*
37083             case 'TreePanel': // our new panel!
37084                 cfg.el = this.el.createChild();
37085                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37086                 this.add(region, ret);
37087                 break;
37088             */
37089             
37090             case 'Nest': 
37091                 // create a new Layout (which is  a Border Layout...
37092                 
37093                 var clayout = cfg.layout;
37094                 clayout.el  = this.el.createChild();
37095                 clayout.items   = clayout.items  || [];
37096                 
37097                 delete cfg.layout;
37098                 
37099                 // replace this exitems with the clayout ones..
37100                 xitems = clayout.items;
37101                  
37102                 // force background off if it's in center...
37103                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37104                     cfg.background = false;
37105                 }
37106                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
37107                 
37108                 
37109                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37110                 //console.log('adding nested layout panel '  + cfg.toSource());
37111                 this.add(region, ret);
37112                 nb = {}; /// find first...
37113                 break;
37114             
37115             case 'Grid':
37116                 
37117                 // needs grid and region
37118                 
37119                 //var el = this.getRegion(region).el.createChild();
37120                 /*
37121                  *var el = this.el.createChild();
37122                 // create the grid first...
37123                 cfg.grid.container = el;
37124                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37125                 */
37126                 
37127                 if (region == 'center' && this.active ) {
37128                     cfg.background = false;
37129                 }
37130                 
37131                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37132                 
37133                 this.add(region, ret);
37134                 /*
37135                 if (cfg.background) {
37136                     // render grid on panel activation (if panel background)
37137                     ret.on('activate', function(gp) {
37138                         if (!gp.grid.rendered) {
37139                     //        gp.grid.render(el);
37140                         }
37141                     });
37142                 } else {
37143                   //  cfg.grid.render(el);
37144                 }
37145                 */
37146                 break;
37147            
37148            
37149             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37150                 // it was the old xcomponent building that caused this before.
37151                 // espeically if border is the top element in the tree.
37152                 ret = this;
37153                 break; 
37154                 
37155                     
37156                 
37157                 
37158                 
37159             default:
37160                 /*
37161                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37162                     
37163                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37164                     this.add(region, ret);
37165                 } else {
37166                 */
37167                     Roo.log(cfg);
37168                     throw "Can not add '" + cfg.xtype + "' to Border";
37169                     return null;
37170              
37171                                 
37172              
37173         }
37174         this.beginUpdate();
37175         // add children..
37176         var region = '';
37177         var abn = {};
37178         Roo.each(xitems, function(i)  {
37179             region = nb && i.region ? i.region : false;
37180             
37181             var add = ret.addxtype(i);
37182            
37183             if (region) {
37184                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37185                 if (!i.background) {
37186                     abn[region] = nb[region] ;
37187                 }
37188             }
37189             
37190         });
37191         this.endUpdate();
37192
37193         // make the last non-background panel active..
37194         //if (nb) { Roo.log(abn); }
37195         if (nb) {
37196             
37197             for(var r in abn) {
37198                 region = this.getRegion(r);
37199                 if (region) {
37200                     // tried using nb[r], but it does not work..
37201                      
37202                     region.showPanel(abn[r]);
37203                    
37204                 }
37205             }
37206         }
37207         return ret;
37208         
37209     },
37210     
37211     
37212 // private
37213     factory : function(cfg)
37214     {
37215         
37216         var validRegions = Roo.bootstrap.layout.Border.regions;
37217
37218         var target = cfg.region;
37219         cfg.mgr = this;
37220         
37221         var r = Roo.bootstrap.layout;
37222         Roo.log(target);
37223         switch(target){
37224             case "north":
37225                 return new r.North(cfg);
37226             case "south":
37227                 return new r.South(cfg);
37228             case "east":
37229                 return new r.East(cfg);
37230             case "west":
37231                 return new r.West(cfg);
37232             case "center":
37233                 return new r.Center(cfg);
37234         }
37235         throw 'Layout region "'+target+'" not supported.';
37236     }
37237     
37238     
37239 });
37240  /*
37241  * Based on:
37242  * Ext JS Library 1.1.1
37243  * Copyright(c) 2006-2007, Ext JS, LLC.
37244  *
37245  * Originally Released Under LGPL - original licence link has changed is not relivant.
37246  *
37247  * Fork - LGPL
37248  * <script type="text/javascript">
37249  */
37250  
37251 /**
37252  * @class Roo.bootstrap.layout.Basic
37253  * @extends Roo.util.Observable
37254  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37255  * and does not have a titlebar, tabs or any other features. All it does is size and position 
37256  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37257  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37258  * @cfg {string}   region  the region that it inhabits..
37259  * @cfg {bool}   skipConfig skip config?
37260  * 
37261
37262  */
37263 Roo.bootstrap.layout.Basic = function(config){
37264     
37265     this.mgr = config.mgr;
37266     
37267     this.position = config.region;
37268     
37269     var skipConfig = config.skipConfig;
37270     
37271     this.events = {
37272         /**
37273          * @scope Roo.BasicLayoutRegion
37274          */
37275         
37276         /**
37277          * @event beforeremove
37278          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37279          * @param {Roo.LayoutRegion} this
37280          * @param {Roo.ContentPanel} panel The panel
37281          * @param {Object} e The cancel event object
37282          */
37283         "beforeremove" : true,
37284         /**
37285          * @event invalidated
37286          * Fires when the layout for this region is changed.
37287          * @param {Roo.LayoutRegion} this
37288          */
37289         "invalidated" : true,
37290         /**
37291          * @event visibilitychange
37292          * Fires when this region is shown or hidden 
37293          * @param {Roo.LayoutRegion} this
37294          * @param {Boolean} visibility true or false
37295          */
37296         "visibilitychange" : true,
37297         /**
37298          * @event paneladded
37299          * Fires when a panel is added. 
37300          * @param {Roo.LayoutRegion} this
37301          * @param {Roo.ContentPanel} panel The panel
37302          */
37303         "paneladded" : true,
37304         /**
37305          * @event panelremoved
37306          * Fires when a panel is removed. 
37307          * @param {Roo.LayoutRegion} this
37308          * @param {Roo.ContentPanel} panel The panel
37309          */
37310         "panelremoved" : true,
37311         /**
37312          * @event beforecollapse
37313          * Fires when this region before collapse.
37314          * @param {Roo.LayoutRegion} this
37315          */
37316         "beforecollapse" : true,
37317         /**
37318          * @event collapsed
37319          * Fires when this region is collapsed.
37320          * @param {Roo.LayoutRegion} this
37321          */
37322         "collapsed" : true,
37323         /**
37324          * @event expanded
37325          * Fires when this region is expanded.
37326          * @param {Roo.LayoutRegion} this
37327          */
37328         "expanded" : true,
37329         /**
37330          * @event slideshow
37331          * Fires when this region is slid into view.
37332          * @param {Roo.LayoutRegion} this
37333          */
37334         "slideshow" : true,
37335         /**
37336          * @event slidehide
37337          * Fires when this region slides out of view. 
37338          * @param {Roo.LayoutRegion} this
37339          */
37340         "slidehide" : true,
37341         /**
37342          * @event panelactivated
37343          * Fires when a panel is activated. 
37344          * @param {Roo.LayoutRegion} this
37345          * @param {Roo.ContentPanel} panel The activated panel
37346          */
37347         "panelactivated" : true,
37348         /**
37349          * @event resized
37350          * Fires when the user resizes this region. 
37351          * @param {Roo.LayoutRegion} this
37352          * @param {Number} newSize The new size (width for east/west, height for north/south)
37353          */
37354         "resized" : true
37355     };
37356     /** A collection of panels in this region. @type Roo.util.MixedCollection */
37357     this.panels = new Roo.util.MixedCollection();
37358     this.panels.getKey = this.getPanelId.createDelegate(this);
37359     this.box = null;
37360     this.activePanel = null;
37361     // ensure listeners are added...
37362     
37363     if (config.listeners || config.events) {
37364         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37365             listeners : config.listeners || {},
37366             events : config.events || {}
37367         });
37368     }
37369     
37370     if(skipConfig !== true){
37371         this.applyConfig(config);
37372     }
37373 };
37374
37375 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
37376 {
37377     getPanelId : function(p){
37378         return p.getId();
37379     },
37380     
37381     applyConfig : function(config){
37382         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37383         this.config = config;
37384         
37385     },
37386     
37387     /**
37388      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
37389      * the width, for horizontal (north, south) the height.
37390      * @param {Number} newSize The new width or height
37391      */
37392     resizeTo : function(newSize){
37393         var el = this.el ? this.el :
37394                  (this.activePanel ? this.activePanel.getEl() : null);
37395         if(el){
37396             switch(this.position){
37397                 case "east":
37398                 case "west":
37399                     el.setWidth(newSize);
37400                     this.fireEvent("resized", this, newSize);
37401                 break;
37402                 case "north":
37403                 case "south":
37404                     el.setHeight(newSize);
37405                     this.fireEvent("resized", this, newSize);
37406                 break;                
37407             }
37408         }
37409     },
37410     
37411     getBox : function(){
37412         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
37413     },
37414     
37415     getMargins : function(){
37416         return this.margins;
37417     },
37418     
37419     updateBox : function(box){
37420         this.box = box;
37421         var el = this.activePanel.getEl();
37422         el.dom.style.left = box.x + "px";
37423         el.dom.style.top = box.y + "px";
37424         this.activePanel.setSize(box.width, box.height);
37425     },
37426     
37427     /**
37428      * Returns the container element for this region.
37429      * @return {Roo.Element}
37430      */
37431     getEl : function(){
37432         return this.activePanel;
37433     },
37434     
37435     /**
37436      * Returns true if this region is currently visible.
37437      * @return {Boolean}
37438      */
37439     isVisible : function(){
37440         return this.activePanel ? true : false;
37441     },
37442     
37443     setActivePanel : function(panel){
37444         panel = this.getPanel(panel);
37445         if(this.activePanel && this.activePanel != panel){
37446             this.activePanel.setActiveState(false);
37447             this.activePanel.getEl().setLeftTop(-10000,-10000);
37448         }
37449         this.activePanel = panel;
37450         panel.setActiveState(true);
37451         if(this.box){
37452             panel.setSize(this.box.width, this.box.height);
37453         }
37454         this.fireEvent("panelactivated", this, panel);
37455         this.fireEvent("invalidated");
37456     },
37457     
37458     /**
37459      * Show the specified panel.
37460      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
37461      * @return {Roo.ContentPanel} The shown panel or null
37462      */
37463     showPanel : function(panel){
37464         panel = this.getPanel(panel);
37465         if(panel){
37466             this.setActivePanel(panel);
37467         }
37468         return panel;
37469     },
37470     
37471     /**
37472      * Get the active panel for this region.
37473      * @return {Roo.ContentPanel} The active panel or null
37474      */
37475     getActivePanel : function(){
37476         return this.activePanel;
37477     },
37478     
37479     /**
37480      * Add the passed ContentPanel(s)
37481      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
37482      * @return {Roo.ContentPanel} The panel added (if only one was added)
37483      */
37484     add : function(panel){
37485         if(arguments.length > 1){
37486             for(var i = 0, len = arguments.length; i < len; i++) {
37487                 this.add(arguments[i]);
37488             }
37489             return null;
37490         }
37491         if(this.hasPanel(panel)){
37492             this.showPanel(panel);
37493             return panel;
37494         }
37495         var el = panel.getEl();
37496         if(el.dom.parentNode != this.mgr.el.dom){
37497             this.mgr.el.dom.appendChild(el.dom);
37498         }
37499         if(panel.setRegion){
37500             panel.setRegion(this);
37501         }
37502         this.panels.add(panel);
37503         el.setStyle("position", "absolute");
37504         if(!panel.background){
37505             this.setActivePanel(panel);
37506             if(this.config.initialSize && this.panels.getCount()==1){
37507                 this.resizeTo(this.config.initialSize);
37508             }
37509         }
37510         this.fireEvent("paneladded", this, panel);
37511         return panel;
37512     },
37513     
37514     /**
37515      * Returns true if the panel is in this region.
37516      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37517      * @return {Boolean}
37518      */
37519     hasPanel : function(panel){
37520         if(typeof panel == "object"){ // must be panel obj
37521             panel = panel.getId();
37522         }
37523         return this.getPanel(panel) ? true : false;
37524     },
37525     
37526     /**
37527      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
37528      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37529      * @param {Boolean} preservePanel Overrides the config preservePanel option
37530      * @return {Roo.ContentPanel} The panel that was removed
37531      */
37532     remove : function(panel, preservePanel){
37533         panel = this.getPanel(panel);
37534         if(!panel){
37535             return null;
37536         }
37537         var e = {};
37538         this.fireEvent("beforeremove", this, panel, e);
37539         if(e.cancel === true){
37540             return null;
37541         }
37542         var panelId = panel.getId();
37543         this.panels.removeKey(panelId);
37544         return panel;
37545     },
37546     
37547     /**
37548      * Returns the panel specified or null if it's not in this region.
37549      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37550      * @return {Roo.ContentPanel}
37551      */
37552     getPanel : function(id){
37553         if(typeof id == "object"){ // must be panel obj
37554             return id;
37555         }
37556         return this.panels.get(id);
37557     },
37558     
37559     /**
37560      * Returns this regions position (north/south/east/west/center).
37561      * @return {String} 
37562      */
37563     getPosition: function(){
37564         return this.position;    
37565     }
37566 });/*
37567  * Based on:
37568  * Ext JS Library 1.1.1
37569  * Copyright(c) 2006-2007, Ext JS, LLC.
37570  *
37571  * Originally Released Under LGPL - original licence link has changed is not relivant.
37572  *
37573  * Fork - LGPL
37574  * <script type="text/javascript">
37575  */
37576  
37577 /**
37578  * @class Roo.bootstrap.layout.Region
37579  * @extends Roo.bootstrap.layout.Basic
37580  * This class represents a region in a layout manager.
37581  
37582  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
37583  * @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})
37584  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
37585  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
37586  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
37587  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
37588  * @cfg {String}    title           The title for the region (overrides panel titles)
37589  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
37590  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
37591  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
37592  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
37593  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
37594  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
37595  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
37596  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
37597  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
37598  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
37599
37600  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
37601  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
37602  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
37603  * @cfg {Number}    width           For East/West panels
37604  * @cfg {Number}    height          For North/South panels
37605  * @cfg {Boolean}   split           To show the splitter
37606  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
37607  * 
37608  * @cfg {string}   cls             Extra CSS classes to add to region
37609  * 
37610  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37611  * @cfg {string}   region  the region that it inhabits..
37612  *
37613
37614  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
37615  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
37616
37617  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
37618  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
37619  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
37620  */
37621 Roo.bootstrap.layout.Region = function(config)
37622 {
37623     this.applyConfig(config);
37624
37625     var mgr = config.mgr;
37626     var pos = config.region;
37627     config.skipConfig = true;
37628     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
37629     
37630     if (mgr.el) {
37631         this.onRender(mgr.el);   
37632     }
37633      
37634     this.visible = true;
37635     this.collapsed = false;
37636     this.unrendered_panels = [];
37637 };
37638
37639 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
37640
37641     position: '', // set by wrapper (eg. north/south etc..)
37642     unrendered_panels : null,  // unrendered panels.
37643     
37644     tabPosition : false,
37645     
37646     mgr: false, // points to 'Border'
37647     
37648     
37649     createBody : function(){
37650         /** This region's body element 
37651         * @type Roo.Element */
37652         this.bodyEl = this.el.createChild({
37653                 tag: "div",
37654                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
37655         });
37656     },
37657
37658     onRender: function(ctr, pos)
37659     {
37660         var dh = Roo.DomHelper;
37661         /** This region's container element 
37662         * @type Roo.Element */
37663         this.el = dh.append(ctr.dom, {
37664                 tag: "div",
37665                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
37666             }, true);
37667         /** This region's title element 
37668         * @type Roo.Element */
37669     
37670         this.titleEl = dh.append(this.el.dom,  {
37671                 tag: "div",
37672                 unselectable: "on",
37673                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
37674                 children:[
37675                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
37676                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
37677                 ]
37678             }, true);
37679         
37680         this.titleEl.enableDisplayMode();
37681         /** This region's title text element 
37682         * @type HTMLElement */
37683         this.titleTextEl = this.titleEl.dom.firstChild;
37684         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
37685         /*
37686         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
37687         this.closeBtn.enableDisplayMode();
37688         this.closeBtn.on("click", this.closeClicked, this);
37689         this.closeBtn.hide();
37690     */
37691         this.createBody(this.config);
37692         if(this.config.hideWhenEmpty){
37693             this.hide();
37694             this.on("paneladded", this.validateVisibility, this);
37695             this.on("panelremoved", this.validateVisibility, this);
37696         }
37697         if(this.autoScroll){
37698             this.bodyEl.setStyle("overflow", "auto");
37699         }else{
37700             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
37701         }
37702         //if(c.titlebar !== false){
37703             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
37704                 this.titleEl.hide();
37705             }else{
37706                 this.titleEl.show();
37707                 if(this.config.title){
37708                     this.titleTextEl.innerHTML = this.config.title;
37709                 }
37710             }
37711         //}
37712         if(this.config.collapsed){
37713             this.collapse(true);
37714         }
37715         if(this.config.hidden){
37716             this.hide();
37717         }
37718         
37719         if (this.unrendered_panels && this.unrendered_panels.length) {
37720             for (var i =0;i< this.unrendered_panels.length; i++) {
37721                 this.add(this.unrendered_panels[i]);
37722             }
37723             this.unrendered_panels = null;
37724             
37725         }
37726         
37727     },
37728     
37729     applyConfig : function(c)
37730     {
37731         /*
37732          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
37733             var dh = Roo.DomHelper;
37734             if(c.titlebar !== false){
37735                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
37736                 this.collapseBtn.on("click", this.collapse, this);
37737                 this.collapseBtn.enableDisplayMode();
37738                 /*
37739                 if(c.showPin === true || this.showPin){
37740                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
37741                     this.stickBtn.enableDisplayMode();
37742                     this.stickBtn.on("click", this.expand, this);
37743                     this.stickBtn.hide();
37744                 }
37745                 
37746             }
37747             */
37748             /** This region's collapsed element
37749             * @type Roo.Element */
37750             /*
37751              *
37752             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
37753                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
37754             ]}, true);
37755             
37756             if(c.floatable !== false){
37757                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
37758                this.collapsedEl.on("click", this.collapseClick, this);
37759             }
37760
37761             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
37762                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
37763                    id: "message", unselectable: "on", style:{"float":"left"}});
37764                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
37765              }
37766             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
37767             this.expandBtn.on("click", this.expand, this);
37768             
37769         }
37770         
37771         if(this.collapseBtn){
37772             this.collapseBtn.setVisible(c.collapsible == true);
37773         }
37774         
37775         this.cmargins = c.cmargins || this.cmargins ||
37776                          (this.position == "west" || this.position == "east" ?
37777                              {top: 0, left: 2, right:2, bottom: 0} :
37778                              {top: 2, left: 0, right:0, bottom: 2});
37779         */
37780         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37781         
37782         
37783         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
37784         
37785         this.autoScroll = c.autoScroll || false;
37786         
37787         
37788        
37789         
37790         this.duration = c.duration || .30;
37791         this.slideDuration = c.slideDuration || .45;
37792         this.config = c;
37793        
37794     },
37795     /**
37796      * Returns true if this region is currently visible.
37797      * @return {Boolean}
37798      */
37799     isVisible : function(){
37800         return this.visible;
37801     },
37802
37803     /**
37804      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
37805      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
37806      */
37807     //setCollapsedTitle : function(title){
37808     //    title = title || "&#160;";
37809      //   if(this.collapsedTitleTextEl){
37810       //      this.collapsedTitleTextEl.innerHTML = title;
37811        // }
37812     //},
37813
37814     getBox : function(){
37815         var b;
37816       //  if(!this.collapsed){
37817             b = this.el.getBox(false, true);
37818        // }else{
37819           //  b = this.collapsedEl.getBox(false, true);
37820         //}
37821         return b;
37822     },
37823
37824     getMargins : function(){
37825         return this.margins;
37826         //return this.collapsed ? this.cmargins : this.margins;
37827     },
37828 /*
37829     highlight : function(){
37830         this.el.addClass("x-layout-panel-dragover");
37831     },
37832
37833     unhighlight : function(){
37834         this.el.removeClass("x-layout-panel-dragover");
37835     },
37836 */
37837     updateBox : function(box)
37838     {
37839         if (!this.bodyEl) {
37840             return; // not rendered yet..
37841         }
37842         
37843         this.box = box;
37844         if(!this.collapsed){
37845             this.el.dom.style.left = box.x + "px";
37846             this.el.dom.style.top = box.y + "px";
37847             this.updateBody(box.width, box.height);
37848         }else{
37849             this.collapsedEl.dom.style.left = box.x + "px";
37850             this.collapsedEl.dom.style.top = box.y + "px";
37851             this.collapsedEl.setSize(box.width, box.height);
37852         }
37853         if(this.tabs){
37854             this.tabs.autoSizeTabs();
37855         }
37856     },
37857
37858     updateBody : function(w, h)
37859     {
37860         if(w !== null){
37861             this.el.setWidth(w);
37862             w -= this.el.getBorderWidth("rl");
37863             if(this.config.adjustments){
37864                 w += this.config.adjustments[0];
37865             }
37866         }
37867         if(h !== null && h > 0){
37868             this.el.setHeight(h);
37869             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
37870             h -= this.el.getBorderWidth("tb");
37871             if(this.config.adjustments){
37872                 h += this.config.adjustments[1];
37873             }
37874             this.bodyEl.setHeight(h);
37875             if(this.tabs){
37876                 h = this.tabs.syncHeight(h);
37877             }
37878         }
37879         if(this.panelSize){
37880             w = w !== null ? w : this.panelSize.width;
37881             h = h !== null ? h : this.panelSize.height;
37882         }
37883         if(this.activePanel){
37884             var el = this.activePanel.getEl();
37885             w = w !== null ? w : el.getWidth();
37886             h = h !== null ? h : el.getHeight();
37887             this.panelSize = {width: w, height: h};
37888             this.activePanel.setSize(w, h);
37889         }
37890         if(Roo.isIE && this.tabs){
37891             this.tabs.el.repaint();
37892         }
37893     },
37894
37895     /**
37896      * Returns the container element for this region.
37897      * @return {Roo.Element}
37898      */
37899     getEl : function(){
37900         return this.el;
37901     },
37902
37903     /**
37904      * Hides this region.
37905      */
37906     hide : function(){
37907         //if(!this.collapsed){
37908             this.el.dom.style.left = "-2000px";
37909             this.el.hide();
37910         //}else{
37911          //   this.collapsedEl.dom.style.left = "-2000px";
37912          //   this.collapsedEl.hide();
37913        // }
37914         this.visible = false;
37915         this.fireEvent("visibilitychange", this, false);
37916     },
37917
37918     /**
37919      * Shows this region if it was previously hidden.
37920      */
37921     show : function(){
37922         //if(!this.collapsed){
37923             this.el.show();
37924         //}else{
37925         //    this.collapsedEl.show();
37926        // }
37927         this.visible = true;
37928         this.fireEvent("visibilitychange", this, true);
37929     },
37930 /*
37931     closeClicked : function(){
37932         if(this.activePanel){
37933             this.remove(this.activePanel);
37934         }
37935     },
37936
37937     collapseClick : function(e){
37938         if(this.isSlid){
37939            e.stopPropagation();
37940            this.slideIn();
37941         }else{
37942            e.stopPropagation();
37943            this.slideOut();
37944         }
37945     },
37946 */
37947     /**
37948      * Collapses this region.
37949      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
37950      */
37951     /*
37952     collapse : function(skipAnim, skipCheck = false){
37953         if(this.collapsed) {
37954             return;
37955         }
37956         
37957         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
37958             
37959             this.collapsed = true;
37960             if(this.split){
37961                 this.split.el.hide();
37962             }
37963             if(this.config.animate && skipAnim !== true){
37964                 this.fireEvent("invalidated", this);
37965                 this.animateCollapse();
37966             }else{
37967                 this.el.setLocation(-20000,-20000);
37968                 this.el.hide();
37969                 this.collapsedEl.show();
37970                 this.fireEvent("collapsed", this);
37971                 this.fireEvent("invalidated", this);
37972             }
37973         }
37974         
37975     },
37976 */
37977     animateCollapse : function(){
37978         // overridden
37979     },
37980
37981     /**
37982      * Expands this region if it was previously collapsed.
37983      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
37984      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
37985      */
37986     /*
37987     expand : function(e, skipAnim){
37988         if(e) {
37989             e.stopPropagation();
37990         }
37991         if(!this.collapsed || this.el.hasActiveFx()) {
37992             return;
37993         }
37994         if(this.isSlid){
37995             this.afterSlideIn();
37996             skipAnim = true;
37997         }
37998         this.collapsed = false;
37999         if(this.config.animate && skipAnim !== true){
38000             this.animateExpand();
38001         }else{
38002             this.el.show();
38003             if(this.split){
38004                 this.split.el.show();
38005             }
38006             this.collapsedEl.setLocation(-2000,-2000);
38007             this.collapsedEl.hide();
38008             this.fireEvent("invalidated", this);
38009             this.fireEvent("expanded", this);
38010         }
38011     },
38012 */
38013     animateExpand : function(){
38014         // overridden
38015     },
38016
38017     initTabs : function()
38018     {
38019         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38020         
38021         var ts = new Roo.bootstrap.panel.Tabs({
38022             el: this.bodyEl.dom,
38023             region : this,
38024             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
38025             disableTooltips: this.config.disableTabTips,
38026             toolbar : this.config.toolbar
38027         });
38028         
38029         if(this.config.hideTabs){
38030             ts.stripWrap.setDisplayed(false);
38031         }
38032         this.tabs = ts;
38033         ts.resizeTabs = this.config.resizeTabs === true;
38034         ts.minTabWidth = this.config.minTabWidth || 40;
38035         ts.maxTabWidth = this.config.maxTabWidth || 250;
38036         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38037         ts.monitorResize = false;
38038         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38039         ts.bodyEl.addClass('roo-layout-tabs-body');
38040         this.panels.each(this.initPanelAsTab, this);
38041     },
38042
38043     initPanelAsTab : function(panel){
38044         var ti = this.tabs.addTab(
38045             panel.getEl().id,
38046             panel.getTitle(),
38047             null,
38048             this.config.closeOnTab && panel.isClosable(),
38049             panel.tpl
38050         );
38051         if(panel.tabTip !== undefined){
38052             ti.setTooltip(panel.tabTip);
38053         }
38054         ti.on("activate", function(){
38055               this.setActivePanel(panel);
38056         }, this);
38057         
38058         if(this.config.closeOnTab){
38059             ti.on("beforeclose", function(t, e){
38060                 e.cancel = true;
38061                 this.remove(panel);
38062             }, this);
38063         }
38064         
38065         panel.tabItem = ti;
38066         
38067         return ti;
38068     },
38069
38070     updatePanelTitle : function(panel, title)
38071     {
38072         if(this.activePanel == panel){
38073             this.updateTitle(title);
38074         }
38075         if(this.tabs){
38076             var ti = this.tabs.getTab(panel.getEl().id);
38077             ti.setText(title);
38078             if(panel.tabTip !== undefined){
38079                 ti.setTooltip(panel.tabTip);
38080             }
38081         }
38082     },
38083
38084     updateTitle : function(title){
38085         if(this.titleTextEl && !this.config.title){
38086             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
38087         }
38088     },
38089
38090     setActivePanel : function(panel)
38091     {
38092         panel = this.getPanel(panel);
38093         if(this.activePanel && this.activePanel != panel){
38094             if(this.activePanel.setActiveState(false) === false){
38095                 return;
38096             }
38097         }
38098         this.activePanel = panel;
38099         panel.setActiveState(true);
38100         if(this.panelSize){
38101             panel.setSize(this.panelSize.width, this.panelSize.height);
38102         }
38103         if(this.closeBtn){
38104             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38105         }
38106         this.updateTitle(panel.getTitle());
38107         if(this.tabs){
38108             this.fireEvent("invalidated", this);
38109         }
38110         this.fireEvent("panelactivated", this, panel);
38111     },
38112
38113     /**
38114      * Shows the specified panel.
38115      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38116      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38117      */
38118     showPanel : function(panel)
38119     {
38120         panel = this.getPanel(panel);
38121         if(panel){
38122             if(this.tabs){
38123                 var tab = this.tabs.getTab(panel.getEl().id);
38124                 if(tab.isHidden()){
38125                     this.tabs.unhideTab(tab.id);
38126                 }
38127                 tab.activate();
38128             }else{
38129                 this.setActivePanel(panel);
38130             }
38131         }
38132         return panel;
38133     },
38134
38135     /**
38136      * Get the active panel for this region.
38137      * @return {Roo.ContentPanel} The active panel or null
38138      */
38139     getActivePanel : function(){
38140         return this.activePanel;
38141     },
38142
38143     validateVisibility : function(){
38144         if(this.panels.getCount() < 1){
38145             this.updateTitle("&#160;");
38146             this.closeBtn.hide();
38147             this.hide();
38148         }else{
38149             if(!this.isVisible()){
38150                 this.show();
38151             }
38152         }
38153     },
38154
38155     /**
38156      * Adds the passed ContentPanel(s) to this region.
38157      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38158      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38159      */
38160     add : function(panel)
38161     {
38162         if(arguments.length > 1){
38163             for(var i = 0, len = arguments.length; i < len; i++) {
38164                 this.add(arguments[i]);
38165             }
38166             return null;
38167         }
38168         
38169         // if we have not been rendered yet, then we can not really do much of this..
38170         if (!this.bodyEl) {
38171             this.unrendered_panels.push(panel);
38172             return panel;
38173         }
38174         
38175         
38176         
38177         
38178         if(this.hasPanel(panel)){
38179             this.showPanel(panel);
38180             return panel;
38181         }
38182         panel.setRegion(this);
38183         this.panels.add(panel);
38184        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38185             // sinle panel - no tab...?? would it not be better to render it with the tabs,
38186             // and hide them... ???
38187             this.bodyEl.dom.appendChild(panel.getEl().dom);
38188             if(panel.background !== true){
38189                 this.setActivePanel(panel);
38190             }
38191             this.fireEvent("paneladded", this, panel);
38192             return panel;
38193         }
38194         */
38195         if(!this.tabs){
38196             this.initTabs();
38197         }else{
38198             this.initPanelAsTab(panel);
38199         }
38200         
38201         
38202         if(panel.background !== true){
38203             this.tabs.activate(panel.getEl().id);
38204         }
38205         this.fireEvent("paneladded", this, panel);
38206         return panel;
38207     },
38208
38209     /**
38210      * Hides the tab for the specified panel.
38211      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38212      */
38213     hidePanel : function(panel){
38214         if(this.tabs && (panel = this.getPanel(panel))){
38215             this.tabs.hideTab(panel.getEl().id);
38216         }
38217     },
38218
38219     /**
38220      * Unhides the tab for a previously hidden panel.
38221      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38222      */
38223     unhidePanel : function(panel){
38224         if(this.tabs && (panel = this.getPanel(panel))){
38225             this.tabs.unhideTab(panel.getEl().id);
38226         }
38227     },
38228
38229     clearPanels : function(){
38230         while(this.panels.getCount() > 0){
38231              this.remove(this.panels.first());
38232         }
38233     },
38234
38235     /**
38236      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38237      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38238      * @param {Boolean} preservePanel Overrides the config preservePanel option
38239      * @return {Roo.ContentPanel} The panel that was removed
38240      */
38241     remove : function(panel, preservePanel)
38242     {
38243         panel = this.getPanel(panel);
38244         if(!panel){
38245             return null;
38246         }
38247         var e = {};
38248         this.fireEvent("beforeremove", this, panel, e);
38249         if(e.cancel === true){
38250             return null;
38251         }
38252         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38253         var panelId = panel.getId();
38254         this.panels.removeKey(panelId);
38255         if(preservePanel){
38256             document.body.appendChild(panel.getEl().dom);
38257         }
38258         if(this.tabs){
38259             this.tabs.removeTab(panel.getEl().id);
38260         }else if (!preservePanel){
38261             this.bodyEl.dom.removeChild(panel.getEl().dom);
38262         }
38263         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38264             var p = this.panels.first();
38265             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38266             tempEl.appendChild(p.getEl().dom);
38267             this.bodyEl.update("");
38268             this.bodyEl.dom.appendChild(p.getEl().dom);
38269             tempEl = null;
38270             this.updateTitle(p.getTitle());
38271             this.tabs = null;
38272             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38273             this.setActivePanel(p);
38274         }
38275         panel.setRegion(null);
38276         if(this.activePanel == panel){
38277             this.activePanel = null;
38278         }
38279         if(this.config.autoDestroy !== false && preservePanel !== true){
38280             try{panel.destroy();}catch(e){}
38281         }
38282         this.fireEvent("panelremoved", this, panel);
38283         return panel;
38284     },
38285
38286     /**
38287      * Returns the TabPanel component used by this region
38288      * @return {Roo.TabPanel}
38289      */
38290     getTabs : function(){
38291         return this.tabs;
38292     },
38293
38294     createTool : function(parentEl, className){
38295         var btn = Roo.DomHelper.append(parentEl, {
38296             tag: "div",
38297             cls: "x-layout-tools-button",
38298             children: [ {
38299                 tag: "div",
38300                 cls: "roo-layout-tools-button-inner " + className,
38301                 html: "&#160;"
38302             }]
38303         }, true);
38304         btn.addClassOnOver("roo-layout-tools-button-over");
38305         return btn;
38306     }
38307 });/*
38308  * Based on:
38309  * Ext JS Library 1.1.1
38310  * Copyright(c) 2006-2007, Ext JS, LLC.
38311  *
38312  * Originally Released Under LGPL - original licence link has changed is not relivant.
38313  *
38314  * Fork - LGPL
38315  * <script type="text/javascript">
38316  */
38317  
38318
38319
38320 /**
38321  * @class Roo.SplitLayoutRegion
38322  * @extends Roo.LayoutRegion
38323  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38324  */
38325 Roo.bootstrap.layout.Split = function(config){
38326     this.cursor = config.cursor;
38327     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38328 };
38329
38330 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38331 {
38332     splitTip : "Drag to resize.",
38333     collapsibleSplitTip : "Drag to resize. Double click to hide.",
38334     useSplitTips : false,
38335
38336     applyConfig : function(config){
38337         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38338     },
38339     
38340     onRender : function(ctr,pos) {
38341         
38342         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38343         if(!this.config.split){
38344             return;
38345         }
38346         if(!this.split){
38347             
38348             var splitEl = Roo.DomHelper.append(ctr.dom,  {
38349                             tag: "div",
38350                             id: this.el.id + "-split",
38351                             cls: "roo-layout-split roo-layout-split-"+this.position,
38352                             html: "&#160;"
38353             });
38354             /** The SplitBar for this region 
38355             * @type Roo.SplitBar */
38356             // does not exist yet...
38357             Roo.log([this.position, this.orientation]);
38358             
38359             this.split = new Roo.bootstrap.SplitBar({
38360                 dragElement : splitEl,
38361                 resizingElement: this.el,
38362                 orientation : this.orientation
38363             });
38364             
38365             this.split.on("moved", this.onSplitMove, this);
38366             this.split.useShim = this.config.useShim === true;
38367             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38368             if(this.useSplitTips){
38369                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38370             }
38371             //if(config.collapsible){
38372             //    this.split.el.on("dblclick", this.collapse,  this);
38373             //}
38374         }
38375         if(typeof this.config.minSize != "undefined"){
38376             this.split.minSize = this.config.minSize;
38377         }
38378         if(typeof this.config.maxSize != "undefined"){
38379             this.split.maxSize = this.config.maxSize;
38380         }
38381         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
38382             this.hideSplitter();
38383         }
38384         
38385     },
38386
38387     getHMaxSize : function(){
38388          var cmax = this.config.maxSize || 10000;
38389          var center = this.mgr.getRegion("center");
38390          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
38391     },
38392
38393     getVMaxSize : function(){
38394          var cmax = this.config.maxSize || 10000;
38395          var center = this.mgr.getRegion("center");
38396          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
38397     },
38398
38399     onSplitMove : function(split, newSize){
38400         this.fireEvent("resized", this, newSize);
38401     },
38402     
38403     /** 
38404      * Returns the {@link Roo.SplitBar} for this region.
38405      * @return {Roo.SplitBar}
38406      */
38407     getSplitBar : function(){
38408         return this.split;
38409     },
38410     
38411     hide : function(){
38412         this.hideSplitter();
38413         Roo.bootstrap.layout.Split.superclass.hide.call(this);
38414     },
38415
38416     hideSplitter : function(){
38417         if(this.split){
38418             this.split.el.setLocation(-2000,-2000);
38419             this.split.el.hide();
38420         }
38421     },
38422
38423     show : function(){
38424         if(this.split){
38425             this.split.el.show();
38426         }
38427         Roo.bootstrap.layout.Split.superclass.show.call(this);
38428     },
38429     
38430     beforeSlide: function(){
38431         if(Roo.isGecko){// firefox overflow auto bug workaround
38432             this.bodyEl.clip();
38433             if(this.tabs) {
38434                 this.tabs.bodyEl.clip();
38435             }
38436             if(this.activePanel){
38437                 this.activePanel.getEl().clip();
38438                 
38439                 if(this.activePanel.beforeSlide){
38440                     this.activePanel.beforeSlide();
38441                 }
38442             }
38443         }
38444     },
38445     
38446     afterSlide : function(){
38447         if(Roo.isGecko){// firefox overflow auto bug workaround
38448             this.bodyEl.unclip();
38449             if(this.tabs) {
38450                 this.tabs.bodyEl.unclip();
38451             }
38452             if(this.activePanel){
38453                 this.activePanel.getEl().unclip();
38454                 if(this.activePanel.afterSlide){
38455                     this.activePanel.afterSlide();
38456                 }
38457             }
38458         }
38459     },
38460
38461     initAutoHide : function(){
38462         if(this.autoHide !== false){
38463             if(!this.autoHideHd){
38464                 var st = new Roo.util.DelayedTask(this.slideIn, this);
38465                 this.autoHideHd = {
38466                     "mouseout": function(e){
38467                         if(!e.within(this.el, true)){
38468                             st.delay(500);
38469                         }
38470                     },
38471                     "mouseover" : function(e){
38472                         st.cancel();
38473                     },
38474                     scope : this
38475                 };
38476             }
38477             this.el.on(this.autoHideHd);
38478         }
38479     },
38480
38481     clearAutoHide : function(){
38482         if(this.autoHide !== false){
38483             this.el.un("mouseout", this.autoHideHd.mouseout);
38484             this.el.un("mouseover", this.autoHideHd.mouseover);
38485         }
38486     },
38487
38488     clearMonitor : function(){
38489         Roo.get(document).un("click", this.slideInIf, this);
38490     },
38491
38492     // these names are backwards but not changed for compat
38493     slideOut : function(){
38494         if(this.isSlid || this.el.hasActiveFx()){
38495             return;
38496         }
38497         this.isSlid = true;
38498         if(this.collapseBtn){
38499             this.collapseBtn.hide();
38500         }
38501         this.closeBtnState = this.closeBtn.getStyle('display');
38502         this.closeBtn.hide();
38503         if(this.stickBtn){
38504             this.stickBtn.show();
38505         }
38506         this.el.show();
38507         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
38508         this.beforeSlide();
38509         this.el.setStyle("z-index", 10001);
38510         this.el.slideIn(this.getSlideAnchor(), {
38511             callback: function(){
38512                 this.afterSlide();
38513                 this.initAutoHide();
38514                 Roo.get(document).on("click", this.slideInIf, this);
38515                 this.fireEvent("slideshow", this);
38516             },
38517             scope: this,
38518             block: true
38519         });
38520     },
38521
38522     afterSlideIn : function(){
38523         this.clearAutoHide();
38524         this.isSlid = false;
38525         this.clearMonitor();
38526         this.el.setStyle("z-index", "");
38527         if(this.collapseBtn){
38528             this.collapseBtn.show();
38529         }
38530         this.closeBtn.setStyle('display', this.closeBtnState);
38531         if(this.stickBtn){
38532             this.stickBtn.hide();
38533         }
38534         this.fireEvent("slidehide", this);
38535     },
38536
38537     slideIn : function(cb){
38538         if(!this.isSlid || this.el.hasActiveFx()){
38539             Roo.callback(cb);
38540             return;
38541         }
38542         this.isSlid = false;
38543         this.beforeSlide();
38544         this.el.slideOut(this.getSlideAnchor(), {
38545             callback: function(){
38546                 this.el.setLeftTop(-10000, -10000);
38547                 this.afterSlide();
38548                 this.afterSlideIn();
38549                 Roo.callback(cb);
38550             },
38551             scope: this,
38552             block: true
38553         });
38554     },
38555     
38556     slideInIf : function(e){
38557         if(!e.within(this.el)){
38558             this.slideIn();
38559         }
38560     },
38561
38562     animateCollapse : function(){
38563         this.beforeSlide();
38564         this.el.setStyle("z-index", 20000);
38565         var anchor = this.getSlideAnchor();
38566         this.el.slideOut(anchor, {
38567             callback : function(){
38568                 this.el.setStyle("z-index", "");
38569                 this.collapsedEl.slideIn(anchor, {duration:.3});
38570                 this.afterSlide();
38571                 this.el.setLocation(-10000,-10000);
38572                 this.el.hide();
38573                 this.fireEvent("collapsed", this);
38574             },
38575             scope: this,
38576             block: true
38577         });
38578     },
38579
38580     animateExpand : function(){
38581         this.beforeSlide();
38582         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
38583         this.el.setStyle("z-index", 20000);
38584         this.collapsedEl.hide({
38585             duration:.1
38586         });
38587         this.el.slideIn(this.getSlideAnchor(), {
38588             callback : function(){
38589                 this.el.setStyle("z-index", "");
38590                 this.afterSlide();
38591                 if(this.split){
38592                     this.split.el.show();
38593                 }
38594                 this.fireEvent("invalidated", this);
38595                 this.fireEvent("expanded", this);
38596             },
38597             scope: this,
38598             block: true
38599         });
38600     },
38601
38602     anchors : {
38603         "west" : "left",
38604         "east" : "right",
38605         "north" : "top",
38606         "south" : "bottom"
38607     },
38608
38609     sanchors : {
38610         "west" : "l",
38611         "east" : "r",
38612         "north" : "t",
38613         "south" : "b"
38614     },
38615
38616     canchors : {
38617         "west" : "tl-tr",
38618         "east" : "tr-tl",
38619         "north" : "tl-bl",
38620         "south" : "bl-tl"
38621     },
38622
38623     getAnchor : function(){
38624         return this.anchors[this.position];
38625     },
38626
38627     getCollapseAnchor : function(){
38628         return this.canchors[this.position];
38629     },
38630
38631     getSlideAnchor : function(){
38632         return this.sanchors[this.position];
38633     },
38634
38635     getAlignAdj : function(){
38636         var cm = this.cmargins;
38637         switch(this.position){
38638             case "west":
38639                 return [0, 0];
38640             break;
38641             case "east":
38642                 return [0, 0];
38643             break;
38644             case "north":
38645                 return [0, 0];
38646             break;
38647             case "south":
38648                 return [0, 0];
38649             break;
38650         }
38651     },
38652
38653     getExpandAdj : function(){
38654         var c = this.collapsedEl, cm = this.cmargins;
38655         switch(this.position){
38656             case "west":
38657                 return [-(cm.right+c.getWidth()+cm.left), 0];
38658             break;
38659             case "east":
38660                 return [cm.right+c.getWidth()+cm.left, 0];
38661             break;
38662             case "north":
38663                 return [0, -(cm.top+cm.bottom+c.getHeight())];
38664             break;
38665             case "south":
38666                 return [0, cm.top+cm.bottom+c.getHeight()];
38667             break;
38668         }
38669     }
38670 });/*
38671  * Based on:
38672  * Ext JS Library 1.1.1
38673  * Copyright(c) 2006-2007, Ext JS, LLC.
38674  *
38675  * Originally Released Under LGPL - original licence link has changed is not relivant.
38676  *
38677  * Fork - LGPL
38678  * <script type="text/javascript">
38679  */
38680 /*
38681  * These classes are private internal classes
38682  */
38683 Roo.bootstrap.layout.Center = function(config){
38684     config.region = "center";
38685     Roo.bootstrap.layout.Region.call(this, config);
38686     this.visible = true;
38687     this.minWidth = config.minWidth || 20;
38688     this.minHeight = config.minHeight || 20;
38689 };
38690
38691 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
38692     hide : function(){
38693         // center panel can't be hidden
38694     },
38695     
38696     show : function(){
38697         // center panel can't be hidden
38698     },
38699     
38700     getMinWidth: function(){
38701         return this.minWidth;
38702     },
38703     
38704     getMinHeight: function(){
38705         return this.minHeight;
38706     }
38707 });
38708
38709
38710
38711
38712  
38713
38714
38715
38716
38717
38718
38719 Roo.bootstrap.layout.North = function(config)
38720 {
38721     config.region = 'north';
38722     config.cursor = 'n-resize';
38723     
38724     Roo.bootstrap.layout.Split.call(this, config);
38725     
38726     
38727     if(this.split){
38728         this.split.placement = Roo.bootstrap.SplitBar.TOP;
38729         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
38730         this.split.el.addClass("roo-layout-split-v");
38731     }
38732     var size = config.initialSize || config.height;
38733     if(typeof size != "undefined"){
38734         this.el.setHeight(size);
38735     }
38736 };
38737 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
38738 {
38739     orientation: Roo.bootstrap.SplitBar.VERTICAL,
38740     
38741     
38742     
38743     getBox : function(){
38744         if(this.collapsed){
38745             return this.collapsedEl.getBox();
38746         }
38747         var box = this.el.getBox();
38748         if(this.split){
38749             box.height += this.split.el.getHeight();
38750         }
38751         return box;
38752     },
38753     
38754     updateBox : function(box){
38755         if(this.split && !this.collapsed){
38756             box.height -= this.split.el.getHeight();
38757             this.split.el.setLeft(box.x);
38758             this.split.el.setTop(box.y+box.height);
38759             this.split.el.setWidth(box.width);
38760         }
38761         if(this.collapsed){
38762             this.updateBody(box.width, null);
38763         }
38764         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38765     }
38766 });
38767
38768
38769
38770
38771
38772 Roo.bootstrap.layout.South = function(config){
38773     config.region = 'south';
38774     config.cursor = 's-resize';
38775     Roo.bootstrap.layout.Split.call(this, config);
38776     if(this.split){
38777         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
38778         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
38779         this.split.el.addClass("roo-layout-split-v");
38780     }
38781     var size = config.initialSize || config.height;
38782     if(typeof size != "undefined"){
38783         this.el.setHeight(size);
38784     }
38785 };
38786
38787 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
38788     orientation: Roo.bootstrap.SplitBar.VERTICAL,
38789     getBox : function(){
38790         if(this.collapsed){
38791             return this.collapsedEl.getBox();
38792         }
38793         var box = this.el.getBox();
38794         if(this.split){
38795             var sh = this.split.el.getHeight();
38796             box.height += sh;
38797             box.y -= sh;
38798         }
38799         return box;
38800     },
38801     
38802     updateBox : function(box){
38803         if(this.split && !this.collapsed){
38804             var sh = this.split.el.getHeight();
38805             box.height -= sh;
38806             box.y += sh;
38807             this.split.el.setLeft(box.x);
38808             this.split.el.setTop(box.y-sh);
38809             this.split.el.setWidth(box.width);
38810         }
38811         if(this.collapsed){
38812             this.updateBody(box.width, null);
38813         }
38814         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38815     }
38816 });
38817
38818 Roo.bootstrap.layout.East = function(config){
38819     config.region = "east";
38820     config.cursor = "e-resize";
38821     Roo.bootstrap.layout.Split.call(this, config);
38822     if(this.split){
38823         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
38824         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
38825         this.split.el.addClass("roo-layout-split-h");
38826     }
38827     var size = config.initialSize || config.width;
38828     if(typeof size != "undefined"){
38829         this.el.setWidth(size);
38830     }
38831 };
38832 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
38833     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
38834     getBox : function(){
38835         if(this.collapsed){
38836             return this.collapsedEl.getBox();
38837         }
38838         var box = this.el.getBox();
38839         if(this.split){
38840             var sw = this.split.el.getWidth();
38841             box.width += sw;
38842             box.x -= sw;
38843         }
38844         return box;
38845     },
38846
38847     updateBox : function(box){
38848         if(this.split && !this.collapsed){
38849             var sw = this.split.el.getWidth();
38850             box.width -= sw;
38851             this.split.el.setLeft(box.x);
38852             this.split.el.setTop(box.y);
38853             this.split.el.setHeight(box.height);
38854             box.x += sw;
38855         }
38856         if(this.collapsed){
38857             this.updateBody(null, box.height);
38858         }
38859         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38860     }
38861 });
38862
38863 Roo.bootstrap.layout.West = function(config){
38864     config.region = "west";
38865     config.cursor = "w-resize";
38866     
38867     Roo.bootstrap.layout.Split.call(this, config);
38868     if(this.split){
38869         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
38870         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
38871         this.split.el.addClass("roo-layout-split-h");
38872     }
38873     
38874 };
38875 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
38876     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
38877     
38878     onRender: function(ctr, pos)
38879     {
38880         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
38881         var size = this.config.initialSize || this.config.width;
38882         if(typeof size != "undefined"){
38883             this.el.setWidth(size);
38884         }
38885     },
38886     
38887     getBox : function(){
38888         if(this.collapsed){
38889             return this.collapsedEl.getBox();
38890         }
38891         var box = this.el.getBox();
38892         if(this.split){
38893             box.width += this.split.el.getWidth();
38894         }
38895         return box;
38896     },
38897     
38898     updateBox : function(box){
38899         if(this.split && !this.collapsed){
38900             var sw = this.split.el.getWidth();
38901             box.width -= sw;
38902             this.split.el.setLeft(box.x+box.width);
38903             this.split.el.setTop(box.y);
38904             this.split.el.setHeight(box.height);
38905         }
38906         if(this.collapsed){
38907             this.updateBody(null, box.height);
38908         }
38909         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38910     }
38911 });Roo.namespace("Roo.bootstrap.panel");/*
38912  * Based on:
38913  * Ext JS Library 1.1.1
38914  * Copyright(c) 2006-2007, Ext JS, LLC.
38915  *
38916  * Originally Released Under LGPL - original licence link has changed is not relivant.
38917  *
38918  * Fork - LGPL
38919  * <script type="text/javascript">
38920  */
38921 /**
38922  * @class Roo.ContentPanel
38923  * @extends Roo.util.Observable
38924  * A basic ContentPanel element.
38925  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
38926  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
38927  * @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
38928  * @cfg {Boolean}   closable      True if the panel can be closed/removed
38929  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
38930  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
38931  * @cfg {Toolbar}   toolbar       A toolbar for this panel
38932  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
38933  * @cfg {String} title          The title for this panel
38934  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
38935  * @cfg {String} url            Calls {@link #setUrl} with this value
38936  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
38937  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
38938  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
38939  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
38940  * @cfg {Boolean} badges render the badges
38941
38942  * @constructor
38943  * Create a new ContentPanel.
38944  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
38945  * @param {String/Object} config A string to set only the title or a config object
38946  * @param {String} content (optional) Set the HTML content for this panel
38947  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
38948  */
38949 Roo.bootstrap.panel.Content = function( config){
38950     
38951     this.tpl = config.tpl || false;
38952     
38953     var el = config.el;
38954     var content = config.content;
38955
38956     if(config.autoCreate){ // xtype is available if this is called from factory
38957         el = Roo.id();
38958     }
38959     this.el = Roo.get(el);
38960     if(!this.el && config && config.autoCreate){
38961         if(typeof config.autoCreate == "object"){
38962             if(!config.autoCreate.id){
38963                 config.autoCreate.id = config.id||el;
38964             }
38965             this.el = Roo.DomHelper.append(document.body,
38966                         config.autoCreate, true);
38967         }else{
38968             var elcfg =  {   tag: "div",
38969                             cls: "roo-layout-inactive-content",
38970                             id: config.id||el
38971                             };
38972             if (config.html) {
38973                 elcfg.html = config.html;
38974                 
38975             }
38976                         
38977             this.el = Roo.DomHelper.append(document.body, elcfg , true);
38978         }
38979     } 
38980     this.closable = false;
38981     this.loaded = false;
38982     this.active = false;
38983    
38984       
38985     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
38986         
38987         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
38988         
38989         this.wrapEl = this.el; //this.el.wrap();
38990         var ti = [];
38991         if (config.toolbar.items) {
38992             ti = config.toolbar.items ;
38993             delete config.toolbar.items ;
38994         }
38995         
38996         var nitems = [];
38997         this.toolbar.render(this.wrapEl, 'before');
38998         for(var i =0;i < ti.length;i++) {
38999           //  Roo.log(['add child', items[i]]);
39000             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39001         }
39002         this.toolbar.items = nitems;
39003         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39004         delete config.toolbar;
39005         
39006     }
39007     /*
39008     // xtype created footer. - not sure if will work as we normally have to render first..
39009     if (this.footer && !this.footer.el && this.footer.xtype) {
39010         if (!this.wrapEl) {
39011             this.wrapEl = this.el.wrap();
39012         }
39013     
39014         this.footer.container = this.wrapEl.createChild();
39015          
39016         this.footer = Roo.factory(this.footer, Roo);
39017         
39018     }
39019     */
39020     
39021      if(typeof config == "string"){
39022         this.title = config;
39023     }else{
39024         Roo.apply(this, config);
39025     }
39026     
39027     if(this.resizeEl){
39028         this.resizeEl = Roo.get(this.resizeEl, true);
39029     }else{
39030         this.resizeEl = this.el;
39031     }
39032     // handle view.xtype
39033     
39034  
39035     
39036     
39037     this.addEvents({
39038         /**
39039          * @event activate
39040          * Fires when this panel is activated. 
39041          * @param {Roo.ContentPanel} this
39042          */
39043         "activate" : true,
39044         /**
39045          * @event deactivate
39046          * Fires when this panel is activated. 
39047          * @param {Roo.ContentPanel} this
39048          */
39049         "deactivate" : true,
39050
39051         /**
39052          * @event resize
39053          * Fires when this panel is resized if fitToFrame is true.
39054          * @param {Roo.ContentPanel} this
39055          * @param {Number} width The width after any component adjustments
39056          * @param {Number} height The height after any component adjustments
39057          */
39058         "resize" : true,
39059         
39060          /**
39061          * @event render
39062          * Fires when this tab is created
39063          * @param {Roo.ContentPanel} this
39064          */
39065         "render" : true
39066         
39067         
39068         
39069     });
39070     
39071
39072     
39073     
39074     if(this.autoScroll){
39075         this.resizeEl.setStyle("overflow", "auto");
39076     } else {
39077         // fix randome scrolling
39078         //this.el.on('scroll', function() {
39079         //    Roo.log('fix random scolling');
39080         //    this.scrollTo('top',0); 
39081         //});
39082     }
39083     content = content || this.content;
39084     if(content){
39085         this.setContent(content);
39086     }
39087     if(config && config.url){
39088         this.setUrl(this.url, this.params, this.loadOnce);
39089     }
39090     
39091     
39092     
39093     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39094     
39095     if (this.view && typeof(this.view.xtype) != 'undefined') {
39096         this.view.el = this.el.appendChild(document.createElement("div"));
39097         this.view = Roo.factory(this.view); 
39098         this.view.render  &&  this.view.render(false, '');  
39099     }
39100     
39101     
39102     this.fireEvent('render', this);
39103 };
39104
39105 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39106     
39107     tabTip : '',
39108     
39109     setRegion : function(region){
39110         this.region = region;
39111         this.setActiveClass(region && !this.background);
39112     },
39113     
39114     
39115     setActiveClass: function(state)
39116     {
39117         if(state){
39118            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39119            this.el.setStyle('position','relative');
39120         }else{
39121            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39122            this.el.setStyle('position', 'absolute');
39123         } 
39124     },
39125     
39126     /**
39127      * Returns the toolbar for this Panel if one was configured. 
39128      * @return {Roo.Toolbar} 
39129      */
39130     getToolbar : function(){
39131         return this.toolbar;
39132     },
39133     
39134     setActiveState : function(active)
39135     {
39136         this.active = active;
39137         this.setActiveClass(active);
39138         if(!active){
39139             if(this.fireEvent("deactivate", this) === false){
39140                 return false;
39141             }
39142             return true;
39143         }
39144         this.fireEvent("activate", this);
39145         return true;
39146     },
39147     /**
39148      * Updates this panel's element
39149      * @param {String} content The new content
39150      * @param {Boolean} loadScripts (optional) true to look for and process scripts
39151     */
39152     setContent : function(content, loadScripts){
39153         this.el.update(content, loadScripts);
39154     },
39155
39156     ignoreResize : function(w, h){
39157         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39158             return true;
39159         }else{
39160             this.lastSize = {width: w, height: h};
39161             return false;
39162         }
39163     },
39164     /**
39165      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39166      * @return {Roo.UpdateManager} The UpdateManager
39167      */
39168     getUpdateManager : function(){
39169         return this.el.getUpdateManager();
39170     },
39171      /**
39172      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39173      * @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:
39174 <pre><code>
39175 panel.load({
39176     url: "your-url.php",
39177     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39178     callback: yourFunction,
39179     scope: yourObject, //(optional scope)
39180     discardUrl: false,
39181     nocache: false,
39182     text: "Loading...",
39183     timeout: 30,
39184     scripts: false
39185 });
39186 </code></pre>
39187      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39188      * 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.
39189      * @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}
39190      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39191      * @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.
39192      * @return {Roo.ContentPanel} this
39193      */
39194     load : function(){
39195         var um = this.el.getUpdateManager();
39196         um.update.apply(um, arguments);
39197         return this;
39198     },
39199
39200
39201     /**
39202      * 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.
39203      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39204      * @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)
39205      * @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)
39206      * @return {Roo.UpdateManager} The UpdateManager
39207      */
39208     setUrl : function(url, params, loadOnce){
39209         if(this.refreshDelegate){
39210             this.removeListener("activate", this.refreshDelegate);
39211         }
39212         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39213         this.on("activate", this.refreshDelegate);
39214         return this.el.getUpdateManager();
39215     },
39216     
39217     _handleRefresh : function(url, params, loadOnce){
39218         if(!loadOnce || !this.loaded){
39219             var updater = this.el.getUpdateManager();
39220             updater.update(url, params, this._setLoaded.createDelegate(this));
39221         }
39222     },
39223     
39224     _setLoaded : function(){
39225         this.loaded = true;
39226     }, 
39227     
39228     /**
39229      * Returns this panel's id
39230      * @return {String} 
39231      */
39232     getId : function(){
39233         return this.el.id;
39234     },
39235     
39236     /** 
39237      * Returns this panel's element - used by regiosn to add.
39238      * @return {Roo.Element} 
39239      */
39240     getEl : function(){
39241         return this.wrapEl || this.el;
39242     },
39243     
39244    
39245     
39246     adjustForComponents : function(width, height)
39247     {
39248         //Roo.log('adjustForComponents ');
39249         if(this.resizeEl != this.el){
39250             width -= this.el.getFrameWidth('lr');
39251             height -= this.el.getFrameWidth('tb');
39252         }
39253         if(this.toolbar){
39254             var te = this.toolbar.getEl();
39255             te.setWidth(width);
39256             height -= te.getHeight();
39257         }
39258         if(this.footer){
39259             var te = this.footer.getEl();
39260             te.setWidth(width);
39261             height -= te.getHeight();
39262         }
39263         
39264         
39265         if(this.adjustments){
39266             width += this.adjustments[0];
39267             height += this.adjustments[1];
39268         }
39269         return {"width": width, "height": height};
39270     },
39271     
39272     setSize : function(width, height){
39273         if(this.fitToFrame && !this.ignoreResize(width, height)){
39274             if(this.fitContainer && this.resizeEl != this.el){
39275                 this.el.setSize(width, height);
39276             }
39277             var size = this.adjustForComponents(width, height);
39278             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39279             this.fireEvent('resize', this, size.width, size.height);
39280         }
39281     },
39282     
39283     /**
39284      * Returns this panel's title
39285      * @return {String} 
39286      */
39287     getTitle : function(){
39288         
39289         if (typeof(this.title) != 'object') {
39290             return this.title;
39291         }
39292         
39293         var t = '';
39294         for (var k in this.title) {
39295             if (!this.title.hasOwnProperty(k)) {
39296                 continue;
39297             }
39298             
39299             if (k.indexOf('-') >= 0) {
39300                 var s = k.split('-');
39301                 for (var i = 0; i<s.length; i++) {
39302                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
39303                 }
39304             } else {
39305                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
39306             }
39307         }
39308         return t;
39309     },
39310     
39311     /**
39312      * Set this panel's title
39313      * @param {String} title
39314      */
39315     setTitle : function(title){
39316         this.title = title;
39317         if(this.region){
39318             this.region.updatePanelTitle(this, title);
39319         }
39320     },
39321     
39322     /**
39323      * Returns true is this panel was configured to be closable
39324      * @return {Boolean} 
39325      */
39326     isClosable : function(){
39327         return this.closable;
39328     },
39329     
39330     beforeSlide : function(){
39331         this.el.clip();
39332         this.resizeEl.clip();
39333     },
39334     
39335     afterSlide : function(){
39336         this.el.unclip();
39337         this.resizeEl.unclip();
39338     },
39339     
39340     /**
39341      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
39342      *   Will fail silently if the {@link #setUrl} method has not been called.
39343      *   This does not activate the panel, just updates its content.
39344      */
39345     refresh : function(){
39346         if(this.refreshDelegate){
39347            this.loaded = false;
39348            this.refreshDelegate();
39349         }
39350     },
39351     
39352     /**
39353      * Destroys this panel
39354      */
39355     destroy : function(){
39356         this.el.removeAllListeners();
39357         var tempEl = document.createElement("span");
39358         tempEl.appendChild(this.el.dom);
39359         tempEl.innerHTML = "";
39360         this.el.remove();
39361         this.el = null;
39362     },
39363     
39364     /**
39365      * form - if the content panel contains a form - this is a reference to it.
39366      * @type {Roo.form.Form}
39367      */
39368     form : false,
39369     /**
39370      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
39371      *    This contains a reference to it.
39372      * @type {Roo.View}
39373      */
39374     view : false,
39375     
39376       /**
39377      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
39378      * <pre><code>
39379
39380 layout.addxtype({
39381        xtype : 'Form',
39382        items: [ .... ]
39383    }
39384 );
39385
39386 </code></pre>
39387      * @param {Object} cfg Xtype definition of item to add.
39388      */
39389     
39390     
39391     getChildContainer: function () {
39392         return this.getEl();
39393     }
39394     
39395     
39396     /*
39397         var  ret = new Roo.factory(cfg);
39398         return ret;
39399         
39400         
39401         // add form..
39402         if (cfg.xtype.match(/^Form$/)) {
39403             
39404             var el;
39405             //if (this.footer) {
39406             //    el = this.footer.container.insertSibling(false, 'before');
39407             //} else {
39408                 el = this.el.createChild();
39409             //}
39410
39411             this.form = new  Roo.form.Form(cfg);
39412             
39413             
39414             if ( this.form.allItems.length) {
39415                 this.form.render(el.dom);
39416             }
39417             return this.form;
39418         }
39419         // should only have one of theses..
39420         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
39421             // views.. should not be just added - used named prop 'view''
39422             
39423             cfg.el = this.el.appendChild(document.createElement("div"));
39424             // factory?
39425             
39426             var ret = new Roo.factory(cfg);
39427              
39428              ret.render && ret.render(false, ''); // render blank..
39429             this.view = ret;
39430             return ret;
39431         }
39432         return false;
39433     }
39434     \*/
39435 });
39436  
39437 /**
39438  * @class Roo.bootstrap.panel.Grid
39439  * @extends Roo.bootstrap.panel.Content
39440  * @constructor
39441  * Create a new GridPanel.
39442  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
39443  * @param {Object} config A the config object
39444   
39445  */
39446
39447
39448
39449 Roo.bootstrap.panel.Grid = function(config)
39450 {
39451     
39452       
39453     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
39454         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
39455
39456     config.el = this.wrapper;
39457     //this.el = this.wrapper;
39458     
39459       if (config.container) {
39460         // ctor'ed from a Border/panel.grid
39461         
39462         
39463         this.wrapper.setStyle("overflow", "hidden");
39464         this.wrapper.addClass('roo-grid-container');
39465
39466     }
39467     
39468     
39469     if(config.toolbar){
39470         var tool_el = this.wrapper.createChild();    
39471         this.toolbar = Roo.factory(config.toolbar);
39472         var ti = [];
39473         if (config.toolbar.items) {
39474             ti = config.toolbar.items ;
39475             delete config.toolbar.items ;
39476         }
39477         
39478         var nitems = [];
39479         this.toolbar.render(tool_el);
39480         for(var i =0;i < ti.length;i++) {
39481           //  Roo.log(['add child', items[i]]);
39482             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39483         }
39484         this.toolbar.items = nitems;
39485         
39486         delete config.toolbar;
39487     }
39488     
39489     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
39490     config.grid.scrollBody = true;;
39491     config.grid.monitorWindowResize = false; // turn off autosizing
39492     config.grid.autoHeight = false;
39493     config.grid.autoWidth = false;
39494     
39495     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
39496     
39497     if (config.background) {
39498         // render grid on panel activation (if panel background)
39499         this.on('activate', function(gp) {
39500             if (!gp.grid.rendered) {
39501                 gp.grid.render(this.wrapper);
39502                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
39503             }
39504         });
39505             
39506     } else {
39507         this.grid.render(this.wrapper);
39508         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
39509
39510     }
39511     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
39512     // ??? needed ??? config.el = this.wrapper;
39513     
39514     
39515     
39516   
39517     // xtype created footer. - not sure if will work as we normally have to render first..
39518     if (this.footer && !this.footer.el && this.footer.xtype) {
39519         
39520         var ctr = this.grid.getView().getFooterPanel(true);
39521         this.footer.dataSource = this.grid.dataSource;
39522         this.footer = Roo.factory(this.footer, Roo);
39523         this.footer.render(ctr);
39524         
39525     }
39526     
39527     
39528     
39529     
39530      
39531 };
39532
39533 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
39534     getId : function(){
39535         return this.grid.id;
39536     },
39537     
39538     /**
39539      * Returns the grid for this panel
39540      * @return {Roo.bootstrap.Table} 
39541      */
39542     getGrid : function(){
39543         return this.grid;    
39544     },
39545     
39546     setSize : function(width, height){
39547         if(!this.ignoreResize(width, height)){
39548             var grid = this.grid;
39549             var size = this.adjustForComponents(width, height);
39550             var gridel = grid.getGridEl();
39551             gridel.setSize(size.width, size.height);
39552             /*
39553             var thd = grid.getGridEl().select('thead',true).first();
39554             var tbd = grid.getGridEl().select('tbody', true).first();
39555             if (tbd) {
39556                 tbd.setSize(width, height - thd.getHeight());
39557             }
39558             */
39559             grid.autoSize();
39560         }
39561     },
39562      
39563     
39564     
39565     beforeSlide : function(){
39566         this.grid.getView().scroller.clip();
39567     },
39568     
39569     afterSlide : function(){
39570         this.grid.getView().scroller.unclip();
39571     },
39572     
39573     destroy : function(){
39574         this.grid.destroy();
39575         delete this.grid;
39576         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
39577     }
39578 });
39579
39580 /**
39581  * @class Roo.bootstrap.panel.Nest
39582  * @extends Roo.bootstrap.panel.Content
39583  * @constructor
39584  * Create a new Panel, that can contain a layout.Border.
39585  * 
39586  * 
39587  * @param {Roo.BorderLayout} layout The layout for this panel
39588  * @param {String/Object} config A string to set only the title or a config object
39589  */
39590 Roo.bootstrap.panel.Nest = function(config)
39591 {
39592     // construct with only one argument..
39593     /* FIXME - implement nicer consturctors
39594     if (layout.layout) {
39595         config = layout;
39596         layout = config.layout;
39597         delete config.layout;
39598     }
39599     if (layout.xtype && !layout.getEl) {
39600         // then layout needs constructing..
39601         layout = Roo.factory(layout, Roo);
39602     }
39603     */
39604     
39605     config.el =  config.layout.getEl();
39606     
39607     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
39608     
39609     config.layout.monitorWindowResize = false; // turn off autosizing
39610     this.layout = config.layout;
39611     this.layout.getEl().addClass("roo-layout-nested-layout");
39612     this.layout.parent = this;
39613     
39614     
39615     
39616     
39617 };
39618
39619 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
39620
39621     setSize : function(width, height){
39622         if(!this.ignoreResize(width, height)){
39623             var size = this.adjustForComponents(width, height);
39624             var el = this.layout.getEl();
39625             if (size.height < 1) {
39626                 el.setWidth(size.width);   
39627             } else {
39628                 el.setSize(size.width, size.height);
39629             }
39630             var touch = el.dom.offsetWidth;
39631             this.layout.layout();
39632             // ie requires a double layout on the first pass
39633             if(Roo.isIE && !this.initialized){
39634                 this.initialized = true;
39635                 this.layout.layout();
39636             }
39637         }
39638     },
39639     
39640     // activate all subpanels if not currently active..
39641     
39642     setActiveState : function(active){
39643         this.active = active;
39644         this.setActiveClass(active);
39645         
39646         if(!active){
39647             this.fireEvent("deactivate", this);
39648             return;
39649         }
39650         
39651         this.fireEvent("activate", this);
39652         // not sure if this should happen before or after..
39653         if (!this.layout) {
39654             return; // should not happen..
39655         }
39656         var reg = false;
39657         for (var r in this.layout.regions) {
39658             reg = this.layout.getRegion(r);
39659             if (reg.getActivePanel()) {
39660                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
39661                 reg.setActivePanel(reg.getActivePanel());
39662                 continue;
39663             }
39664             if (!reg.panels.length) {
39665                 continue;
39666             }
39667             reg.showPanel(reg.getPanel(0));
39668         }
39669         
39670         
39671         
39672         
39673     },
39674     
39675     /**
39676      * Returns the nested BorderLayout for this panel
39677      * @return {Roo.BorderLayout} 
39678      */
39679     getLayout : function(){
39680         return this.layout;
39681     },
39682     
39683      /**
39684      * Adds a xtype elements to the layout of the nested panel
39685      * <pre><code>
39686
39687 panel.addxtype({
39688        xtype : 'ContentPanel',
39689        region: 'west',
39690        items: [ .... ]
39691    }
39692 );
39693
39694 panel.addxtype({
39695         xtype : 'NestedLayoutPanel',
39696         region: 'west',
39697         layout: {
39698            center: { },
39699            west: { }   
39700         },
39701         items : [ ... list of content panels or nested layout panels.. ]
39702    }
39703 );
39704 </code></pre>
39705      * @param {Object} cfg Xtype definition of item to add.
39706      */
39707     addxtype : function(cfg) {
39708         return this.layout.addxtype(cfg);
39709     
39710     }
39711 });/*
39712  * Based on:
39713  * Ext JS Library 1.1.1
39714  * Copyright(c) 2006-2007, Ext JS, LLC.
39715  *
39716  * Originally Released Under LGPL - original licence link has changed is not relivant.
39717  *
39718  * Fork - LGPL
39719  * <script type="text/javascript">
39720  */
39721 /**
39722  * @class Roo.TabPanel
39723  * @extends Roo.util.Observable
39724  * A lightweight tab container.
39725  * <br><br>
39726  * Usage:
39727  * <pre><code>
39728 // basic tabs 1, built from existing content
39729 var tabs = new Roo.TabPanel("tabs1");
39730 tabs.addTab("script", "View Script");
39731 tabs.addTab("markup", "View Markup");
39732 tabs.activate("script");
39733
39734 // more advanced tabs, built from javascript
39735 var jtabs = new Roo.TabPanel("jtabs");
39736 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
39737
39738 // set up the UpdateManager
39739 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
39740 var updater = tab2.getUpdateManager();
39741 updater.setDefaultUrl("ajax1.htm");
39742 tab2.on('activate', updater.refresh, updater, true);
39743
39744 // Use setUrl for Ajax loading
39745 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
39746 tab3.setUrl("ajax2.htm", null, true);
39747
39748 // Disabled tab
39749 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
39750 tab4.disable();
39751
39752 jtabs.activate("jtabs-1");
39753  * </code></pre>
39754  * @constructor
39755  * Create a new TabPanel.
39756  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
39757  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
39758  */
39759 Roo.bootstrap.panel.Tabs = function(config){
39760     /**
39761     * The container element for this TabPanel.
39762     * @type Roo.Element
39763     */
39764     this.el = Roo.get(config.el);
39765     delete config.el;
39766     if(config){
39767         if(typeof config == "boolean"){
39768             this.tabPosition = config ? "bottom" : "top";
39769         }else{
39770             Roo.apply(this, config);
39771         }
39772     }
39773     
39774     if(this.tabPosition == "bottom"){
39775         // if tabs are at the bottom = create the body first.
39776         this.bodyEl = Roo.get(this.createBody(this.el.dom));
39777         this.el.addClass("roo-tabs-bottom");
39778     }
39779     // next create the tabs holders
39780     
39781     if (this.tabPosition == "west"){
39782         
39783         var reg = this.region; // fake it..
39784         while (reg) {
39785             if (!reg.mgr.parent) {
39786                 break;
39787             }
39788             reg = reg.mgr.parent.region;
39789         }
39790         Roo.log("got nest?");
39791         Roo.log(reg);
39792         if (reg.mgr.getRegion('west')) {
39793             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
39794             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
39795             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
39796             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
39797             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
39798         
39799             
39800         }
39801         
39802         
39803     } else {
39804      
39805         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
39806         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
39807         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
39808         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
39809     }
39810     
39811     
39812     if(Roo.isIE){
39813         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
39814     }
39815     
39816     // finally - if tabs are at the top, then create the body last..
39817     if(this.tabPosition != "bottom"){
39818         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
39819          * @type Roo.Element
39820          */
39821         this.bodyEl = Roo.get(this.createBody(this.el.dom));
39822         this.el.addClass("roo-tabs-top");
39823     }
39824     this.items = [];
39825
39826     this.bodyEl.setStyle("position", "relative");
39827
39828     this.active = null;
39829     this.activateDelegate = this.activate.createDelegate(this);
39830
39831     this.addEvents({
39832         /**
39833          * @event tabchange
39834          * Fires when the active tab changes
39835          * @param {Roo.TabPanel} this
39836          * @param {Roo.TabPanelItem} activePanel The new active tab
39837          */
39838         "tabchange": true,
39839         /**
39840          * @event beforetabchange
39841          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
39842          * @param {Roo.TabPanel} this
39843          * @param {Object} e Set cancel to true on this object to cancel the tab change
39844          * @param {Roo.TabPanelItem} tab The tab being changed to
39845          */
39846         "beforetabchange" : true
39847     });
39848
39849     Roo.EventManager.onWindowResize(this.onResize, this);
39850     this.cpad = this.el.getPadding("lr");
39851     this.hiddenCount = 0;
39852
39853
39854     // toolbar on the tabbar support...
39855     if (this.toolbar) {
39856         alert("no toolbar support yet");
39857         this.toolbar  = false;
39858         /*
39859         var tcfg = this.toolbar;
39860         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
39861         this.toolbar = new Roo.Toolbar(tcfg);
39862         if (Roo.isSafari) {
39863             var tbl = tcfg.container.child('table', true);
39864             tbl.setAttribute('width', '100%');
39865         }
39866         */
39867         
39868     }
39869    
39870
39871
39872     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
39873 };
39874
39875 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
39876     /*
39877      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
39878      */
39879     tabPosition : "top",
39880     /*
39881      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
39882      */
39883     currentTabWidth : 0,
39884     /*
39885      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
39886      */
39887     minTabWidth : 40,
39888     /*
39889      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
39890      */
39891     maxTabWidth : 250,
39892     /*
39893      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
39894      */
39895     preferredTabWidth : 175,
39896     /*
39897      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
39898      */
39899     resizeTabs : false,
39900     /*
39901      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
39902      */
39903     monitorResize : true,
39904     /*
39905      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
39906      */
39907     toolbar : false,  // set by caller..
39908     
39909     region : false, /// set by caller
39910     
39911     disableTooltips : true, // not used yet...
39912
39913     /**
39914      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
39915      * @param {String} id The id of the div to use <b>or create</b>
39916      * @param {String} text The text for the tab
39917      * @param {String} content (optional) Content to put in the TabPanelItem body
39918      * @param {Boolean} closable (optional) True to create a close icon on the tab
39919      * @return {Roo.TabPanelItem} The created TabPanelItem
39920      */
39921     addTab : function(id, text, content, closable, tpl)
39922     {
39923         var item = new Roo.bootstrap.panel.TabItem({
39924             panel: this,
39925             id : id,
39926             text : text,
39927             closable : closable,
39928             tpl : tpl
39929         });
39930         this.addTabItem(item);
39931         if(content){
39932             item.setContent(content);
39933         }
39934         return item;
39935     },
39936
39937     /**
39938      * Returns the {@link Roo.TabPanelItem} with the specified id/index
39939      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
39940      * @return {Roo.TabPanelItem}
39941      */
39942     getTab : function(id){
39943         return this.items[id];
39944     },
39945
39946     /**
39947      * Hides the {@link Roo.TabPanelItem} with the specified id/index
39948      * @param {String/Number} id The id or index of the TabPanelItem to hide.
39949      */
39950     hideTab : function(id){
39951         var t = this.items[id];
39952         if(!t.isHidden()){
39953            t.setHidden(true);
39954            this.hiddenCount++;
39955            this.autoSizeTabs();
39956         }
39957     },
39958
39959     /**
39960      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
39961      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
39962      */
39963     unhideTab : function(id){
39964         var t = this.items[id];
39965         if(t.isHidden()){
39966            t.setHidden(false);
39967            this.hiddenCount--;
39968            this.autoSizeTabs();
39969         }
39970     },
39971
39972     /**
39973      * Adds an existing {@link Roo.TabPanelItem}.
39974      * @param {Roo.TabPanelItem} item The TabPanelItem to add
39975      */
39976     addTabItem : function(item)
39977     {
39978         this.items[item.id] = item;
39979         this.items.push(item);
39980         this.autoSizeTabs();
39981       //  if(this.resizeTabs){
39982     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
39983   //         this.autoSizeTabs();
39984 //        }else{
39985 //            item.autoSize();
39986        // }
39987     },
39988
39989     /**
39990      * Removes a {@link Roo.TabPanelItem}.
39991      * @param {String/Number} id The id or index of the TabPanelItem to remove.
39992      */
39993     removeTab : function(id){
39994         var items = this.items;
39995         var tab = items[id];
39996         if(!tab) { return; }
39997         var index = items.indexOf(tab);
39998         if(this.active == tab && items.length > 1){
39999             var newTab = this.getNextAvailable(index);
40000             if(newTab) {
40001                 newTab.activate();
40002             }
40003         }
40004         this.stripEl.dom.removeChild(tab.pnode.dom);
40005         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40006             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40007         }
40008         items.splice(index, 1);
40009         delete this.items[tab.id];
40010         tab.fireEvent("close", tab);
40011         tab.purgeListeners();
40012         this.autoSizeTabs();
40013     },
40014
40015     getNextAvailable : function(start){
40016         var items = this.items;
40017         var index = start;
40018         // look for a next tab that will slide over to
40019         // replace the one being removed
40020         while(index < items.length){
40021             var item = items[++index];
40022             if(item && !item.isHidden()){
40023                 return item;
40024             }
40025         }
40026         // if one isn't found select the previous tab (on the left)
40027         index = start;
40028         while(index >= 0){
40029             var item = items[--index];
40030             if(item && !item.isHidden()){
40031                 return item;
40032             }
40033         }
40034         return null;
40035     },
40036
40037     /**
40038      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40039      * @param {String/Number} id The id or index of the TabPanelItem to disable.
40040      */
40041     disableTab : function(id){
40042         var tab = this.items[id];
40043         if(tab && this.active != tab){
40044             tab.disable();
40045         }
40046     },
40047
40048     /**
40049      * Enables a {@link Roo.TabPanelItem} that is disabled.
40050      * @param {String/Number} id The id or index of the TabPanelItem to enable.
40051      */
40052     enableTab : function(id){
40053         var tab = this.items[id];
40054         tab.enable();
40055     },
40056
40057     /**
40058      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40059      * @param {String/Number} id The id or index of the TabPanelItem to activate.
40060      * @return {Roo.TabPanelItem} The TabPanelItem.
40061      */
40062     activate : function(id)
40063     {
40064         //Roo.log('activite:'  + id);
40065         
40066         var tab = this.items[id];
40067         if(!tab){
40068             return null;
40069         }
40070         if(tab == this.active || tab.disabled){
40071             return tab;
40072         }
40073         var e = {};
40074         this.fireEvent("beforetabchange", this, e, tab);
40075         if(e.cancel !== true && !tab.disabled){
40076             if(this.active){
40077                 this.active.hide();
40078             }
40079             this.active = this.items[id];
40080             this.active.show();
40081             this.fireEvent("tabchange", this, this.active);
40082         }
40083         return tab;
40084     },
40085
40086     /**
40087      * Gets the active {@link Roo.TabPanelItem}.
40088      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40089      */
40090     getActiveTab : function(){
40091         return this.active;
40092     },
40093
40094     /**
40095      * Updates the tab body element to fit the height of the container element
40096      * for overflow scrolling
40097      * @param {Number} targetHeight (optional) Override the starting height from the elements height
40098      */
40099     syncHeight : function(targetHeight){
40100         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40101         var bm = this.bodyEl.getMargins();
40102         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40103         this.bodyEl.setHeight(newHeight);
40104         return newHeight;
40105     },
40106
40107     onResize : function(){
40108         if(this.monitorResize){
40109             this.autoSizeTabs();
40110         }
40111     },
40112
40113     /**
40114      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40115      */
40116     beginUpdate : function(){
40117         this.updating = true;
40118     },
40119
40120     /**
40121      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40122      */
40123     endUpdate : function(){
40124         this.updating = false;
40125         this.autoSizeTabs();
40126     },
40127
40128     /**
40129      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40130      */
40131     autoSizeTabs : function()
40132     {
40133         var count = this.items.length;
40134         var vcount = count - this.hiddenCount;
40135         
40136         if (vcount < 2) {
40137             this.stripEl.hide();
40138         } else {
40139             this.stripEl.show();
40140         }
40141         
40142         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40143             return;
40144         }
40145         
40146         
40147         var w = Math.max(this.el.getWidth() - this.cpad, 10);
40148         var availWidth = Math.floor(w / vcount);
40149         var b = this.stripBody;
40150         if(b.getWidth() > w){
40151             var tabs = this.items;
40152             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40153             if(availWidth < this.minTabWidth){
40154                 /*if(!this.sleft){    // incomplete scrolling code
40155                     this.createScrollButtons();
40156                 }
40157                 this.showScroll();
40158                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40159             }
40160         }else{
40161             if(this.currentTabWidth < this.preferredTabWidth){
40162                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40163             }
40164         }
40165     },
40166
40167     /**
40168      * Returns the number of tabs in this TabPanel.
40169      * @return {Number}
40170      */
40171      getCount : function(){
40172          return this.items.length;
40173      },
40174
40175     /**
40176      * Resizes all the tabs to the passed width
40177      * @param {Number} The new width
40178      */
40179     setTabWidth : function(width){
40180         this.currentTabWidth = width;
40181         for(var i = 0, len = this.items.length; i < len; i++) {
40182                 if(!this.items[i].isHidden()) {
40183                 this.items[i].setWidth(width);
40184             }
40185         }
40186     },
40187
40188     /**
40189      * Destroys this TabPanel
40190      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40191      */
40192     destroy : function(removeEl){
40193         Roo.EventManager.removeResizeListener(this.onResize, this);
40194         for(var i = 0, len = this.items.length; i < len; i++){
40195             this.items[i].purgeListeners();
40196         }
40197         if(removeEl === true){
40198             this.el.update("");
40199             this.el.remove();
40200         }
40201     },
40202     
40203     createStrip : function(container)
40204     {
40205         var strip = document.createElement("nav");
40206         strip.className = Roo.bootstrap.version == 4 ?
40207             "navbar-light bg-light" : 
40208             "navbar navbar-default"; //"x-tabs-wrap";
40209         container.appendChild(strip);
40210         return strip;
40211     },
40212     
40213     createStripList : function(strip)
40214     {
40215         // div wrapper for retard IE
40216         // returns the "tr" element.
40217         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40218         //'<div class="x-tabs-strip-wrap">'+
40219           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40220           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40221         return strip.firstChild; //.firstChild.firstChild.firstChild;
40222     },
40223     createBody : function(container)
40224     {
40225         var body = document.createElement("div");
40226         Roo.id(body, "tab-body");
40227         //Roo.fly(body).addClass("x-tabs-body");
40228         Roo.fly(body).addClass("tab-content");
40229         container.appendChild(body);
40230         return body;
40231     },
40232     createItemBody :function(bodyEl, id){
40233         var body = Roo.getDom(id);
40234         if(!body){
40235             body = document.createElement("div");
40236             body.id = id;
40237         }
40238         //Roo.fly(body).addClass("x-tabs-item-body");
40239         Roo.fly(body).addClass("tab-pane");
40240          bodyEl.insertBefore(body, bodyEl.firstChild);
40241         return body;
40242     },
40243     /** @private */
40244     createStripElements :  function(stripEl, text, closable, tpl)
40245     {
40246         var td = document.createElement("li"); // was td..
40247         td.className = 'nav-item';
40248         
40249         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40250         
40251         
40252         stripEl.appendChild(td);
40253         /*if(closable){
40254             td.className = "x-tabs-closable";
40255             if(!this.closeTpl){
40256                 this.closeTpl = new Roo.Template(
40257                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40258                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40259                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
40260                 );
40261             }
40262             var el = this.closeTpl.overwrite(td, {"text": text});
40263             var close = el.getElementsByTagName("div")[0];
40264             var inner = el.getElementsByTagName("em")[0];
40265             return {"el": el, "close": close, "inner": inner};
40266         } else {
40267         */
40268         // not sure what this is..
40269 //            if(!this.tabTpl){
40270                 //this.tabTpl = new Roo.Template(
40271                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40272                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40273                 //);
40274 //                this.tabTpl = new Roo.Template(
40275 //                   '<a href="#">' +
40276 //                   '<span unselectable="on"' +
40277 //                            (this.disableTooltips ? '' : ' title="{text}"') +
40278 //                            ' >{text}</span></a>'
40279 //                );
40280 //                
40281 //            }
40282
40283
40284             var template = tpl || this.tabTpl || false;
40285             
40286             if(!template){
40287                 template =  new Roo.Template(
40288                         Roo.bootstrap.version == 4 ? 
40289                             (
40290                                 '<a class="nav-link" href="#" unselectable="on"' +
40291                                      (this.disableTooltips ? '' : ' title="{text}"') +
40292                                      ' >{text}</a>'
40293                             ) : (
40294                                 '<a class="nav-link" href="#">' +
40295                                 '<span unselectable="on"' +
40296                                          (this.disableTooltips ? '' : ' title="{text}"') +
40297                                     ' >{text}</span></a>'
40298                             )
40299                 );
40300             }
40301             
40302             switch (typeof(template)) {
40303                 case 'object' :
40304                     break;
40305                 case 'string' :
40306                     template = new Roo.Template(template);
40307                     break;
40308                 default :
40309                     break;
40310             }
40311             
40312             var el = template.overwrite(td, {"text": text});
40313             
40314             var inner = el.getElementsByTagName("span")[0];
40315             
40316             return {"el": el, "inner": inner};
40317             
40318     }
40319         
40320     
40321 });
40322
40323 /**
40324  * @class Roo.TabPanelItem
40325  * @extends Roo.util.Observable
40326  * Represents an individual item (tab plus body) in a TabPanel.
40327  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
40328  * @param {String} id The id of this TabPanelItem
40329  * @param {String} text The text for the tab of this TabPanelItem
40330  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
40331  */
40332 Roo.bootstrap.panel.TabItem = function(config){
40333     /**
40334      * The {@link Roo.TabPanel} this TabPanelItem belongs to
40335      * @type Roo.TabPanel
40336      */
40337     this.tabPanel = config.panel;
40338     /**
40339      * The id for this TabPanelItem
40340      * @type String
40341      */
40342     this.id = config.id;
40343     /** @private */
40344     this.disabled = false;
40345     /** @private */
40346     this.text = config.text;
40347     /** @private */
40348     this.loaded = false;
40349     this.closable = config.closable;
40350
40351     /**
40352      * The body element for this TabPanelItem.
40353      * @type Roo.Element
40354      */
40355     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
40356     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
40357     this.bodyEl.setStyle("display", "block");
40358     this.bodyEl.setStyle("zoom", "1");
40359     //this.hideAction();
40360
40361     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
40362     /** @private */
40363     this.el = Roo.get(els.el);
40364     this.inner = Roo.get(els.inner, true);
40365      this.textEl = Roo.bootstrap.version == 4 ?
40366         this.el : Roo.get(this.el.dom.firstChild, true);
40367
40368     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
40369     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
40370
40371     
40372 //    this.el.on("mousedown", this.onTabMouseDown, this);
40373     this.el.on("click", this.onTabClick, this);
40374     /** @private */
40375     if(config.closable){
40376         var c = Roo.get(els.close, true);
40377         c.dom.title = this.closeText;
40378         c.addClassOnOver("close-over");
40379         c.on("click", this.closeClick, this);
40380      }
40381
40382     this.addEvents({
40383          /**
40384          * @event activate
40385          * Fires when this tab becomes the active tab.
40386          * @param {Roo.TabPanel} tabPanel The parent TabPanel
40387          * @param {Roo.TabPanelItem} this
40388          */
40389         "activate": true,
40390         /**
40391          * @event beforeclose
40392          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
40393          * @param {Roo.TabPanelItem} this
40394          * @param {Object} e Set cancel to true on this object to cancel the close.
40395          */
40396         "beforeclose": true,
40397         /**
40398          * @event close
40399          * Fires when this tab is closed.
40400          * @param {Roo.TabPanelItem} this
40401          */
40402          "close": true,
40403         /**
40404          * @event deactivate
40405          * Fires when this tab is no longer the active tab.
40406          * @param {Roo.TabPanel} tabPanel The parent TabPanel
40407          * @param {Roo.TabPanelItem} this
40408          */
40409          "deactivate" : true
40410     });
40411     this.hidden = false;
40412
40413     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
40414 };
40415
40416 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
40417            {
40418     purgeListeners : function(){
40419        Roo.util.Observable.prototype.purgeListeners.call(this);
40420        this.el.removeAllListeners();
40421     },
40422     /**
40423      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
40424      */
40425     show : function(){
40426         this.status_node.addClass("active");
40427         this.showAction();
40428         if(Roo.isOpera){
40429             this.tabPanel.stripWrap.repaint();
40430         }
40431         this.fireEvent("activate", this.tabPanel, this);
40432     },
40433
40434     /**
40435      * Returns true if this tab is the active tab.
40436      * @return {Boolean}
40437      */
40438     isActive : function(){
40439         return this.tabPanel.getActiveTab() == this;
40440     },
40441
40442     /**
40443      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
40444      */
40445     hide : function(){
40446         this.status_node.removeClass("active");
40447         this.hideAction();
40448         this.fireEvent("deactivate", this.tabPanel, this);
40449     },
40450
40451     hideAction : function(){
40452         this.bodyEl.hide();
40453         this.bodyEl.setStyle("position", "absolute");
40454         this.bodyEl.setLeft("-20000px");
40455         this.bodyEl.setTop("-20000px");
40456     },
40457
40458     showAction : function(){
40459         this.bodyEl.setStyle("position", "relative");
40460         this.bodyEl.setTop("");
40461         this.bodyEl.setLeft("");
40462         this.bodyEl.show();
40463     },
40464
40465     /**
40466      * Set the tooltip for the tab.
40467      * @param {String} tooltip The tab's tooltip
40468      */
40469     setTooltip : function(text){
40470         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
40471             this.textEl.dom.qtip = text;
40472             this.textEl.dom.removeAttribute('title');
40473         }else{
40474             this.textEl.dom.title = text;
40475         }
40476     },
40477
40478     onTabClick : function(e){
40479         e.preventDefault();
40480         this.tabPanel.activate(this.id);
40481     },
40482
40483     onTabMouseDown : function(e){
40484         e.preventDefault();
40485         this.tabPanel.activate(this.id);
40486     },
40487 /*
40488     getWidth : function(){
40489         return this.inner.getWidth();
40490     },
40491
40492     setWidth : function(width){
40493         var iwidth = width - this.linode.getPadding("lr");
40494         this.inner.setWidth(iwidth);
40495         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
40496         this.linode.setWidth(width);
40497     },
40498 */
40499     /**
40500      * Show or hide the tab
40501      * @param {Boolean} hidden True to hide or false to show.
40502      */
40503     setHidden : function(hidden){
40504         this.hidden = hidden;
40505         this.linode.setStyle("display", hidden ? "none" : "");
40506     },
40507
40508     /**
40509      * Returns true if this tab is "hidden"
40510      * @return {Boolean}
40511      */
40512     isHidden : function(){
40513         return this.hidden;
40514     },
40515
40516     /**
40517      * Returns the text for this tab
40518      * @return {String}
40519      */
40520     getText : function(){
40521         return this.text;
40522     },
40523     /*
40524     autoSize : function(){
40525         //this.el.beginMeasure();
40526         this.textEl.setWidth(1);
40527         /*
40528          *  #2804 [new] Tabs in Roojs
40529          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
40530          */
40531         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
40532         //this.el.endMeasure();
40533     //},
40534
40535     /**
40536      * Sets the text for the tab (Note: this also sets the tooltip text)
40537      * @param {String} text The tab's text and tooltip
40538      */
40539     setText : function(text){
40540         this.text = text;
40541         this.textEl.update(text);
40542         this.setTooltip(text);
40543         //if(!this.tabPanel.resizeTabs){
40544         //    this.autoSize();
40545         //}
40546     },
40547     /**
40548      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
40549      */
40550     activate : function(){
40551         this.tabPanel.activate(this.id);
40552     },
40553
40554     /**
40555      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
40556      */
40557     disable : function(){
40558         if(this.tabPanel.active != this){
40559             this.disabled = true;
40560             this.status_node.addClass("disabled");
40561         }
40562     },
40563
40564     /**
40565      * Enables this TabPanelItem if it was previously disabled.
40566      */
40567     enable : function(){
40568         this.disabled = false;
40569         this.status_node.removeClass("disabled");
40570     },
40571
40572     /**
40573      * Sets the content for this TabPanelItem.
40574      * @param {String} content The content
40575      * @param {Boolean} loadScripts true to look for and load scripts
40576      */
40577     setContent : function(content, loadScripts){
40578         this.bodyEl.update(content, loadScripts);
40579     },
40580
40581     /**
40582      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
40583      * @return {Roo.UpdateManager} The UpdateManager
40584      */
40585     getUpdateManager : function(){
40586         return this.bodyEl.getUpdateManager();
40587     },
40588
40589     /**
40590      * Set a URL to be used to load the content for this TabPanelItem.
40591      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
40592      * @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)
40593      * @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)
40594      * @return {Roo.UpdateManager} The UpdateManager
40595      */
40596     setUrl : function(url, params, loadOnce){
40597         if(this.refreshDelegate){
40598             this.un('activate', this.refreshDelegate);
40599         }
40600         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40601         this.on("activate", this.refreshDelegate);
40602         return this.bodyEl.getUpdateManager();
40603     },
40604
40605     /** @private */
40606     _handleRefresh : function(url, params, loadOnce){
40607         if(!loadOnce || !this.loaded){
40608             var updater = this.bodyEl.getUpdateManager();
40609             updater.update(url, params, this._setLoaded.createDelegate(this));
40610         }
40611     },
40612
40613     /**
40614      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
40615      *   Will fail silently if the setUrl method has not been called.
40616      *   This does not activate the panel, just updates its content.
40617      */
40618     refresh : function(){
40619         if(this.refreshDelegate){
40620            this.loaded = false;
40621            this.refreshDelegate();
40622         }
40623     },
40624
40625     /** @private */
40626     _setLoaded : function(){
40627         this.loaded = true;
40628     },
40629
40630     /** @private */
40631     closeClick : function(e){
40632         var o = {};
40633         e.stopEvent();
40634         this.fireEvent("beforeclose", this, o);
40635         if(o.cancel !== true){
40636             this.tabPanel.removeTab(this.id);
40637         }
40638     },
40639     /**
40640      * The text displayed in the tooltip for the close icon.
40641      * @type String
40642      */
40643     closeText : "Close this tab"
40644 });
40645 /**
40646 *    This script refer to:
40647 *    Title: International Telephone Input
40648 *    Author: Jack O'Connor
40649 *    Code version:  v12.1.12
40650 *    Availability: https://github.com/jackocnr/intl-tel-input.git
40651 **/
40652
40653 Roo.bootstrap.PhoneInputData = function() {
40654     var d = [
40655       [
40656         "Afghanistan (‫افغانستان‬‎)",
40657         "af",
40658         "93"
40659       ],
40660       [
40661         "Albania (Shqipëri)",
40662         "al",
40663         "355"
40664       ],
40665       [
40666         "Algeria (‫الجزائر‬‎)",
40667         "dz",
40668         "213"
40669       ],
40670       [
40671         "American Samoa",
40672         "as",
40673         "1684"
40674       ],
40675       [
40676         "Andorra",
40677         "ad",
40678         "376"
40679       ],
40680       [
40681         "Angola",
40682         "ao",
40683         "244"
40684       ],
40685       [
40686         "Anguilla",
40687         "ai",
40688         "1264"
40689       ],
40690       [
40691         "Antigua and Barbuda",
40692         "ag",
40693         "1268"
40694       ],
40695       [
40696         "Argentina",
40697         "ar",
40698         "54"
40699       ],
40700       [
40701         "Armenia (Հայաստան)",
40702         "am",
40703         "374"
40704       ],
40705       [
40706         "Aruba",
40707         "aw",
40708         "297"
40709       ],
40710       [
40711         "Australia",
40712         "au",
40713         "61",
40714         0
40715       ],
40716       [
40717         "Austria (Österreich)",
40718         "at",
40719         "43"
40720       ],
40721       [
40722         "Azerbaijan (Azərbaycan)",
40723         "az",
40724         "994"
40725       ],
40726       [
40727         "Bahamas",
40728         "bs",
40729         "1242"
40730       ],
40731       [
40732         "Bahrain (‫البحرين‬‎)",
40733         "bh",
40734         "973"
40735       ],
40736       [
40737         "Bangladesh (বাংলাদেশ)",
40738         "bd",
40739         "880"
40740       ],
40741       [
40742         "Barbados",
40743         "bb",
40744         "1246"
40745       ],
40746       [
40747         "Belarus (Беларусь)",
40748         "by",
40749         "375"
40750       ],
40751       [
40752         "Belgium (België)",
40753         "be",
40754         "32"
40755       ],
40756       [
40757         "Belize",
40758         "bz",
40759         "501"
40760       ],
40761       [
40762         "Benin (Bénin)",
40763         "bj",
40764         "229"
40765       ],
40766       [
40767         "Bermuda",
40768         "bm",
40769         "1441"
40770       ],
40771       [
40772         "Bhutan (འབྲུག)",
40773         "bt",
40774         "975"
40775       ],
40776       [
40777         "Bolivia",
40778         "bo",
40779         "591"
40780       ],
40781       [
40782         "Bosnia and Herzegovina (Босна и Херцеговина)",
40783         "ba",
40784         "387"
40785       ],
40786       [
40787         "Botswana",
40788         "bw",
40789         "267"
40790       ],
40791       [
40792         "Brazil (Brasil)",
40793         "br",
40794         "55"
40795       ],
40796       [
40797         "British Indian Ocean Territory",
40798         "io",
40799         "246"
40800       ],
40801       [
40802         "British Virgin Islands",
40803         "vg",
40804         "1284"
40805       ],
40806       [
40807         "Brunei",
40808         "bn",
40809         "673"
40810       ],
40811       [
40812         "Bulgaria (България)",
40813         "bg",
40814         "359"
40815       ],
40816       [
40817         "Burkina Faso",
40818         "bf",
40819         "226"
40820       ],
40821       [
40822         "Burundi (Uburundi)",
40823         "bi",
40824         "257"
40825       ],
40826       [
40827         "Cambodia (កម្ពុជា)",
40828         "kh",
40829         "855"
40830       ],
40831       [
40832         "Cameroon (Cameroun)",
40833         "cm",
40834         "237"
40835       ],
40836       [
40837         "Canada",
40838         "ca",
40839         "1",
40840         1,
40841         ["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"]
40842       ],
40843       [
40844         "Cape Verde (Kabu Verdi)",
40845         "cv",
40846         "238"
40847       ],
40848       [
40849         "Caribbean Netherlands",
40850         "bq",
40851         "599",
40852         1
40853       ],
40854       [
40855         "Cayman Islands",
40856         "ky",
40857         "1345"
40858       ],
40859       [
40860         "Central African Republic (République centrafricaine)",
40861         "cf",
40862         "236"
40863       ],
40864       [
40865         "Chad (Tchad)",
40866         "td",
40867         "235"
40868       ],
40869       [
40870         "Chile",
40871         "cl",
40872         "56"
40873       ],
40874       [
40875         "China (中国)",
40876         "cn",
40877         "86"
40878       ],
40879       [
40880         "Christmas Island",
40881         "cx",
40882         "61",
40883         2
40884       ],
40885       [
40886         "Cocos (Keeling) Islands",
40887         "cc",
40888         "61",
40889         1
40890       ],
40891       [
40892         "Colombia",
40893         "co",
40894         "57"
40895       ],
40896       [
40897         "Comoros (‫جزر القمر‬‎)",
40898         "km",
40899         "269"
40900       ],
40901       [
40902         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
40903         "cd",
40904         "243"
40905       ],
40906       [
40907         "Congo (Republic) (Congo-Brazzaville)",
40908         "cg",
40909         "242"
40910       ],
40911       [
40912         "Cook Islands",
40913         "ck",
40914         "682"
40915       ],
40916       [
40917         "Costa Rica",
40918         "cr",
40919         "506"
40920       ],
40921       [
40922         "Côte d’Ivoire",
40923         "ci",
40924         "225"
40925       ],
40926       [
40927         "Croatia (Hrvatska)",
40928         "hr",
40929         "385"
40930       ],
40931       [
40932         "Cuba",
40933         "cu",
40934         "53"
40935       ],
40936       [
40937         "Curaçao",
40938         "cw",
40939         "599",
40940         0
40941       ],
40942       [
40943         "Cyprus (Κύπρος)",
40944         "cy",
40945         "357"
40946       ],
40947       [
40948         "Czech Republic (Česká republika)",
40949         "cz",
40950         "420"
40951       ],
40952       [
40953         "Denmark (Danmark)",
40954         "dk",
40955         "45"
40956       ],
40957       [
40958         "Djibouti",
40959         "dj",
40960         "253"
40961       ],
40962       [
40963         "Dominica",
40964         "dm",
40965         "1767"
40966       ],
40967       [
40968         "Dominican Republic (República Dominicana)",
40969         "do",
40970         "1",
40971         2,
40972         ["809", "829", "849"]
40973       ],
40974       [
40975         "Ecuador",
40976         "ec",
40977         "593"
40978       ],
40979       [
40980         "Egypt (‫مصر‬‎)",
40981         "eg",
40982         "20"
40983       ],
40984       [
40985         "El Salvador",
40986         "sv",
40987         "503"
40988       ],
40989       [
40990         "Equatorial Guinea (Guinea Ecuatorial)",
40991         "gq",
40992         "240"
40993       ],
40994       [
40995         "Eritrea",
40996         "er",
40997         "291"
40998       ],
40999       [
41000         "Estonia (Eesti)",
41001         "ee",
41002         "372"
41003       ],
41004       [
41005         "Ethiopia",
41006         "et",
41007         "251"
41008       ],
41009       [
41010         "Falkland Islands (Islas Malvinas)",
41011         "fk",
41012         "500"
41013       ],
41014       [
41015         "Faroe Islands (Føroyar)",
41016         "fo",
41017         "298"
41018       ],
41019       [
41020         "Fiji",
41021         "fj",
41022         "679"
41023       ],
41024       [
41025         "Finland (Suomi)",
41026         "fi",
41027         "358",
41028         0
41029       ],
41030       [
41031         "France",
41032         "fr",
41033         "33"
41034       ],
41035       [
41036         "French Guiana (Guyane française)",
41037         "gf",
41038         "594"
41039       ],
41040       [
41041         "French Polynesia (Polynésie française)",
41042         "pf",
41043         "689"
41044       ],
41045       [
41046         "Gabon",
41047         "ga",
41048         "241"
41049       ],
41050       [
41051         "Gambia",
41052         "gm",
41053         "220"
41054       ],
41055       [
41056         "Georgia (საქართველო)",
41057         "ge",
41058         "995"
41059       ],
41060       [
41061         "Germany (Deutschland)",
41062         "de",
41063         "49"
41064       ],
41065       [
41066         "Ghana (Gaana)",
41067         "gh",
41068         "233"
41069       ],
41070       [
41071         "Gibraltar",
41072         "gi",
41073         "350"
41074       ],
41075       [
41076         "Greece (Ελλάδα)",
41077         "gr",
41078         "30"
41079       ],
41080       [
41081         "Greenland (Kalaallit Nunaat)",
41082         "gl",
41083         "299"
41084       ],
41085       [
41086         "Grenada",
41087         "gd",
41088         "1473"
41089       ],
41090       [
41091         "Guadeloupe",
41092         "gp",
41093         "590",
41094         0
41095       ],
41096       [
41097         "Guam",
41098         "gu",
41099         "1671"
41100       ],
41101       [
41102         "Guatemala",
41103         "gt",
41104         "502"
41105       ],
41106       [
41107         "Guernsey",
41108         "gg",
41109         "44",
41110         1
41111       ],
41112       [
41113         "Guinea (Guinée)",
41114         "gn",
41115         "224"
41116       ],
41117       [
41118         "Guinea-Bissau (Guiné Bissau)",
41119         "gw",
41120         "245"
41121       ],
41122       [
41123         "Guyana",
41124         "gy",
41125         "592"
41126       ],
41127       [
41128         "Haiti",
41129         "ht",
41130         "509"
41131       ],
41132       [
41133         "Honduras",
41134         "hn",
41135         "504"
41136       ],
41137       [
41138         "Hong Kong (香港)",
41139         "hk",
41140         "852"
41141       ],
41142       [
41143         "Hungary (Magyarország)",
41144         "hu",
41145         "36"
41146       ],
41147       [
41148         "Iceland (Ísland)",
41149         "is",
41150         "354"
41151       ],
41152       [
41153         "India (भारत)",
41154         "in",
41155         "91"
41156       ],
41157       [
41158         "Indonesia",
41159         "id",
41160         "62"
41161       ],
41162       [
41163         "Iran (‫ایران‬‎)",
41164         "ir",
41165         "98"
41166       ],
41167       [
41168         "Iraq (‫العراق‬‎)",
41169         "iq",
41170         "964"
41171       ],
41172       [
41173         "Ireland",
41174         "ie",
41175         "353"
41176       ],
41177       [
41178         "Isle of Man",
41179         "im",
41180         "44",
41181         2
41182       ],
41183       [
41184         "Israel (‫ישראל‬‎)",
41185         "il",
41186         "972"
41187       ],
41188       [
41189         "Italy (Italia)",
41190         "it",
41191         "39",
41192         0
41193       ],
41194       [
41195         "Jamaica",
41196         "jm",
41197         "1876"
41198       ],
41199       [
41200         "Japan (日本)",
41201         "jp",
41202         "81"
41203       ],
41204       [
41205         "Jersey",
41206         "je",
41207         "44",
41208         3
41209       ],
41210       [
41211         "Jordan (‫الأردن‬‎)",
41212         "jo",
41213         "962"
41214       ],
41215       [
41216         "Kazakhstan (Казахстан)",
41217         "kz",
41218         "7",
41219         1
41220       ],
41221       [
41222         "Kenya",
41223         "ke",
41224         "254"
41225       ],
41226       [
41227         "Kiribati",
41228         "ki",
41229         "686"
41230       ],
41231       [
41232         "Kosovo",
41233         "xk",
41234         "383"
41235       ],
41236       [
41237         "Kuwait (‫الكويت‬‎)",
41238         "kw",
41239         "965"
41240       ],
41241       [
41242         "Kyrgyzstan (Кыргызстан)",
41243         "kg",
41244         "996"
41245       ],
41246       [
41247         "Laos (ລາວ)",
41248         "la",
41249         "856"
41250       ],
41251       [
41252         "Latvia (Latvija)",
41253         "lv",
41254         "371"
41255       ],
41256       [
41257         "Lebanon (‫لبنان‬‎)",
41258         "lb",
41259         "961"
41260       ],
41261       [
41262         "Lesotho",
41263         "ls",
41264         "266"
41265       ],
41266       [
41267         "Liberia",
41268         "lr",
41269         "231"
41270       ],
41271       [
41272         "Libya (‫ليبيا‬‎)",
41273         "ly",
41274         "218"
41275       ],
41276       [
41277         "Liechtenstein",
41278         "li",
41279         "423"
41280       ],
41281       [
41282         "Lithuania (Lietuva)",
41283         "lt",
41284         "370"
41285       ],
41286       [
41287         "Luxembourg",
41288         "lu",
41289         "352"
41290       ],
41291       [
41292         "Macau (澳門)",
41293         "mo",
41294         "853"
41295       ],
41296       [
41297         "Macedonia (FYROM) (Македонија)",
41298         "mk",
41299         "389"
41300       ],
41301       [
41302         "Madagascar (Madagasikara)",
41303         "mg",
41304         "261"
41305       ],
41306       [
41307         "Malawi",
41308         "mw",
41309         "265"
41310       ],
41311       [
41312         "Malaysia",
41313         "my",
41314         "60"
41315       ],
41316       [
41317         "Maldives",
41318         "mv",
41319         "960"
41320       ],
41321       [
41322         "Mali",
41323         "ml",
41324         "223"
41325       ],
41326       [
41327         "Malta",
41328         "mt",
41329         "356"
41330       ],
41331       [
41332         "Marshall Islands",
41333         "mh",
41334         "692"
41335       ],
41336       [
41337         "Martinique",
41338         "mq",
41339         "596"
41340       ],
41341       [
41342         "Mauritania (‫موريتانيا‬‎)",
41343         "mr",
41344         "222"
41345       ],
41346       [
41347         "Mauritius (Moris)",
41348         "mu",
41349         "230"
41350       ],
41351       [
41352         "Mayotte",
41353         "yt",
41354         "262",
41355         1
41356       ],
41357       [
41358         "Mexico (México)",
41359         "mx",
41360         "52"
41361       ],
41362       [
41363         "Micronesia",
41364         "fm",
41365         "691"
41366       ],
41367       [
41368         "Moldova (Republica Moldova)",
41369         "md",
41370         "373"
41371       ],
41372       [
41373         "Monaco",
41374         "mc",
41375         "377"
41376       ],
41377       [
41378         "Mongolia (Монгол)",
41379         "mn",
41380         "976"
41381       ],
41382       [
41383         "Montenegro (Crna Gora)",
41384         "me",
41385         "382"
41386       ],
41387       [
41388         "Montserrat",
41389         "ms",
41390         "1664"
41391       ],
41392       [
41393         "Morocco (‫المغرب‬‎)",
41394         "ma",
41395         "212",
41396         0
41397       ],
41398       [
41399         "Mozambique (Moçambique)",
41400         "mz",
41401         "258"
41402       ],
41403       [
41404         "Myanmar (Burma) (မြန်မာ)",
41405         "mm",
41406         "95"
41407       ],
41408       [
41409         "Namibia (Namibië)",
41410         "na",
41411         "264"
41412       ],
41413       [
41414         "Nauru",
41415         "nr",
41416         "674"
41417       ],
41418       [
41419         "Nepal (नेपाल)",
41420         "np",
41421         "977"
41422       ],
41423       [
41424         "Netherlands (Nederland)",
41425         "nl",
41426         "31"
41427       ],
41428       [
41429         "New Caledonia (Nouvelle-Calédonie)",
41430         "nc",
41431         "687"
41432       ],
41433       [
41434         "New Zealand",
41435         "nz",
41436         "64"
41437       ],
41438       [
41439         "Nicaragua",
41440         "ni",
41441         "505"
41442       ],
41443       [
41444         "Niger (Nijar)",
41445         "ne",
41446         "227"
41447       ],
41448       [
41449         "Nigeria",
41450         "ng",
41451         "234"
41452       ],
41453       [
41454         "Niue",
41455         "nu",
41456         "683"
41457       ],
41458       [
41459         "Norfolk Island",
41460         "nf",
41461         "672"
41462       ],
41463       [
41464         "North Korea (조선 민주주의 인민 공화국)",
41465         "kp",
41466         "850"
41467       ],
41468       [
41469         "Northern Mariana Islands",
41470         "mp",
41471         "1670"
41472       ],
41473       [
41474         "Norway (Norge)",
41475         "no",
41476         "47",
41477         0
41478       ],
41479       [
41480         "Oman (‫عُمان‬‎)",
41481         "om",
41482         "968"
41483       ],
41484       [
41485         "Pakistan (‫پاکستان‬‎)",
41486         "pk",
41487         "92"
41488       ],
41489       [
41490         "Palau",
41491         "pw",
41492         "680"
41493       ],
41494       [
41495         "Palestine (‫فلسطين‬‎)",
41496         "ps",
41497         "970"
41498       ],
41499       [
41500         "Panama (Panamá)",
41501         "pa",
41502         "507"
41503       ],
41504       [
41505         "Papua New Guinea",
41506         "pg",
41507         "675"
41508       ],
41509       [
41510         "Paraguay",
41511         "py",
41512         "595"
41513       ],
41514       [
41515         "Peru (Perú)",
41516         "pe",
41517         "51"
41518       ],
41519       [
41520         "Philippines",
41521         "ph",
41522         "63"
41523       ],
41524       [
41525         "Poland (Polska)",
41526         "pl",
41527         "48"
41528       ],
41529       [
41530         "Portugal",
41531         "pt",
41532         "351"
41533       ],
41534       [
41535         "Puerto Rico",
41536         "pr",
41537         "1",
41538         3,
41539         ["787", "939"]
41540       ],
41541       [
41542         "Qatar (‫قطر‬‎)",
41543         "qa",
41544         "974"
41545       ],
41546       [
41547         "Réunion (La Réunion)",
41548         "re",
41549         "262",
41550         0
41551       ],
41552       [
41553         "Romania (România)",
41554         "ro",
41555         "40"
41556       ],
41557       [
41558         "Russia (Россия)",
41559         "ru",
41560         "7",
41561         0
41562       ],
41563       [
41564         "Rwanda",
41565         "rw",
41566         "250"
41567       ],
41568       [
41569         "Saint Barthélemy",
41570         "bl",
41571         "590",
41572         1
41573       ],
41574       [
41575         "Saint Helena",
41576         "sh",
41577         "290"
41578       ],
41579       [
41580         "Saint Kitts and Nevis",
41581         "kn",
41582         "1869"
41583       ],
41584       [
41585         "Saint Lucia",
41586         "lc",
41587         "1758"
41588       ],
41589       [
41590         "Saint Martin (Saint-Martin (partie française))",
41591         "mf",
41592         "590",
41593         2
41594       ],
41595       [
41596         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
41597         "pm",
41598         "508"
41599       ],
41600       [
41601         "Saint Vincent and the Grenadines",
41602         "vc",
41603         "1784"
41604       ],
41605       [
41606         "Samoa",
41607         "ws",
41608         "685"
41609       ],
41610       [
41611         "San Marino",
41612         "sm",
41613         "378"
41614       ],
41615       [
41616         "São Tomé and Príncipe (São Tomé e Príncipe)",
41617         "st",
41618         "239"
41619       ],
41620       [
41621         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
41622         "sa",
41623         "966"
41624       ],
41625       [
41626         "Senegal (Sénégal)",
41627         "sn",
41628         "221"
41629       ],
41630       [
41631         "Serbia (Србија)",
41632         "rs",
41633         "381"
41634       ],
41635       [
41636         "Seychelles",
41637         "sc",
41638         "248"
41639       ],
41640       [
41641         "Sierra Leone",
41642         "sl",
41643         "232"
41644       ],
41645       [
41646         "Singapore",
41647         "sg",
41648         "65"
41649       ],
41650       [
41651         "Sint Maarten",
41652         "sx",
41653         "1721"
41654       ],
41655       [
41656         "Slovakia (Slovensko)",
41657         "sk",
41658         "421"
41659       ],
41660       [
41661         "Slovenia (Slovenija)",
41662         "si",
41663         "386"
41664       ],
41665       [
41666         "Solomon Islands",
41667         "sb",
41668         "677"
41669       ],
41670       [
41671         "Somalia (Soomaaliya)",
41672         "so",
41673         "252"
41674       ],
41675       [
41676         "South Africa",
41677         "za",
41678         "27"
41679       ],
41680       [
41681         "South Korea (대한민국)",
41682         "kr",
41683         "82"
41684       ],
41685       [
41686         "South Sudan (‫جنوب السودان‬‎)",
41687         "ss",
41688         "211"
41689       ],
41690       [
41691         "Spain (España)",
41692         "es",
41693         "34"
41694       ],
41695       [
41696         "Sri Lanka (ශ්‍රී ලංකාව)",
41697         "lk",
41698         "94"
41699       ],
41700       [
41701         "Sudan (‫السودان‬‎)",
41702         "sd",
41703         "249"
41704       ],
41705       [
41706         "Suriname",
41707         "sr",
41708         "597"
41709       ],
41710       [
41711         "Svalbard and Jan Mayen",
41712         "sj",
41713         "47",
41714         1
41715       ],
41716       [
41717         "Swaziland",
41718         "sz",
41719         "268"
41720       ],
41721       [
41722         "Sweden (Sverige)",
41723         "se",
41724         "46"
41725       ],
41726       [
41727         "Switzerland (Schweiz)",
41728         "ch",
41729         "41"
41730       ],
41731       [
41732         "Syria (‫سوريا‬‎)",
41733         "sy",
41734         "963"
41735       ],
41736       [
41737         "Taiwan (台灣)",
41738         "tw",
41739         "886"
41740       ],
41741       [
41742         "Tajikistan",
41743         "tj",
41744         "992"
41745       ],
41746       [
41747         "Tanzania",
41748         "tz",
41749         "255"
41750       ],
41751       [
41752         "Thailand (ไทย)",
41753         "th",
41754         "66"
41755       ],
41756       [
41757         "Timor-Leste",
41758         "tl",
41759         "670"
41760       ],
41761       [
41762         "Togo",
41763         "tg",
41764         "228"
41765       ],
41766       [
41767         "Tokelau",
41768         "tk",
41769         "690"
41770       ],
41771       [
41772         "Tonga",
41773         "to",
41774         "676"
41775       ],
41776       [
41777         "Trinidad and Tobago",
41778         "tt",
41779         "1868"
41780       ],
41781       [
41782         "Tunisia (‫تونس‬‎)",
41783         "tn",
41784         "216"
41785       ],
41786       [
41787         "Turkey (Türkiye)",
41788         "tr",
41789         "90"
41790       ],
41791       [
41792         "Turkmenistan",
41793         "tm",
41794         "993"
41795       ],
41796       [
41797         "Turks and Caicos Islands",
41798         "tc",
41799         "1649"
41800       ],
41801       [
41802         "Tuvalu",
41803         "tv",
41804         "688"
41805       ],
41806       [
41807         "U.S. Virgin Islands",
41808         "vi",
41809         "1340"
41810       ],
41811       [
41812         "Uganda",
41813         "ug",
41814         "256"
41815       ],
41816       [
41817         "Ukraine (Україна)",
41818         "ua",
41819         "380"
41820       ],
41821       [
41822         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
41823         "ae",
41824         "971"
41825       ],
41826       [
41827         "United Kingdom",
41828         "gb",
41829         "44",
41830         0
41831       ],
41832       [
41833         "United States",
41834         "us",
41835         "1",
41836         0
41837       ],
41838       [
41839         "Uruguay",
41840         "uy",
41841         "598"
41842       ],
41843       [
41844         "Uzbekistan (Oʻzbekiston)",
41845         "uz",
41846         "998"
41847       ],
41848       [
41849         "Vanuatu",
41850         "vu",
41851         "678"
41852       ],
41853       [
41854         "Vatican City (Città del Vaticano)",
41855         "va",
41856         "39",
41857         1
41858       ],
41859       [
41860         "Venezuela",
41861         "ve",
41862         "58"
41863       ],
41864       [
41865         "Vietnam (Việt Nam)",
41866         "vn",
41867         "84"
41868       ],
41869       [
41870         "Wallis and Futuna (Wallis-et-Futuna)",
41871         "wf",
41872         "681"
41873       ],
41874       [
41875         "Western Sahara (‫الصحراء الغربية‬‎)",
41876         "eh",
41877         "212",
41878         1
41879       ],
41880       [
41881         "Yemen (‫اليمن‬‎)",
41882         "ye",
41883         "967"
41884       ],
41885       [
41886         "Zambia",
41887         "zm",
41888         "260"
41889       ],
41890       [
41891         "Zimbabwe",
41892         "zw",
41893         "263"
41894       ],
41895       [
41896         "Åland Islands",
41897         "ax",
41898         "358",
41899         1
41900       ]
41901   ];
41902   
41903   return d;
41904 }/**
41905 *    This script refer to:
41906 *    Title: International Telephone Input
41907 *    Author: Jack O'Connor
41908 *    Code version:  v12.1.12
41909 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41910 **/
41911
41912 /**
41913  * @class Roo.bootstrap.PhoneInput
41914  * @extends Roo.bootstrap.TriggerField
41915  * An input with International dial-code selection
41916  
41917  * @cfg {String} defaultDialCode default '+852'
41918  * @cfg {Array} preferedCountries default []
41919   
41920  * @constructor
41921  * Create a new PhoneInput.
41922  * @param {Object} config Configuration options
41923  */
41924
41925 Roo.bootstrap.PhoneInput = function(config) {
41926     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
41927 };
41928
41929 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
41930         
41931         listWidth: undefined,
41932         
41933         selectedClass: 'active',
41934         
41935         invalidClass : "has-warning",
41936         
41937         validClass: 'has-success',
41938         
41939         allowed: '0123456789',
41940         
41941         max_length: 15,
41942         
41943         /**
41944          * @cfg {String} defaultDialCode The default dial code when initializing the input
41945          */
41946         defaultDialCode: '+852',
41947         
41948         /**
41949          * @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
41950          */
41951         preferedCountries: false,
41952         
41953         getAutoCreate : function()
41954         {
41955             var data = Roo.bootstrap.PhoneInputData();
41956             var align = this.labelAlign || this.parentLabelAlign();
41957             var id = Roo.id();
41958             
41959             this.allCountries = [];
41960             this.dialCodeMapping = [];
41961             
41962             for (var i = 0; i < data.length; i++) {
41963               var c = data[i];
41964               this.allCountries[i] = {
41965                 name: c[0],
41966                 iso2: c[1],
41967                 dialCode: c[2],
41968                 priority: c[3] || 0,
41969                 areaCodes: c[4] || null
41970               };
41971               this.dialCodeMapping[c[2]] = {
41972                   name: c[0],
41973                   iso2: c[1],
41974                   priority: c[3] || 0,
41975                   areaCodes: c[4] || null
41976               };
41977             }
41978             
41979             var cfg = {
41980                 cls: 'form-group',
41981                 cn: []
41982             };
41983             
41984             var input =  {
41985                 tag: 'input',
41986                 id : id,
41987                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
41988                 maxlength: this.max_length,
41989                 cls : 'form-control tel-input',
41990                 autocomplete: 'new-password'
41991             };
41992             
41993             var hiddenInput = {
41994                 tag: 'input',
41995                 type: 'hidden',
41996                 cls: 'hidden-tel-input'
41997             };
41998             
41999             if (this.name) {
42000                 hiddenInput.name = this.name;
42001             }
42002             
42003             if (this.disabled) {
42004                 input.disabled = true;
42005             }
42006             
42007             var flag_container = {
42008                 tag: 'div',
42009                 cls: 'flag-box',
42010                 cn: [
42011                     {
42012                         tag: 'div',
42013                         cls: 'flag'
42014                     },
42015                     {
42016                         tag: 'div',
42017                         cls: 'caret'
42018                     }
42019                 ]
42020             };
42021             
42022             var box = {
42023                 tag: 'div',
42024                 cls: this.hasFeedback ? 'has-feedback' : '',
42025                 cn: [
42026                     hiddenInput,
42027                     input,
42028                     {
42029                         tag: 'input',
42030                         cls: 'dial-code-holder',
42031                         disabled: true
42032                     }
42033                 ]
42034             };
42035             
42036             var container = {
42037                 cls: 'roo-select2-container input-group',
42038                 cn: [
42039                     flag_container,
42040                     box
42041                 ]
42042             };
42043             
42044             if (this.fieldLabel.length) {
42045                 var indicator = {
42046                     tag: 'i',
42047                     tooltip: 'This field is required'
42048                 };
42049                 
42050                 var label = {
42051                     tag: 'label',
42052                     'for':  id,
42053                     cls: 'control-label',
42054                     cn: []
42055                 };
42056                 
42057                 var label_text = {
42058                     tag: 'span',
42059                     html: this.fieldLabel
42060                 };
42061                 
42062                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42063                 label.cn = [
42064                     indicator,
42065                     label_text
42066                 ];
42067                 
42068                 if(this.indicatorpos == 'right') {
42069                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42070                     label.cn = [
42071                         label_text,
42072                         indicator
42073                     ];
42074                 }
42075                 
42076                 if(align == 'left') {
42077                     container = {
42078                         tag: 'div',
42079                         cn: [
42080                             container
42081                         ]
42082                     };
42083                     
42084                     if(this.labelWidth > 12){
42085                         label.style = "width: " + this.labelWidth + 'px';
42086                     }
42087                     if(this.labelWidth < 13 && this.labelmd == 0){
42088                         this.labelmd = this.labelWidth;
42089                     }
42090                     if(this.labellg > 0){
42091                         label.cls += ' col-lg-' + this.labellg;
42092                         input.cls += ' col-lg-' + (12 - this.labellg);
42093                     }
42094                     if(this.labelmd > 0){
42095                         label.cls += ' col-md-' + this.labelmd;
42096                         container.cls += ' col-md-' + (12 - this.labelmd);
42097                     }
42098                     if(this.labelsm > 0){
42099                         label.cls += ' col-sm-' + this.labelsm;
42100                         container.cls += ' col-sm-' + (12 - this.labelsm);
42101                     }
42102                     if(this.labelxs > 0){
42103                         label.cls += ' col-xs-' + this.labelxs;
42104                         container.cls += ' col-xs-' + (12 - this.labelxs);
42105                     }
42106                 }
42107             }
42108             
42109             cfg.cn = [
42110                 label,
42111                 container
42112             ];
42113             
42114             var settings = this;
42115             
42116             ['xs','sm','md','lg'].map(function(size){
42117                 if (settings[size]) {
42118                     cfg.cls += ' col-' + size + '-' + settings[size];
42119                 }
42120             });
42121             
42122             this.store = new Roo.data.Store({
42123                 proxy : new Roo.data.MemoryProxy({}),
42124                 reader : new Roo.data.JsonReader({
42125                     fields : [
42126                         {
42127                             'name' : 'name',
42128                             'type' : 'string'
42129                         },
42130                         {
42131                             'name' : 'iso2',
42132                             'type' : 'string'
42133                         },
42134                         {
42135                             'name' : 'dialCode',
42136                             'type' : 'string'
42137                         },
42138                         {
42139                             'name' : 'priority',
42140                             'type' : 'string'
42141                         },
42142                         {
42143                             'name' : 'areaCodes',
42144                             'type' : 'string'
42145                         }
42146                     ]
42147                 })
42148             });
42149             
42150             if(!this.preferedCountries) {
42151                 this.preferedCountries = [
42152                     'hk',
42153                     'gb',
42154                     'us'
42155                 ];
42156             }
42157             
42158             var p = this.preferedCountries.reverse();
42159             
42160             if(p) {
42161                 for (var i = 0; i < p.length; i++) {
42162                     for (var j = 0; j < this.allCountries.length; j++) {
42163                         if(this.allCountries[j].iso2 == p[i]) {
42164                             var t = this.allCountries[j];
42165                             this.allCountries.splice(j,1);
42166                             this.allCountries.unshift(t);
42167                         }
42168                     } 
42169                 }
42170             }
42171             
42172             this.store.proxy.data = {
42173                 success: true,
42174                 data: this.allCountries
42175             };
42176             
42177             return cfg;
42178         },
42179         
42180         initEvents : function()
42181         {
42182             this.createList();
42183             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42184             
42185             this.indicator = this.indicatorEl();
42186             this.flag = this.flagEl();
42187             this.dialCodeHolder = this.dialCodeHolderEl();
42188             
42189             this.trigger = this.el.select('div.flag-box',true).first();
42190             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42191             
42192             var _this = this;
42193             
42194             (function(){
42195                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42196                 _this.list.setWidth(lw);
42197             }).defer(100);
42198             
42199             this.list.on('mouseover', this.onViewOver, this);
42200             this.list.on('mousemove', this.onViewMove, this);
42201             this.inputEl().on("keyup", this.onKeyUp, this);
42202             this.inputEl().on("keypress", this.onKeyPress, this);
42203             
42204             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42205
42206             this.view = new Roo.View(this.list, this.tpl, {
42207                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42208             });
42209             
42210             this.view.on('click', this.onViewClick, this);
42211             this.setValue(this.defaultDialCode);
42212         },
42213         
42214         onTriggerClick : function(e)
42215         {
42216             Roo.log('trigger click');
42217             if(this.disabled){
42218                 return;
42219             }
42220             
42221             if(this.isExpanded()){
42222                 this.collapse();
42223                 this.hasFocus = false;
42224             }else {
42225                 this.store.load({});
42226                 this.hasFocus = true;
42227                 this.expand();
42228             }
42229         },
42230         
42231         isExpanded : function()
42232         {
42233             return this.list.isVisible();
42234         },
42235         
42236         collapse : function()
42237         {
42238             if(!this.isExpanded()){
42239                 return;
42240             }
42241             this.list.hide();
42242             Roo.get(document).un('mousedown', this.collapseIf, this);
42243             Roo.get(document).un('mousewheel', this.collapseIf, this);
42244             this.fireEvent('collapse', this);
42245             this.validate();
42246         },
42247         
42248         expand : function()
42249         {
42250             Roo.log('expand');
42251
42252             if(this.isExpanded() || !this.hasFocus){
42253                 return;
42254             }
42255             
42256             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42257             this.list.setWidth(lw);
42258             
42259             this.list.show();
42260             this.restrictHeight();
42261             
42262             Roo.get(document).on('mousedown', this.collapseIf, this);
42263             Roo.get(document).on('mousewheel', this.collapseIf, this);
42264             
42265             this.fireEvent('expand', this);
42266         },
42267         
42268         restrictHeight : function()
42269         {
42270             this.list.alignTo(this.inputEl(), this.listAlign);
42271             this.list.alignTo(this.inputEl(), this.listAlign);
42272         },
42273         
42274         onViewOver : function(e, t)
42275         {
42276             if(this.inKeyMode){
42277                 return;
42278             }
42279             var item = this.view.findItemFromChild(t);
42280             
42281             if(item){
42282                 var index = this.view.indexOf(item);
42283                 this.select(index, false);
42284             }
42285         },
42286
42287         // private
42288         onViewClick : function(view, doFocus, el, e)
42289         {
42290             var index = this.view.getSelectedIndexes()[0];
42291             
42292             var r = this.store.getAt(index);
42293             
42294             if(r){
42295                 this.onSelect(r, index);
42296             }
42297             if(doFocus !== false && !this.blockFocus){
42298                 this.inputEl().focus();
42299             }
42300         },
42301         
42302         onViewMove : function(e, t)
42303         {
42304             this.inKeyMode = false;
42305         },
42306         
42307         select : function(index, scrollIntoView)
42308         {
42309             this.selectedIndex = index;
42310             this.view.select(index);
42311             if(scrollIntoView !== false){
42312                 var el = this.view.getNode(index);
42313                 if(el){
42314                     this.list.scrollChildIntoView(el, false);
42315                 }
42316             }
42317         },
42318         
42319         createList : function()
42320         {
42321             this.list = Roo.get(document.body).createChild({
42322                 tag: 'ul',
42323                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
42324                 style: 'display:none'
42325             });
42326             
42327             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
42328         },
42329         
42330         collapseIf : function(e)
42331         {
42332             var in_combo  = e.within(this.el);
42333             var in_list =  e.within(this.list);
42334             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
42335             
42336             if (in_combo || in_list || is_list) {
42337                 return;
42338             }
42339             this.collapse();
42340         },
42341         
42342         onSelect : function(record, index)
42343         {
42344             if(this.fireEvent('beforeselect', this, record, index) !== false){
42345                 
42346                 this.setFlagClass(record.data.iso2);
42347                 this.setDialCode(record.data.dialCode);
42348                 this.hasFocus = false;
42349                 this.collapse();
42350                 this.fireEvent('select', this, record, index);
42351             }
42352         },
42353         
42354         flagEl : function()
42355         {
42356             var flag = this.el.select('div.flag',true).first();
42357             if(!flag){
42358                 return false;
42359             }
42360             return flag;
42361         },
42362         
42363         dialCodeHolderEl : function()
42364         {
42365             var d = this.el.select('input.dial-code-holder',true).first();
42366             if(!d){
42367                 return false;
42368             }
42369             return d;
42370         },
42371         
42372         setDialCode : function(v)
42373         {
42374             this.dialCodeHolder.dom.value = '+'+v;
42375         },
42376         
42377         setFlagClass : function(n)
42378         {
42379             this.flag.dom.className = 'flag '+n;
42380         },
42381         
42382         getValue : function()
42383         {
42384             var v = this.inputEl().getValue();
42385             if(this.dialCodeHolder) {
42386                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
42387             }
42388             return v;
42389         },
42390         
42391         setValue : function(v)
42392         {
42393             var d = this.getDialCode(v);
42394             
42395             //invalid dial code
42396             if(v.length == 0 || !d || d.length == 0) {
42397                 if(this.rendered){
42398                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
42399                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
42400                 }
42401                 return;
42402             }
42403             
42404             //valid dial code
42405             this.setFlagClass(this.dialCodeMapping[d].iso2);
42406             this.setDialCode(d);
42407             this.inputEl().dom.value = v.replace('+'+d,'');
42408             this.hiddenEl().dom.value = this.getValue();
42409             
42410             this.validate();
42411         },
42412         
42413         getDialCode : function(v)
42414         {
42415             v = v ||  '';
42416             
42417             if (v.length == 0) {
42418                 return this.dialCodeHolder.dom.value;
42419             }
42420             
42421             var dialCode = "";
42422             if (v.charAt(0) != "+") {
42423                 return false;
42424             }
42425             var numericChars = "";
42426             for (var i = 1; i < v.length; i++) {
42427               var c = v.charAt(i);
42428               if (!isNaN(c)) {
42429                 numericChars += c;
42430                 if (this.dialCodeMapping[numericChars]) {
42431                   dialCode = v.substr(1, i);
42432                 }
42433                 if (numericChars.length == 4) {
42434                   break;
42435                 }
42436               }
42437             }
42438             return dialCode;
42439         },
42440         
42441         reset : function()
42442         {
42443             this.setValue(this.defaultDialCode);
42444             this.validate();
42445         },
42446         
42447         hiddenEl : function()
42448         {
42449             return this.el.select('input.hidden-tel-input',true).first();
42450         },
42451         
42452         // after setting val
42453         onKeyUp : function(e){
42454             this.setValue(this.getValue());
42455         },
42456         
42457         onKeyPress : function(e){
42458             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
42459                 e.stopEvent();
42460             }
42461         }
42462         
42463 });
42464 /**
42465  * @class Roo.bootstrap.MoneyField
42466  * @extends Roo.bootstrap.ComboBox
42467  * Bootstrap MoneyField class
42468  * 
42469  * @constructor
42470  * Create a new MoneyField.
42471  * @param {Object} config Configuration options
42472  */
42473
42474 Roo.bootstrap.MoneyField = function(config) {
42475     
42476     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
42477     
42478 };
42479
42480 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
42481     
42482     /**
42483      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
42484      */
42485     allowDecimals : true,
42486     /**
42487      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
42488      */
42489     decimalSeparator : ".",
42490     /**
42491      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
42492      */
42493     decimalPrecision : 0,
42494     /**
42495      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
42496      */
42497     allowNegative : true,
42498     /**
42499      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
42500      */
42501     allowZero: true,
42502     /**
42503      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
42504      */
42505     minValue : Number.NEGATIVE_INFINITY,
42506     /**
42507      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
42508      */
42509     maxValue : Number.MAX_VALUE,
42510     /**
42511      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
42512      */
42513     minText : "The minimum value for this field is {0}",
42514     /**
42515      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
42516      */
42517     maxText : "The maximum value for this field is {0}",
42518     /**
42519      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
42520      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
42521      */
42522     nanText : "{0} is not a valid number",
42523     /**
42524      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
42525      */
42526     castInt : true,
42527     /**
42528      * @cfg {String} defaults currency of the MoneyField
42529      * value should be in lkey
42530      */
42531     defaultCurrency : false,
42532     /**
42533      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
42534      */
42535     thousandsDelimiter : false,
42536     /**
42537      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
42538      */
42539     max_length: false,
42540     
42541     inputlg : 9,
42542     inputmd : 9,
42543     inputsm : 9,
42544     inputxs : 6,
42545     
42546     store : false,
42547     
42548     getAutoCreate : function()
42549     {
42550         var align = this.labelAlign || this.parentLabelAlign();
42551         
42552         var id = Roo.id();
42553
42554         var cfg = {
42555             cls: 'form-group',
42556             cn: []
42557         };
42558
42559         var input =  {
42560             tag: 'input',
42561             id : id,
42562             cls : 'form-control roo-money-amount-input',
42563             autocomplete: 'new-password'
42564         };
42565         
42566         var hiddenInput = {
42567             tag: 'input',
42568             type: 'hidden',
42569             id: Roo.id(),
42570             cls: 'hidden-number-input'
42571         };
42572         
42573         if(this.max_length) {
42574             input.maxlength = this.max_length; 
42575         }
42576         
42577         if (this.name) {
42578             hiddenInput.name = this.name;
42579         }
42580
42581         if (this.disabled) {
42582             input.disabled = true;
42583         }
42584
42585         var clg = 12 - this.inputlg;
42586         var cmd = 12 - this.inputmd;
42587         var csm = 12 - this.inputsm;
42588         var cxs = 12 - this.inputxs;
42589         
42590         var container = {
42591             tag : 'div',
42592             cls : 'row roo-money-field',
42593             cn : [
42594                 {
42595                     tag : 'div',
42596                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
42597                     cn : [
42598                         {
42599                             tag : 'div',
42600                             cls: 'roo-select2-container input-group',
42601                             cn: [
42602                                 {
42603                                     tag : 'input',
42604                                     cls : 'form-control roo-money-currency-input',
42605                                     autocomplete: 'new-password',
42606                                     readOnly : 1,
42607                                     name : this.currencyName
42608                                 },
42609                                 {
42610                                     tag :'span',
42611                                     cls : 'input-group-addon',
42612                                     cn : [
42613                                         {
42614                                             tag: 'span',
42615                                             cls: 'caret'
42616                                         }
42617                                     ]
42618                                 }
42619                             ]
42620                         }
42621                     ]
42622                 },
42623                 {
42624                     tag : 'div',
42625                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
42626                     cn : [
42627                         {
42628                             tag: 'div',
42629                             cls: this.hasFeedback ? 'has-feedback' : '',
42630                             cn: [
42631                                 input
42632                             ]
42633                         }
42634                     ]
42635                 }
42636             ]
42637             
42638         };
42639         
42640         if (this.fieldLabel.length) {
42641             var indicator = {
42642                 tag: 'i',
42643                 tooltip: 'This field is required'
42644             };
42645
42646             var label = {
42647                 tag: 'label',
42648                 'for':  id,
42649                 cls: 'control-label',
42650                 cn: []
42651             };
42652
42653             var label_text = {
42654                 tag: 'span',
42655                 html: this.fieldLabel
42656             };
42657
42658             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42659             label.cn = [
42660                 indicator,
42661                 label_text
42662             ];
42663
42664             if(this.indicatorpos == 'right') {
42665                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42666                 label.cn = [
42667                     label_text,
42668                     indicator
42669                 ];
42670             }
42671
42672             if(align == 'left') {
42673                 container = {
42674                     tag: 'div',
42675                     cn: [
42676                         container
42677                     ]
42678                 };
42679
42680                 if(this.labelWidth > 12){
42681                     label.style = "width: " + this.labelWidth + 'px';
42682                 }
42683                 if(this.labelWidth < 13 && this.labelmd == 0){
42684                     this.labelmd = this.labelWidth;
42685                 }
42686                 if(this.labellg > 0){
42687                     label.cls += ' col-lg-' + this.labellg;
42688                     input.cls += ' col-lg-' + (12 - this.labellg);
42689                 }
42690                 if(this.labelmd > 0){
42691                     label.cls += ' col-md-' + this.labelmd;
42692                     container.cls += ' col-md-' + (12 - this.labelmd);
42693                 }
42694                 if(this.labelsm > 0){
42695                     label.cls += ' col-sm-' + this.labelsm;
42696                     container.cls += ' col-sm-' + (12 - this.labelsm);
42697                 }
42698                 if(this.labelxs > 0){
42699                     label.cls += ' col-xs-' + this.labelxs;
42700                     container.cls += ' col-xs-' + (12 - this.labelxs);
42701                 }
42702             }
42703         }
42704
42705         cfg.cn = [
42706             label,
42707             container,
42708             hiddenInput
42709         ];
42710         
42711         var settings = this;
42712
42713         ['xs','sm','md','lg'].map(function(size){
42714             if (settings[size]) {
42715                 cfg.cls += ' col-' + size + '-' + settings[size];
42716             }
42717         });
42718         
42719         return cfg;
42720     },
42721     
42722     initEvents : function()
42723     {
42724         this.indicator = this.indicatorEl();
42725         
42726         this.initCurrencyEvent();
42727         
42728         this.initNumberEvent();
42729     },
42730     
42731     initCurrencyEvent : function()
42732     {
42733         if (!this.store) {
42734             throw "can not find store for combo";
42735         }
42736         
42737         this.store = Roo.factory(this.store, Roo.data);
42738         this.store.parent = this;
42739         
42740         this.createList();
42741         
42742         this.triggerEl = this.el.select('.input-group-addon', true).first();
42743         
42744         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
42745         
42746         var _this = this;
42747         
42748         (function(){
42749             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42750             _this.list.setWidth(lw);
42751         }).defer(100);
42752         
42753         this.list.on('mouseover', this.onViewOver, this);
42754         this.list.on('mousemove', this.onViewMove, this);
42755         this.list.on('scroll', this.onViewScroll, this);
42756         
42757         if(!this.tpl){
42758             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
42759         }
42760         
42761         this.view = new Roo.View(this.list, this.tpl, {
42762             singleSelect:true, store: this.store, selectedClass: this.selectedClass
42763         });
42764         
42765         this.view.on('click', this.onViewClick, this);
42766         
42767         this.store.on('beforeload', this.onBeforeLoad, this);
42768         this.store.on('load', this.onLoad, this);
42769         this.store.on('loadexception', this.onLoadException, this);
42770         
42771         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
42772             "up" : function(e){
42773                 this.inKeyMode = true;
42774                 this.selectPrev();
42775             },
42776
42777             "down" : function(e){
42778                 if(!this.isExpanded()){
42779                     this.onTriggerClick();
42780                 }else{
42781                     this.inKeyMode = true;
42782                     this.selectNext();
42783                 }
42784             },
42785
42786             "enter" : function(e){
42787                 this.collapse();
42788                 
42789                 if(this.fireEvent("specialkey", this, e)){
42790                     this.onViewClick(false);
42791                 }
42792                 
42793                 return true;
42794             },
42795
42796             "esc" : function(e){
42797                 this.collapse();
42798             },
42799
42800             "tab" : function(e){
42801                 this.collapse();
42802                 
42803                 if(this.fireEvent("specialkey", this, e)){
42804                     this.onViewClick(false);
42805                 }
42806                 
42807                 return true;
42808             },
42809
42810             scope : this,
42811
42812             doRelay : function(foo, bar, hname){
42813                 if(hname == 'down' || this.scope.isExpanded()){
42814                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
42815                 }
42816                 return true;
42817             },
42818
42819             forceKeyDown: true
42820         });
42821         
42822         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
42823         
42824     },
42825     
42826     initNumberEvent : function(e)
42827     {
42828         this.inputEl().on("keydown" , this.fireKey,  this);
42829         this.inputEl().on("focus", this.onFocus,  this);
42830         this.inputEl().on("blur", this.onBlur,  this);
42831         
42832         this.inputEl().relayEvent('keyup', this);
42833         
42834         if(this.indicator){
42835             this.indicator.addClass('invisible');
42836         }
42837  
42838         this.originalValue = this.getValue();
42839         
42840         if(this.validationEvent == 'keyup'){
42841             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
42842             this.inputEl().on('keyup', this.filterValidation, this);
42843         }
42844         else if(this.validationEvent !== false){
42845             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
42846         }
42847         
42848         if(this.selectOnFocus){
42849             this.on("focus", this.preFocus, this);
42850             
42851         }
42852         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
42853             this.inputEl().on("keypress", this.filterKeys, this);
42854         } else {
42855             this.inputEl().relayEvent('keypress', this);
42856         }
42857         
42858         var allowed = "0123456789";
42859         
42860         if(this.allowDecimals){
42861             allowed += this.decimalSeparator;
42862         }
42863         
42864         if(this.allowNegative){
42865             allowed += "-";
42866         }
42867         
42868         if(this.thousandsDelimiter) {
42869             allowed += ",";
42870         }
42871         
42872         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
42873         
42874         var keyPress = function(e){
42875             
42876             var k = e.getKey();
42877             
42878             var c = e.getCharCode();
42879             
42880             if(
42881                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
42882                     allowed.indexOf(String.fromCharCode(c)) === -1
42883             ){
42884                 e.stopEvent();
42885                 return;
42886             }
42887             
42888             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
42889                 return;
42890             }
42891             
42892             if(allowed.indexOf(String.fromCharCode(c)) === -1){
42893                 e.stopEvent();
42894             }
42895         };
42896         
42897         this.inputEl().on("keypress", keyPress, this);
42898         
42899     },
42900     
42901     onTriggerClick : function(e)
42902     {   
42903         if(this.disabled){
42904             return;
42905         }
42906         
42907         this.page = 0;
42908         this.loadNext = false;
42909         
42910         if(this.isExpanded()){
42911             this.collapse();
42912             return;
42913         }
42914         
42915         this.hasFocus = true;
42916         
42917         if(this.triggerAction == 'all') {
42918             this.doQuery(this.allQuery, true);
42919             return;
42920         }
42921         
42922         this.doQuery(this.getRawValue());
42923     },
42924     
42925     getCurrency : function()
42926     {   
42927         var v = this.currencyEl().getValue();
42928         
42929         return v;
42930     },
42931     
42932     restrictHeight : function()
42933     {
42934         this.list.alignTo(this.currencyEl(), this.listAlign);
42935         this.list.alignTo(this.currencyEl(), this.listAlign);
42936     },
42937     
42938     onViewClick : function(view, doFocus, el, e)
42939     {
42940         var index = this.view.getSelectedIndexes()[0];
42941         
42942         var r = this.store.getAt(index);
42943         
42944         if(r){
42945             this.onSelect(r, index);
42946         }
42947     },
42948     
42949     onSelect : function(record, index){
42950         
42951         if(this.fireEvent('beforeselect', this, record, index) !== false){
42952         
42953             this.setFromCurrencyData(index > -1 ? record.data : false);
42954             
42955             this.collapse();
42956             
42957             this.fireEvent('select', this, record, index);
42958         }
42959     },
42960     
42961     setFromCurrencyData : function(o)
42962     {
42963         var currency = '';
42964         
42965         this.lastCurrency = o;
42966         
42967         if (this.currencyField) {
42968             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
42969         } else {
42970             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
42971         }
42972         
42973         this.lastSelectionText = currency;
42974         
42975         //setting default currency
42976         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
42977             this.setCurrency(this.defaultCurrency);
42978             return;
42979         }
42980         
42981         this.setCurrency(currency);
42982     },
42983     
42984     setFromData : function(o)
42985     {
42986         var c = {};
42987         
42988         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
42989         
42990         this.setFromCurrencyData(c);
42991         
42992         var value = '';
42993         
42994         if (this.name) {
42995             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
42996         } else {
42997             Roo.log('no value set for '+ (this.name ? this.name : this.id));
42998         }
42999         
43000         this.setValue(value);
43001         
43002     },
43003     
43004     setCurrency : function(v)
43005     {   
43006         this.currencyValue = v;
43007         
43008         if(this.rendered){
43009             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43010             this.validate();
43011         }
43012     },
43013     
43014     setValue : function(v)
43015     {
43016         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43017         
43018         this.value = v;
43019         
43020         if(this.rendered){
43021             
43022             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43023             
43024             this.inputEl().dom.value = (v == '') ? '' :
43025                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43026             
43027             if(!this.allowZero && v === '0') {
43028                 this.hiddenEl().dom.value = '';
43029                 this.inputEl().dom.value = '';
43030             }
43031             
43032             this.validate();
43033         }
43034     },
43035     
43036     getRawValue : function()
43037     {
43038         var v = this.inputEl().getValue();
43039         
43040         return v;
43041     },
43042     
43043     getValue : function()
43044     {
43045         return this.fixPrecision(this.parseValue(this.getRawValue()));
43046     },
43047     
43048     parseValue : function(value)
43049     {
43050         if(this.thousandsDelimiter) {
43051             value += "";
43052             r = new RegExp(",", "g");
43053             value = value.replace(r, "");
43054         }
43055         
43056         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43057         return isNaN(value) ? '' : value;
43058         
43059     },
43060     
43061     fixPrecision : function(value)
43062     {
43063         if(this.thousandsDelimiter) {
43064             value += "";
43065             r = new RegExp(",", "g");
43066             value = value.replace(r, "");
43067         }
43068         
43069         var nan = isNaN(value);
43070         
43071         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43072             return nan ? '' : value;
43073         }
43074         return parseFloat(value).toFixed(this.decimalPrecision);
43075     },
43076     
43077     decimalPrecisionFcn : function(v)
43078     {
43079         return Math.floor(v);
43080     },
43081     
43082     validateValue : function(value)
43083     {
43084         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43085             return false;
43086         }
43087         
43088         var num = this.parseValue(value);
43089         
43090         if(isNaN(num)){
43091             this.markInvalid(String.format(this.nanText, value));
43092             return false;
43093         }
43094         
43095         if(num < this.minValue){
43096             this.markInvalid(String.format(this.minText, this.minValue));
43097             return false;
43098         }
43099         
43100         if(num > this.maxValue){
43101             this.markInvalid(String.format(this.maxText, this.maxValue));
43102             return false;
43103         }
43104         
43105         return true;
43106     },
43107     
43108     validate : function()
43109     {
43110         if(this.disabled || this.allowBlank){
43111             this.markValid();
43112             return true;
43113         }
43114         
43115         var currency = this.getCurrency();
43116         
43117         if(this.validateValue(this.getRawValue()) && currency.length){
43118             this.markValid();
43119             return true;
43120         }
43121         
43122         this.markInvalid();
43123         return false;
43124     },
43125     
43126     getName: function()
43127     {
43128         return this.name;
43129     },
43130     
43131     beforeBlur : function()
43132     {
43133         if(!this.castInt){
43134             return;
43135         }
43136         
43137         var v = this.parseValue(this.getRawValue());
43138         
43139         if(v || v == 0){
43140             this.setValue(v);
43141         }
43142     },
43143     
43144     onBlur : function()
43145     {
43146         this.beforeBlur();
43147         
43148         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43149             //this.el.removeClass(this.focusClass);
43150         }
43151         
43152         this.hasFocus = false;
43153         
43154         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43155             this.validate();
43156         }
43157         
43158         var v = this.getValue();
43159         
43160         if(String(v) !== String(this.startValue)){
43161             this.fireEvent('change', this, v, this.startValue);
43162         }
43163         
43164         this.fireEvent("blur", this);
43165     },
43166     
43167     inputEl : function()
43168     {
43169         return this.el.select('.roo-money-amount-input', true).first();
43170     },
43171     
43172     currencyEl : function()
43173     {
43174         return this.el.select('.roo-money-currency-input', true).first();
43175     },
43176     
43177     hiddenEl : function()
43178     {
43179         return this.el.select('input.hidden-number-input',true).first();
43180     }
43181     
43182 });/**
43183  * @class Roo.bootstrap.BezierSignature
43184  * @extends Roo.bootstrap.Component
43185  * Bootstrap BezierSignature class
43186  * This script refer to:
43187  *    Title: Signature Pad
43188  *    Author: szimek
43189  *    Availability: https://github.com/szimek/signature_pad
43190  *
43191  * @constructor
43192  * Create a new BezierSignature
43193  * @param {Object} config The config object
43194  */
43195
43196 Roo.bootstrap.BezierSignature = function(config){
43197     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43198     this.addEvents({
43199         "resize" : true
43200     });
43201 };
43202
43203 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43204 {
43205      
43206     curve_data: [],
43207     
43208     is_empty: true,
43209     
43210     mouse_btn_down: true,
43211     
43212     /**
43213      * @cfg {int} canvas height
43214      */
43215     canvas_height: '200px',
43216     
43217     /**
43218      * @cfg {float|function} Radius of a single dot.
43219      */ 
43220     dot_size: false,
43221     
43222     /**
43223      * @cfg {float} Minimum width of a line. Defaults to 0.5.
43224      */
43225     min_width: 0.5,
43226     
43227     /**
43228      * @cfg {float} Maximum width of a line. Defaults to 2.5.
43229      */
43230     max_width: 2.5,
43231     
43232     /**
43233      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43234      */
43235     throttle: 16,
43236     
43237     /**
43238      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43239      */
43240     min_distance: 5,
43241     
43242     /**
43243      * @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.
43244      */
43245     bg_color: 'rgba(0, 0, 0, 0)',
43246     
43247     /**
43248      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43249      */
43250     dot_color: 'black',
43251     
43252     /**
43253      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43254      */ 
43255     velocity_filter_weight: 0.7,
43256     
43257     /**
43258      * @cfg {function} Callback when stroke begin. 
43259      */
43260     onBegin: false,
43261     
43262     /**
43263      * @cfg {function} Callback when stroke end.
43264      */
43265     onEnd: false,
43266     
43267     getAutoCreate : function()
43268     {
43269         var cls = 'roo-signature column';
43270         
43271         if(this.cls){
43272             cls += ' ' + this.cls;
43273         }
43274         
43275         var col_sizes = [
43276             'lg',
43277             'md',
43278             'sm',
43279             'xs'
43280         ];
43281         
43282         for(var i = 0; i < col_sizes.length; i++) {
43283             if(this[col_sizes[i]]) {
43284                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
43285             }
43286         }
43287         
43288         var cfg = {
43289             tag: 'div',
43290             cls: cls,
43291             cn: [
43292                 {
43293                     tag: 'div',
43294                     cls: 'roo-signature-body',
43295                     cn: [
43296                         {
43297                             tag: 'canvas',
43298                             cls: 'roo-signature-body-canvas',
43299                             height: this.canvas_height,
43300                             width: this.canvas_width
43301                         }
43302                     ]
43303                 },
43304                 {
43305                     tag: 'input',
43306                     type: 'file',
43307                     style: 'display: none'
43308                 }
43309             ]
43310         };
43311         
43312         return cfg;
43313     },
43314     
43315     initEvents: function() 
43316     {
43317         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
43318         
43319         var canvas = this.canvasEl();
43320         
43321         // mouse && touch event swapping...
43322         canvas.dom.style.touchAction = 'none';
43323         canvas.dom.style.msTouchAction = 'none';
43324         
43325         this.mouse_btn_down = false;
43326         canvas.on('mousedown', this._handleMouseDown, this);
43327         canvas.on('mousemove', this._handleMouseMove, this);
43328         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
43329         
43330         if (window.PointerEvent) {
43331             canvas.on('pointerdown', this._handleMouseDown, this);
43332             canvas.on('pointermove', this._handleMouseMove, this);
43333             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
43334         }
43335         
43336         if ('ontouchstart' in window) {
43337             canvas.on('touchstart', this._handleTouchStart, this);
43338             canvas.on('touchmove', this._handleTouchMove, this);
43339             canvas.on('touchend', this._handleTouchEnd, this);
43340         }
43341         
43342         Roo.EventManager.onWindowResize(this.resize, this, true);
43343         
43344         // file input event
43345         this.fileEl().on('change', this.uploadImage, this);
43346         
43347         this.clear();
43348         
43349         this.resize();
43350     },
43351     
43352     resize: function(){
43353         
43354         var canvas = this.canvasEl().dom;
43355         var ctx = this.canvasElCtx();
43356         var img_data = false;
43357         
43358         if(canvas.width > 0) {
43359             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
43360         }
43361         // setting canvas width will clean img data
43362         canvas.width = 0;
43363         
43364         var style = window.getComputedStyle ? 
43365             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
43366             
43367         var padding_left = parseInt(style.paddingLeft) || 0;
43368         var padding_right = parseInt(style.paddingRight) || 0;
43369         
43370         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
43371         
43372         if(img_data) {
43373             ctx.putImageData(img_data, 0, 0);
43374         }
43375     },
43376     
43377     _handleMouseDown: function(e)
43378     {
43379         if (e.browserEvent.which === 1) {
43380             this.mouse_btn_down = true;
43381             this.strokeBegin(e);
43382         }
43383     },
43384     
43385     _handleMouseMove: function (e)
43386     {
43387         if (this.mouse_btn_down) {
43388             this.strokeMoveUpdate(e);
43389         }
43390     },
43391     
43392     _handleMouseUp: function (e)
43393     {
43394         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
43395             this.mouse_btn_down = false;
43396             this.strokeEnd(e);
43397         }
43398     },
43399     
43400     _handleTouchStart: function (e) {
43401         
43402         e.preventDefault();
43403         if (e.browserEvent.targetTouches.length === 1) {
43404             // var touch = e.browserEvent.changedTouches[0];
43405             // this.strokeBegin(touch);
43406             
43407              this.strokeBegin(e); // assume e catching the correct xy...
43408         }
43409     },
43410     
43411     _handleTouchMove: function (e) {
43412         e.preventDefault();
43413         // var touch = event.targetTouches[0];
43414         // _this._strokeMoveUpdate(touch);
43415         this.strokeMoveUpdate(e);
43416     },
43417     
43418     _handleTouchEnd: function (e) {
43419         var wasCanvasTouched = e.target === this.canvasEl().dom;
43420         if (wasCanvasTouched) {
43421             e.preventDefault();
43422             // var touch = event.changedTouches[0];
43423             // _this._strokeEnd(touch);
43424             this.strokeEnd(e);
43425         }
43426     },
43427     
43428     reset: function () {
43429         this._lastPoints = [];
43430         this._lastVelocity = 0;
43431         this._lastWidth = (this.min_width + this.max_width) / 2;
43432         this.canvasElCtx().fillStyle = this.dot_color;
43433     },
43434     
43435     strokeMoveUpdate: function(e)
43436     {
43437         this.strokeUpdate(e);
43438         
43439         if (this.throttle) {
43440             this.throttleStroke(this.strokeUpdate, this.throttle);
43441         }
43442         else {
43443             this.strokeUpdate(e);
43444         }
43445     },
43446     
43447     strokeBegin: function(e)
43448     {
43449         var newPointGroup = {
43450             color: this.dot_color,
43451             points: []
43452         };
43453         
43454         if (typeof this.onBegin === 'function') {
43455             this.onBegin(e);
43456         }
43457         
43458         this.curve_data.push(newPointGroup);
43459         this.reset();
43460         this.strokeUpdate(e);
43461     },
43462     
43463     strokeUpdate: function(e)
43464     {
43465         var rect = this.canvasEl().dom.getBoundingClientRect();
43466         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
43467         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
43468         var lastPoints = lastPointGroup.points;
43469         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
43470         var isLastPointTooClose = lastPoint
43471             ? point.distanceTo(lastPoint) <= this.min_distance
43472             : false;
43473         var color = lastPointGroup.color;
43474         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
43475             var curve = this.addPoint(point);
43476             if (!lastPoint) {
43477                 this.drawDot({color: color, point: point});
43478             }
43479             else if (curve) {
43480                 this.drawCurve({color: color, curve: curve});
43481             }
43482             lastPoints.push({
43483                 time: point.time,
43484                 x: point.x,
43485                 y: point.y
43486             });
43487         }
43488     },
43489     
43490     strokeEnd: function(e)
43491     {
43492         this.strokeUpdate(e);
43493         if (typeof this.onEnd === 'function') {
43494             this.onEnd(e);
43495         }
43496     },
43497     
43498     addPoint:  function (point) {
43499         var _lastPoints = this._lastPoints;
43500         _lastPoints.push(point);
43501         if (_lastPoints.length > 2) {
43502             if (_lastPoints.length === 3) {
43503                 _lastPoints.unshift(_lastPoints[0]);
43504             }
43505             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
43506             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
43507             _lastPoints.shift();
43508             return curve;
43509         }
43510         return null;
43511     },
43512     
43513     calculateCurveWidths: function (startPoint, endPoint) {
43514         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
43515             (1 - this.velocity_filter_weight) * this._lastVelocity;
43516
43517         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
43518         var widths = {
43519             end: newWidth,
43520             start: this._lastWidth
43521         };
43522         
43523         this._lastVelocity = velocity;
43524         this._lastWidth = newWidth;
43525         return widths;
43526     },
43527     
43528     drawDot: function (_a) {
43529         var color = _a.color, point = _a.point;
43530         var ctx = this.canvasElCtx();
43531         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
43532         ctx.beginPath();
43533         this.drawCurveSegment(point.x, point.y, width);
43534         ctx.closePath();
43535         ctx.fillStyle = color;
43536         ctx.fill();
43537     },
43538     
43539     drawCurve: function (_a) {
43540         var color = _a.color, curve = _a.curve;
43541         var ctx = this.canvasElCtx();
43542         var widthDelta = curve.endWidth - curve.startWidth;
43543         var drawSteps = Math.floor(curve.length()) * 2;
43544         ctx.beginPath();
43545         ctx.fillStyle = color;
43546         for (var i = 0; i < drawSteps; i += 1) {
43547         var t = i / drawSteps;
43548         var tt = t * t;
43549         var ttt = tt * t;
43550         var u = 1 - t;
43551         var uu = u * u;
43552         var uuu = uu * u;
43553         var x = uuu * curve.startPoint.x;
43554         x += 3 * uu * t * curve.control1.x;
43555         x += 3 * u * tt * curve.control2.x;
43556         x += ttt * curve.endPoint.x;
43557         var y = uuu * curve.startPoint.y;
43558         y += 3 * uu * t * curve.control1.y;
43559         y += 3 * u * tt * curve.control2.y;
43560         y += ttt * curve.endPoint.y;
43561         var width = curve.startWidth + ttt * widthDelta;
43562         this.drawCurveSegment(x, y, width);
43563         }
43564         ctx.closePath();
43565         ctx.fill();
43566     },
43567     
43568     drawCurveSegment: function (x, y, width) {
43569         var ctx = this.canvasElCtx();
43570         ctx.moveTo(x, y);
43571         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
43572         this.is_empty = false;
43573     },
43574     
43575     clear: function()
43576     {
43577         var ctx = this.canvasElCtx();
43578         var canvas = this.canvasEl().dom;
43579         ctx.fillStyle = this.bg_color;
43580         ctx.clearRect(0, 0, canvas.width, canvas.height);
43581         ctx.fillRect(0, 0, canvas.width, canvas.height);
43582         this.curve_data = [];
43583         this.reset();
43584         this.is_empty = true;
43585     },
43586     
43587     fileEl: function()
43588     {
43589         return  this.el.select('input',true).first();
43590     },
43591     
43592     canvasEl: function()
43593     {
43594         return this.el.select('canvas',true).first();
43595     },
43596     
43597     canvasElCtx: function()
43598     {
43599         return this.el.select('canvas',true).first().dom.getContext('2d');
43600     },
43601     
43602     getImage: function(type)
43603     {
43604         if(this.is_empty) {
43605             return false;
43606         }
43607         
43608         // encryption ?
43609         return this.canvasEl().dom.toDataURL('image/'+type, 1);
43610     },
43611     
43612     drawFromImage: function(img_src)
43613     {
43614         var img = new Image();
43615         
43616         img.onload = function(){
43617             this.canvasElCtx().drawImage(img, 0, 0);
43618         }.bind(this);
43619         
43620         img.src = img_src;
43621         
43622         this.is_empty = false;
43623     },
43624     
43625     selectImage: function()
43626     {
43627         this.fileEl().dom.click();
43628     },
43629     
43630     uploadImage: function(e)
43631     {
43632         var reader = new FileReader();
43633         
43634         reader.onload = function(e){
43635             var img = new Image();
43636             img.onload = function(){
43637                 this.reset();
43638                 this.canvasElCtx().drawImage(img, 0, 0);
43639             }.bind(this);
43640             img.src = e.target.result;
43641         }.bind(this);
43642         
43643         reader.readAsDataURL(e.target.files[0]);
43644     },
43645     
43646     // Bezier Point Constructor
43647     Point: (function () {
43648         function Point(x, y, time) {
43649             this.x = x;
43650             this.y = y;
43651             this.time = time || Date.now();
43652         }
43653         Point.prototype.distanceTo = function (start) {
43654             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
43655         };
43656         Point.prototype.equals = function (other) {
43657             return this.x === other.x && this.y === other.y && this.time === other.time;
43658         };
43659         Point.prototype.velocityFrom = function (start) {
43660             return this.time !== start.time
43661             ? this.distanceTo(start) / (this.time - start.time)
43662             : 0;
43663         };
43664         return Point;
43665     }()),
43666     
43667     
43668     // Bezier Constructor
43669     Bezier: (function () {
43670         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
43671             this.startPoint = startPoint;
43672             this.control2 = control2;
43673             this.control1 = control1;
43674             this.endPoint = endPoint;
43675             this.startWidth = startWidth;
43676             this.endWidth = endWidth;
43677         }
43678         Bezier.fromPoints = function (points, widths, scope) {
43679             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
43680             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
43681             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
43682         };
43683         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
43684             var dx1 = s1.x - s2.x;
43685             var dy1 = s1.y - s2.y;
43686             var dx2 = s2.x - s3.x;
43687             var dy2 = s2.y - s3.y;
43688             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
43689             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
43690             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
43691             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
43692             var dxm = m1.x - m2.x;
43693             var dym = m1.y - m2.y;
43694             var k = l2 / (l1 + l2);
43695             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
43696             var tx = s2.x - cm.x;
43697             var ty = s2.y - cm.y;
43698             return {
43699                 c1: new scope.Point(m1.x + tx, m1.y + ty),
43700                 c2: new scope.Point(m2.x + tx, m2.y + ty)
43701             };
43702         };
43703         Bezier.prototype.length = function () {
43704             var steps = 10;
43705             var length = 0;
43706             var px;
43707             var py;
43708             for (var i = 0; i <= steps; i += 1) {
43709                 var t = i / steps;
43710                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
43711                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
43712                 if (i > 0) {
43713                     var xdiff = cx - px;
43714                     var ydiff = cy - py;
43715                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
43716                 }
43717                 px = cx;
43718                 py = cy;
43719             }
43720             return length;
43721         };
43722         Bezier.prototype.point = function (t, start, c1, c2, end) {
43723             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
43724             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
43725             + (3.0 * c2 * (1.0 - t) * t * t)
43726             + (end * t * t * t);
43727         };
43728         return Bezier;
43729     }()),
43730     
43731     throttleStroke: function(fn, wait) {
43732       if (wait === void 0) { wait = 250; }
43733       var previous = 0;
43734       var timeout = null;
43735       var result;
43736       var storedContext;
43737       var storedArgs;
43738       var later = function () {
43739           previous = Date.now();
43740           timeout = null;
43741           result = fn.apply(storedContext, storedArgs);
43742           if (!timeout) {
43743               storedContext = null;
43744               storedArgs = [];
43745           }
43746       };
43747       return function wrapper() {
43748           var args = [];
43749           for (var _i = 0; _i < arguments.length; _i++) {
43750               args[_i] = arguments[_i];
43751           }
43752           var now = Date.now();
43753           var remaining = wait - (now - previous);
43754           storedContext = this;
43755           storedArgs = args;
43756           if (remaining <= 0 || remaining > wait) {
43757               if (timeout) {
43758                   clearTimeout(timeout);
43759                   timeout = null;
43760               }
43761               previous = now;
43762               result = fn.apply(storedContext, storedArgs);
43763               if (!timeout) {
43764                   storedContext = null;
43765                   storedArgs = [];
43766               }
43767           }
43768           else if (!timeout) {
43769               timeout = window.setTimeout(later, remaining);
43770           }
43771           return result;
43772       };
43773   }
43774   
43775 });
43776
43777  
43778
43779