fix enums
[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             
1074             if (['default', 'secondary' , 'primary', 'success', 'info', 'warning', 'danger', 'link'].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 (['default', 'primary', 'secondary', 'success', 'info', 'warning', 'danger', 'link'].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 (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].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.weightClass );
1358         this.weight = str;
1359         var outline = this.outline ? 'outline-' : '';
1360         if (str == 'default') {
1361             this.el.addClass('btn-default btn-outline-secondary');        
1362             return;
1363         }
1364         this.el.addClass('btn-' + outline + str);        
1365     }
1366     
1367     
1368 });
1369 // fixme - should include btn-outline-*
1370 Roo.bootstrap.Button.weightClass = [
1371                         
1372        "btn-default",
1373        "btn-outline-secondary",
1374        "btn-secondary",        
1375        "btn-primary", 
1376        "btn-success", 
1377        "btn-info", 
1378        "btn-warning",
1379        "btn-danger",
1380        "btn-link",
1381        'btn-light',
1382        'btn-dark'
1383 ];/*
1384  * - LGPL
1385  *
1386  * column
1387  * 
1388  */
1389
1390 /**
1391  * @class Roo.bootstrap.Column
1392  * @extends Roo.bootstrap.Component
1393  * Bootstrap Column class
1394  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1395  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1396  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1397  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1398  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1399  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1400  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1401  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1402  *
1403  * 
1404  * @cfg {Boolean} hidden (true|false) hide the element
1405  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1406  * @cfg {String} fa (ban|check|...) font awesome icon
1407  * @cfg {Number} fasize (1|2|....) font awsome size
1408
1409  * @cfg {String} icon (info-sign|check|...) glyphicon name
1410
1411  * @cfg {String} html content of column.
1412  * 
1413  * @constructor
1414  * Create a new Column
1415  * @param {Object} config The config object
1416  */
1417
1418 Roo.bootstrap.Column = function(config){
1419     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1420 };
1421
1422 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1423     
1424     xs: false,
1425     sm: false,
1426     md: false,
1427     lg: false,
1428     xsoff: false,
1429     smoff: false,
1430     mdoff: false,
1431     lgoff: false,
1432     html: '',
1433     offset: 0,
1434     alert: false,
1435     fa: false,
1436     icon : false,
1437     hidden : false,
1438     fasize : 1,
1439     
1440     getAutoCreate : function(){
1441         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1442         
1443         cfg = {
1444             tag: 'div',
1445             cls: 'column'
1446         };
1447         
1448         var settings=this;
1449         var sizes =   ['xs','sm','md','lg'];
1450         sizes.map(function(size ,ix){
1451             //Roo.log( size + ':' + settings[size]);
1452             
1453             if (settings[size+'off'] !== false) {
1454                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1455             }
1456             
1457             if (settings[size] === false) {
1458                 return;
1459             }
1460             
1461             if (!settings[size]) { // 0 = hidden
1462                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1463                 // bootsrap4
1464                 for (var i = ix; i > -1; i--) {
1465                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1466                 }
1467                 
1468                 
1469                 return;
1470             }
1471             cfg.cls += ' col-' + size + '-' + settings[size] + (
1472                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1473             );
1474             
1475         });
1476         
1477         if (this.hidden) {
1478             cfg.cls += ' hidden';
1479         }
1480         
1481         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1482             cfg.cls +=' alert alert-' + this.alert;
1483         }
1484         
1485         
1486         if (this.html.length) {
1487             cfg.html = this.html;
1488         }
1489         if (this.fa) {
1490             var fasize = '';
1491             if (this.fasize > 1) {
1492                 fasize = ' fa-' + this.fasize + 'x';
1493             }
1494             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1495             
1496             
1497         }
1498         if (this.icon) {
1499             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1500         }
1501         
1502         return cfg;
1503     }
1504    
1505 });
1506
1507  
1508
1509  /*
1510  * - LGPL
1511  *
1512  * page container.
1513  * 
1514  */
1515
1516
1517 /**
1518  * @class Roo.bootstrap.Container
1519  * @extends Roo.bootstrap.Component
1520  * Bootstrap Container class
1521  * @cfg {Boolean} jumbotron is it a jumbotron element
1522  * @cfg {String} html content of element
1523  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1524  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1525  * @cfg {String} header content of header (for panel)
1526  * @cfg {String} footer content of footer (for panel)
1527  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1528  * @cfg {String} tag (header|aside|section) type of HTML tag.
1529  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1530  * @cfg {String} fa font awesome icon
1531  * @cfg {String} icon (info-sign|check|...) glyphicon name
1532  * @cfg {Boolean} hidden (true|false) hide the element
1533  * @cfg {Boolean} expandable (true|false) default false
1534  * @cfg {Boolean} expanded (true|false) default true
1535  * @cfg {String} rheader contet on the right of header
1536  * @cfg {Boolean} clickable (true|false) default false
1537
1538  *     
1539  * @constructor
1540  * Create a new Container
1541  * @param {Object} config The config object
1542  */
1543
1544 Roo.bootstrap.Container = function(config){
1545     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1546     
1547     this.addEvents({
1548         // raw events
1549          /**
1550          * @event expand
1551          * After the panel has been expand
1552          * 
1553          * @param {Roo.bootstrap.Container} this
1554          */
1555         "expand" : true,
1556         /**
1557          * @event collapse
1558          * After the panel has been collapsed
1559          * 
1560          * @param {Roo.bootstrap.Container} this
1561          */
1562         "collapse" : true,
1563         /**
1564          * @event click
1565          * When a element is chick
1566          * @param {Roo.bootstrap.Container} this
1567          * @param {Roo.EventObject} e
1568          */
1569         "click" : true
1570     });
1571 };
1572
1573 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1574     
1575     jumbotron : false,
1576     well: '',
1577     panel : '',
1578     header: '',
1579     footer : '',
1580     sticky: '',
1581     tag : false,
1582     alert : false,
1583     fa: false,
1584     icon : false,
1585     expandable : false,
1586     rheader : '',
1587     expanded : true,
1588     clickable: false,
1589   
1590      
1591     getChildContainer : function() {
1592         
1593         if(!this.el){
1594             return false;
1595         }
1596         
1597         if (this.panel.length) {
1598             return this.el.select('.panel-body',true).first();
1599         }
1600         
1601         return this.el;
1602     },
1603     
1604     
1605     getAutoCreate : function(){
1606         
1607         var cfg = {
1608             tag : this.tag || 'div',
1609             html : '',
1610             cls : ''
1611         };
1612         if (this.jumbotron) {
1613             cfg.cls = 'jumbotron';
1614         }
1615         
1616         
1617         
1618         // - this is applied by the parent..
1619         //if (this.cls) {
1620         //    cfg.cls = this.cls + '';
1621         //}
1622         
1623         if (this.sticky.length) {
1624             
1625             var bd = Roo.get(document.body);
1626             if (!bd.hasClass('bootstrap-sticky')) {
1627                 bd.addClass('bootstrap-sticky');
1628                 Roo.select('html',true).setStyle('height', '100%');
1629             }
1630              
1631             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1632         }
1633         
1634         
1635         if (this.well.length) {
1636             switch (this.well) {
1637                 case 'lg':
1638                 case 'sm':
1639                     cfg.cls +=' well well-' +this.well;
1640                     break;
1641                 default:
1642                     cfg.cls +=' well';
1643                     break;
1644             }
1645         }
1646         
1647         if (this.hidden) {
1648             cfg.cls += ' hidden';
1649         }
1650         
1651         
1652         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1653             cfg.cls +=' alert alert-' + this.alert;
1654         }
1655         
1656         var body = cfg;
1657         
1658         if (this.panel.length) {
1659             cfg.cls += ' panel panel-' + this.panel;
1660             cfg.cn = [];
1661             if (this.header.length) {
1662                 
1663                 var h = [];
1664                 
1665                 if(this.expandable){
1666                     
1667                     cfg.cls = cfg.cls + ' expandable';
1668                     
1669                     h.push({
1670                         tag: 'i',
1671                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1672                     });
1673                     
1674                 }
1675                 
1676                 h.push(
1677                     {
1678                         tag: 'span',
1679                         cls : 'panel-title',
1680                         html : (this.expandable ? '&nbsp;' : '') + this.header
1681                     },
1682                     {
1683                         tag: 'span',
1684                         cls: 'panel-header-right',
1685                         html: this.rheader
1686                     }
1687                 );
1688                 
1689                 cfg.cn.push({
1690                     cls : 'panel-heading',
1691                     style : this.expandable ? 'cursor: pointer' : '',
1692                     cn : h
1693                 });
1694                 
1695             }
1696             
1697             body = false;
1698             cfg.cn.push({
1699                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1700                 html : this.html
1701             });
1702             
1703             
1704             if (this.footer.length) {
1705                 cfg.cn.push({
1706                     cls : 'panel-footer',
1707                     html : this.footer
1708                     
1709                 });
1710             }
1711             
1712         }
1713         
1714         if (body) {
1715             body.html = this.html || cfg.html;
1716             // prefix with the icons..
1717             if (this.fa) {
1718                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1719             }
1720             if (this.icon) {
1721                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1722             }
1723             
1724             
1725         }
1726         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1727             cfg.cls =  'container';
1728         }
1729         
1730         return cfg;
1731     },
1732     
1733     initEvents: function() 
1734     {
1735         if(this.expandable){
1736             var headerEl = this.headerEl();
1737         
1738             if(headerEl){
1739                 headerEl.on('click', this.onToggleClick, this);
1740             }
1741         }
1742         
1743         if(this.clickable){
1744             this.el.on('click', this.onClick, this);
1745         }
1746         
1747     },
1748     
1749     onToggleClick : function()
1750     {
1751         var headerEl = this.headerEl();
1752         
1753         if(!headerEl){
1754             return;
1755         }
1756         
1757         if(this.expanded){
1758             this.collapse();
1759             return;
1760         }
1761         
1762         this.expand();
1763     },
1764     
1765     expand : function()
1766     {
1767         if(this.fireEvent('expand', this)) {
1768             
1769             this.expanded = true;
1770             
1771             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1772             
1773             this.el.select('.panel-body',true).first().removeClass('hide');
1774             
1775             var toggleEl = this.toggleEl();
1776
1777             if(!toggleEl){
1778                 return;
1779             }
1780
1781             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1782         }
1783         
1784     },
1785     
1786     collapse : function()
1787     {
1788         if(this.fireEvent('collapse', this)) {
1789             
1790             this.expanded = false;
1791             
1792             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1793             this.el.select('.panel-body',true).first().addClass('hide');
1794         
1795             var toggleEl = this.toggleEl();
1796
1797             if(!toggleEl){
1798                 return;
1799             }
1800
1801             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1802         }
1803     },
1804     
1805     toggleEl : function()
1806     {
1807         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1808             return;
1809         }
1810         
1811         return this.el.select('.panel-heading .fa',true).first();
1812     },
1813     
1814     headerEl : function()
1815     {
1816         if(!this.el || !this.panel.length || !this.header.length){
1817             return;
1818         }
1819         
1820         return this.el.select('.panel-heading',true).first()
1821     },
1822     
1823     bodyEl : function()
1824     {
1825         if(!this.el || !this.panel.length){
1826             return;
1827         }
1828         
1829         return this.el.select('.panel-body',true).first()
1830     },
1831     
1832     titleEl : function()
1833     {
1834         if(!this.el || !this.panel.length || !this.header.length){
1835             return;
1836         }
1837         
1838         return this.el.select('.panel-title',true).first();
1839     },
1840     
1841     setTitle : function(v)
1842     {
1843         var titleEl = this.titleEl();
1844         
1845         if(!titleEl){
1846             return;
1847         }
1848         
1849         titleEl.dom.innerHTML = v;
1850     },
1851     
1852     getTitle : function()
1853     {
1854         
1855         var titleEl = this.titleEl();
1856         
1857         if(!titleEl){
1858             return '';
1859         }
1860         
1861         return titleEl.dom.innerHTML;
1862     },
1863     
1864     setRightTitle : function(v)
1865     {
1866         var t = this.el.select('.panel-header-right',true).first();
1867         
1868         if(!t){
1869             return;
1870         }
1871         
1872         t.dom.innerHTML = v;
1873     },
1874     
1875     onClick : function(e)
1876     {
1877         e.preventDefault();
1878         
1879         this.fireEvent('click', this, e);
1880     }
1881 });
1882
1883  /*
1884  *  - LGPL
1885  *
1886  *  This is BS4's Card element.. - similar to our containers probably..
1887  * 
1888  */
1889 /**
1890  * @class Roo.bootstrap.Card
1891  * @extends Roo.bootstrap.Component
1892  * Bootstrap Card class
1893  *
1894  *
1895  * possible... may not be implemented..
1896  * @cfg {String} header_image  src url of image.
1897  * @cfg {String|Object} header
1898  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1899  * 
1900  * @cfg {String} title
1901  * @cfg {String} subtitle
1902  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1903  * @cfg {String} footer
1904  
1905  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1906  * 
1907  * @cfg {String} margin (0|1|2|3|4|5|auto)
1908  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1909  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1910  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1911  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1912  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1913  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1914  *
1915  * @cfg {String} padding (0|1|2|3|4|5)
1916  * @cfg {String} padding_top (0|1|2|3|4|5)
1917  * @cfg {String} padding_bottom (0|1|2|3|4|5)
1918  * @cfg {String} padding_left (0|1|2|3|4|5)
1919  * @cfg {String} padding_right (0|1|2|3|4|5)
1920  * @cfg {String} padding_x (0|1|2|3|4|5)
1921  * @cfg {String} padding_y (0|1|2|3|4|5)
1922  *
1923  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1924  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1925  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1926  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1927  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1928  
1929  * @config {Boolean} dragable  if this card can be dragged.
1930  * @config {String} drag_group  group for drag
1931  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
1932  * @config {String} drop_group  group for drag
1933  * 
1934  * @config {Boolean} collapsable can the body be collapsed.
1935  * @config {Boolean} collapsed is the body collapsed when rendered...
1936  * @config {Boolean} rotateable can the body be rotated by clicking on it..
1937  * @config {Boolean} rotated is the body rotated when rendered...
1938  * 
1939  * @constructor
1940  * Create a new Container
1941  * @param {Object} config The config object
1942  */
1943
1944 Roo.bootstrap.Card = function(config){
1945     Roo.bootstrap.Card.superclass.constructor.call(this, config);
1946     
1947     this.addEvents({
1948          // raw events
1949         /**
1950          * @event drop
1951          * When a element a card is dropped
1952          * @param {Roo.bootstrap.Element} this
1953          * @param {Roo.Element} n the node being dropped?
1954          * @param {Object} dd Drag and drop data
1955          * @param {Roo.EventObject} e
1956          * @param {Roo.EventObject} data  the data passed via getDragData
1957          */
1958         'drop' : true,
1959          /**
1960          * @event rotate
1961          * When a element a card is rotate
1962          * @param {Roo.bootstrap.Element} this
1963          * @param {Roo.Element} n the node being dropped?
1964          * @param {Boolean} rotate status
1965          */
1966         'rotate' : true
1967         
1968     });
1969 };
1970
1971
1972 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
1973     
1974     
1975     weight : '',
1976     
1977     margin: '', /// may be better in component?
1978     margin_top: '', 
1979     margin_bottom: '', 
1980     margin_left: '',
1981     margin_right: '',
1982     margin_x: '',
1983     margin_y: '',
1984     
1985     padding : '',
1986     padding_top: '', 
1987     padding_bottom: '', 
1988     padding_left: '',
1989     padding_right: '',
1990     padding_x: '',
1991     padding_y: '',
1992     
1993     display: '', 
1994     display_xs: '', 
1995     display_sm: '', 
1996     display_lg: '',
1997     display_xl: '',
1998  
1999     header_image  : '',
2000     header : '',
2001     header_size : 0,
2002     title : '',
2003     subtitle : '',
2004     html : '',
2005     footer: '',
2006
2007     collapsable : false,
2008     collapsed : false,
2009     rotateable : false,
2010     rotated : false,
2011     
2012     dragable : false,
2013     drag_group : false,
2014     dropable : false,
2015     drop_group : false,
2016     childContainer : false,
2017     dropEl : false, /// the dom placeholde element that indicates drop location.
2018     
2019     layoutCls : function()
2020     {
2021         var cls = '';
2022         var t = this;
2023         Roo.log(this.margin_bottom.length);
2024         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2025             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2026             
2027             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2028                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2029             }
2030             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2031                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2032             }
2033         });
2034         
2035         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2036             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2037                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['margin' + (v.length ? '_' : '') + v]
2038             }
2039         });
2040         
2041         // more generic support?
2042         if (this.hidden) {
2043             cls += ' d-none';
2044         }
2045         
2046         return cls;
2047     },
2048  
2049        // Roo.log("Call onRender: " + this.xtype);
2050         /*  We are looking at something like this.
2051 <div class="card">
2052     <img src="..." class="card-img-top" alt="...">
2053     <div class="card-body">
2054         <h5 class="card-title">Card title</h5>
2055          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2056
2057         >> this bit is really the body...
2058         <div> << we will ad dthis in hopefully it will not break shit.
2059         
2060         ** card text does not actually have any styling...
2061         
2062             <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>
2063         
2064         </div> <<
2065           <a href="#" class="card-link">Card link</a>
2066           
2067     </div>
2068     <div class="card-footer">
2069         <small class="text-muted">Last updated 3 mins ago</small>
2070     </div>
2071 </div>
2072          */
2073     getAutoCreate : function(){
2074         
2075         var cfg = {
2076             tag : 'div',
2077             cls : 'card',
2078             cn : [ ]
2079         };
2080         
2081         if (this.weight.length && this.weight != 'light') {
2082             cfg.cls += ' text-white';
2083         } else {
2084             cfg.cls += ' text-dark'; // need as it's nested..
2085         }
2086         if (this.weight.length) {
2087             cfg.cls += ' bg-' + this.weight;
2088         }
2089         
2090         cfg.cls += this.layoutCls(); 
2091         
2092         var hdr = false;
2093         if (this.header.length) {
2094             hdr = {
2095                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2096                 cls : 'card-header',
2097                 cn : []
2098             };
2099             cfg.cn.push(hdr);
2100             hdr_ctr = hdr;
2101         } else {
2102             hdr = {
2103                 tag : 'div',
2104                 cls : 'card-header d-none',
2105                 cn : []
2106             };
2107             cfg.cn.push(hdr);
2108         }
2109         if (this.collapsable) {
2110             hdr_ctr = {
2111             tag : 'a',
2112             cls : 'd-block user-select-none',
2113             cn: [
2114                     {
2115                         tag: 'i',
2116                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2117                     }
2118                    
2119                 ]
2120             };
2121             hdr.cn.push(hdr_ctr);
2122         }
2123         
2124         hdr_ctr.cn.push(        {
2125             tag: 'span',
2126             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2127             html : this.header
2128         });
2129         
2130         
2131         if (this.header_image.length) {
2132             cfg.cn.push({
2133                 tag : 'img',
2134                 cls : 'card-img-top',
2135                 src: this.header_image // escape?
2136             });
2137         } else {
2138             cfg.cn.push({
2139                     tag : 'div',
2140                     cls : 'card-img-top d-none' 
2141                 });
2142         }
2143             
2144         var body = {
2145             tag : 'div',
2146             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2147             cn : []
2148         };
2149         var obody = body;
2150         if (this.collapsable || this.rotateable) {
2151             obody = {
2152                 tag: 'div',
2153                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2154                 cn : [  body ]
2155             };
2156         }
2157         
2158         cfg.cn.push(obody);
2159         
2160         if (this.title.length) {
2161             body.cn.push({
2162                 tag : 'div',
2163                 cls : 'card-title',
2164                 src: this.title // escape?
2165             });
2166         }  
2167         
2168         if (this.subtitle.length) {
2169             body.cn.push({
2170                 tag : 'div',
2171                 cls : 'card-title',
2172                 src: this.subtitle // escape?
2173             });
2174         }
2175         
2176         body.cn.push({
2177             tag : 'div',
2178             cls : 'roo-card-body-ctr'
2179         });
2180         
2181         if (this.html.length) {
2182             body.cn.push({
2183                 tag: 'div',
2184                 html : this.html
2185             });
2186         }
2187         // fixme ? handle objects?
2188         
2189         if (this.footer.length) {
2190            
2191             cfg.cn.push({
2192                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2193                 html : this.footer
2194             });
2195             
2196         } else {
2197             cfg.cn.push({cls : 'card-footer d-none'});
2198         }
2199         
2200         // footer...
2201         
2202         return cfg;
2203     },
2204     
2205     
2206     getCardHeader : function()
2207     {
2208         var  ret = this.el.select('.card-header',true).first();
2209         if (ret.hasClass('d-none')) {
2210             ret.removeClass('d-none');
2211         }
2212         
2213         return ret;
2214     },
2215     getCardFooter : function()
2216     {
2217         var  ret = this.el.select('.card-footer',true).first();
2218         if (ret.hasClass('d-none')) {
2219             ret.removeClass('d-none');
2220         }
2221         
2222         return ret;
2223     },
2224     getCardImageTop : function()
2225     {
2226         var  ret = this.el.select('.card-img-top',true).first();
2227         if (ret.hasClass('d-none')) {
2228             ret.removeClass('d-none');
2229         }
2230             
2231         return ret;
2232     },
2233     
2234     getChildContainer : function()
2235     {
2236         
2237         if(!this.el){
2238             return false;
2239         }
2240         return this.el.select('.roo-card-body-ctr',true).first();    
2241     },
2242     
2243     initEvents: function() 
2244     {
2245         
2246         this.bodyEl = this.getChildContainer();
2247         if(this.dragable){
2248             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2249                     containerScroll: true,
2250                     ddGroup: this.drag_group || 'default_card_drag_group'
2251             });
2252             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2253         }
2254         if (this.dropable) {
2255             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2256                 containerScroll: true,
2257                 ddGroup: this.drop_group || 'default_card_drag_group'
2258             });
2259             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2260             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2261             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2262             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2263             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2264         }
2265         
2266         if (this.collapsable) {
2267             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2268         }
2269         if (this.rotateable) {
2270             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2271         }
2272         this.collapsableEl = this.el.select('.roo-collapsable').first();
2273          
2274         this.footerEl = this.el.select('.card-footer').first();
2275         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle');
2276         this.headerEl = this.el.select('.roo-card-header-ctr').first();
2277         
2278         if (this.rotated) {
2279             this.el.addClass('roo-card-rotated');
2280             this.fireEvent('rotate', this, true);
2281         }
2282         
2283     },
2284     getDragData : function(e)
2285     {
2286         var target = this.getEl();
2287         if (target) {
2288             //this.handleSelection(e);
2289             
2290             var dragData = {
2291                 source: this,
2292                 copy: false,
2293                 nodes: this.getEl(),
2294                 records: []
2295             };
2296             
2297             
2298             dragData.ddel = target.dom ;    // the div element
2299             Roo.log(target.getWidth( ));
2300             dragData.ddel.style.width = target.getWidth() + 'px';
2301             
2302             return dragData;
2303         }
2304         return false;
2305     },
2306     /**
2307     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2308     *    whole Element becomes the target, and this causes the drop gesture to append.
2309     */
2310     getTargetFromEvent : function(e, dragged_card_el)
2311     {
2312         var target = e.getTarget();
2313         while ((target !== null) && (target.parentNode != this.bodyEl.dom)) {
2314             target = target.parentNode;
2315         }
2316         
2317         var ret = {
2318             position: '',
2319             cards : [],
2320             card_n : -1,
2321             items_n : -1,
2322             card : false 
2323         };
2324         
2325         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2326         // see if target is one of the 'cards'...
2327         
2328         
2329         //Roo.log(this.items.length);
2330         var pos = false;
2331         
2332         var last_card_n = 0;
2333         var cards_len  = 0;
2334         for (var i = 0;i< this.items.length;i++) {
2335             
2336             if (!this.items[i].el.hasClass('card')) {
2337                  continue;
2338             }
2339             pos = this.getDropPoint(e, this.items[i].el.dom);
2340             
2341             cards_len = ret.cards.length;
2342             //Roo.log(this.items[i].el.dom.id);
2343             ret.cards.push(this.items[i]);
2344             last_card_n  = i;
2345             if (ret.card_n < 0 && pos == 'above') {
2346                 ret.position = cards_len > 0 ? 'below' : pos;
2347                 ret.items_n = i > 0 ? i - 1 : 0;
2348                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2349                 ret.card = ret.cards[ret.card_n];
2350             }
2351         }
2352         if (!ret.cards.length) {
2353             ret.card = true;
2354             ret.position = 'below';
2355             ret.items_n;
2356             return ret;
2357         }
2358         // could not find a card.. stick it at the end..
2359         if (ret.card_n < 0) {
2360             ret.card_n = last_card_n;
2361             ret.card = ret.cards[last_card_n];
2362             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2363             ret.position = 'below';
2364         }
2365         
2366         if (this.items[ret.items_n].el == dragged_card_el) {
2367             return false;
2368         }
2369         
2370         if (ret.position == 'below') {
2371             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2372             
2373             if (card_after  && card_after.el == dragged_card_el) {
2374                 return false;
2375             }
2376             return ret;
2377         }
2378         
2379         // its's after ..
2380         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2381         
2382         if (card_before  && card_before.el == dragged_card_el) {
2383             return false;
2384         }
2385         
2386         return ret;
2387     },
2388     
2389     onNodeEnter : function(n, dd, e, data){
2390         return false;
2391     },
2392     onNodeOver : function(n, dd, e, data)
2393     {
2394        
2395         var target_info = this.getTargetFromEvent(e,data.source.el);
2396         if (target_info === false) {
2397             this.dropPlaceHolder('hide');
2398             return false;
2399         }
2400         Roo.log(['getTargetFromEvent', target_info ]);
2401         
2402          
2403         this.dropPlaceHolder('show', target_info,data);
2404         
2405         return false; 
2406     },
2407     onNodeOut : function(n, dd, e, data){
2408         this.dropPlaceHolder('hide');
2409      
2410     },
2411     onNodeDrop : function(n, dd, e, data)
2412     {
2413         
2414         // call drop - return false if
2415         
2416         // this could actually fail - if the Network drops..
2417         // we will ignore this at present..- client should probably reload
2418         // the whole set of cards if stuff like that fails.
2419         
2420         
2421         var info = this.getTargetFromEvent(e,data.source.el);
2422         if (info === false) {
2423             return false;
2424         }
2425         
2426         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
2427             return false;
2428         }
2429          
2430         this.dropPlaceHolder('hide');
2431         
2432         // do the dom manipulation first..
2433         var dom = data.source.el.dom;
2434         dom.parentNode.removeChild(dom);
2435         
2436         
2437         if (info.card !== true) {
2438             var cardel = info.card.el.dom;
2439             
2440             if (info.position == 'above') {
2441                 cardel.parentNode.insertBefore(dom, cardel);
2442             } else if (cardel.nextSibling) {
2443                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2444             } else {
2445                 cardel.parentNode.append(dom);
2446             }
2447         } else {
2448             // card container???
2449             this.bodyEl.dom.append(dom);
2450         }
2451         
2452         //FIXME HANDLE card = true 
2453         
2454         // add this to the correct place in items.
2455         
2456         
2457         
2458         // remove Card from items.
2459         
2460         var old_parent = data.source.parent();
2461         
2462         old_parent.items = old_parent.items.filter(function(e) { return e != data.source });
2463         
2464         if (this.items.length) {
2465             var nitems = [];
2466             //Roo.log([info.items_n, info.position, this.items.length]);
2467             for (var i =0; i < this.items.length; i++) {
2468                 if (i == info.items_n && info.position == 'above') {
2469                     nitems.push(data.source);
2470                 }
2471                 nitems.push(this.items[i]);
2472                 if (i == info.items_n && info.position == 'below') {
2473                     nitems.push(data.source);
2474                 }
2475             }
2476             this.items = nitems;
2477             Roo.log(this.items);
2478         } else {
2479             this.items.push(data.source);
2480         }
2481         
2482         data.source.parentId = this.id;
2483         
2484         return true;
2485     },
2486     
2487     /**    Decide whether to drop above or below a View node. */
2488     getDropPoint : function(e, n, dd)
2489     {
2490         if (dd) {
2491              return false;
2492         }
2493         if (n == this.bodyEl.dom) {
2494             return "above";
2495         }
2496         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2497         var c = t + (b - t) / 2;
2498         var y = Roo.lib.Event.getPageY(e);
2499         if(y <= c) {
2500             return "above";
2501         }else{
2502             return "below";
2503         }
2504     },
2505     onToggleCollapse : function(e)
2506         {
2507         if (this.collapsed) {
2508             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2509             this.collapsableEl.addClass('show');
2510             this.collapsed = false;
2511             return;
2512         }
2513         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2514         this.collapsableEl.removeClass('show');
2515         this.collapsed = true;
2516         
2517     
2518     },
2519     
2520     onToggleRotate : function(e)
2521     {
2522         this.collapsableEl.removeClass('show');
2523         this.footerEl.removeClass('d-none');
2524         this.el.removeClass('roo-card-rotated');
2525         this.el.removeClass('d-none');
2526         if (this.rotated) {
2527             
2528             this.collapsableEl.addClass('show');
2529             this.rotated = false;
2530             this.fireEvent('rotate', this, this.rotated);
2531             return;
2532         }
2533         this.el.addClass('roo-card-rotated');
2534         this.footerEl.addClass('d-none');
2535         this.el.select('.roo-collapsable').removeClass('show');
2536         
2537         this.rotated = true;
2538         this.fireEvent('rotate', this, this.rotated);
2539     
2540     },
2541     
2542     dropPlaceHolder: function (action, info, data)
2543     {
2544         if (this.dropEl === false) {
2545             this.dropEl = Roo.DomHelper.append(this.bodyEl, {
2546             cls : 'd-none'
2547             },true);
2548         }
2549         this.dropEl.removeClass(['d-none', 'd-block']);        
2550         if (action == 'hide') {
2551             
2552             this.dropEl.addClass('d-none');
2553             return;
2554         }
2555         // FIXME - info.card == true!!!
2556         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2557         
2558         if (info.card !== true) {
2559             var cardel = info.card.el.dom;
2560             
2561             if (info.position == 'above') {
2562                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2563             } else if (cardel.nextSibling) {
2564                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2565             } else {
2566                 cardel.parentNode.append(this.dropEl.dom);
2567             }
2568         } else {
2569             // card container???
2570             this.bodyEl.dom.append(this.dropEl.dom);
2571         }
2572         
2573         this.dropEl.addClass('d-block roo-card-dropzone');
2574         
2575         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2576         
2577         
2578     
2579     
2580     
2581     },
2582     setHeaderText: function(html)
2583     {
2584         this.headerEl.dom.innerHTML = html;
2585     }
2586
2587     
2588 });
2589
2590 /*
2591  * - LGPL
2592  *
2593  * Card header - holder for the card header elements.
2594  * 
2595  */
2596
2597 /**
2598  * @class Roo.bootstrap.CardHeader
2599  * @extends Roo.bootstrap.Element
2600  * Bootstrap CardHeader class
2601  * @constructor
2602  * Create a new Card Header - that you can embed children into
2603  * @param {Object} config The config object
2604  */
2605
2606 Roo.bootstrap.CardHeader = function(config){
2607     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2608 };
2609
2610 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2611     
2612     
2613     container_method : 'getCardHeader' 
2614     
2615      
2616     
2617     
2618    
2619 });
2620
2621  
2622
2623  /*
2624  * - LGPL
2625  *
2626  * Card footer - holder for the card footer elements.
2627  * 
2628  */
2629
2630 /**
2631  * @class Roo.bootstrap.CardFooter
2632  * @extends Roo.bootstrap.Element
2633  * Bootstrap CardFooter class
2634  * @constructor
2635  * Create a new Card Footer - that you can embed children into
2636  * @param {Object} config The config object
2637  */
2638
2639 Roo.bootstrap.CardFooter = function(config){
2640     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2641 };
2642
2643 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2644     
2645     
2646     container_method : 'getCardFooter' 
2647     
2648      
2649     
2650     
2651    
2652 });
2653
2654  
2655
2656  /*
2657  * - LGPL
2658  *
2659  * Card header - holder for the card header elements.
2660  * 
2661  */
2662
2663 /**
2664  * @class Roo.bootstrap.CardImageTop
2665  * @extends Roo.bootstrap.Element
2666  * Bootstrap CardImageTop class
2667  * @constructor
2668  * Create a new Card Image Top container
2669  * @param {Object} config The config object
2670  */
2671
2672 Roo.bootstrap.CardImageTop = function(config){
2673     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2674 };
2675
2676 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2677     
2678    
2679     container_method : 'getCardImageTop' 
2680     
2681      
2682     
2683    
2684 });
2685
2686  
2687
2688  /*
2689  * - LGPL
2690  *
2691  * image
2692  * 
2693  */
2694
2695
2696 /**
2697  * @class Roo.bootstrap.Img
2698  * @extends Roo.bootstrap.Component
2699  * Bootstrap Img class
2700  * @cfg {Boolean} imgResponsive false | true
2701  * @cfg {String} border rounded | circle | thumbnail
2702  * @cfg {String} src image source
2703  * @cfg {String} alt image alternative text
2704  * @cfg {String} href a tag href
2705  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2706  * @cfg {String} xsUrl xs image source
2707  * @cfg {String} smUrl sm image source
2708  * @cfg {String} mdUrl md image source
2709  * @cfg {String} lgUrl lg image source
2710  * 
2711  * @constructor
2712  * Create a new Input
2713  * @param {Object} config The config object
2714  */
2715
2716 Roo.bootstrap.Img = function(config){
2717     Roo.bootstrap.Img.superclass.constructor.call(this, config);
2718     
2719     this.addEvents({
2720         // img events
2721         /**
2722          * @event click
2723          * The img click event for the img.
2724          * @param {Roo.EventObject} e
2725          */
2726         "click" : true
2727     });
2728 };
2729
2730 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
2731     
2732     imgResponsive: true,
2733     border: '',
2734     src: 'about:blank',
2735     href: false,
2736     target: false,
2737     xsUrl: '',
2738     smUrl: '',
2739     mdUrl: '',
2740     lgUrl: '',
2741
2742     getAutoCreate : function()
2743     {   
2744         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2745             return this.createSingleImg();
2746         }
2747         
2748         var cfg = {
2749             tag: 'div',
2750             cls: 'roo-image-responsive-group',
2751             cn: []
2752         };
2753         var _this = this;
2754         
2755         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2756             
2757             if(!_this[size + 'Url']){
2758                 return;
2759             }
2760             
2761             var img = {
2762                 tag: 'img',
2763                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2764                 html: _this.html || cfg.html,
2765                 src: _this[size + 'Url']
2766             };
2767             
2768             img.cls += ' roo-image-responsive-' + size;
2769             
2770             var s = ['xs', 'sm', 'md', 'lg'];
2771             
2772             s.splice(s.indexOf(size), 1);
2773             
2774             Roo.each(s, function(ss){
2775                 img.cls += ' hidden-' + ss;
2776             });
2777             
2778             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2779                 cfg.cls += ' img-' + _this.border;
2780             }
2781             
2782             if(_this.alt){
2783                 cfg.alt = _this.alt;
2784             }
2785             
2786             if(_this.href){
2787                 var a = {
2788                     tag: 'a',
2789                     href: _this.href,
2790                     cn: [
2791                         img
2792                     ]
2793                 };
2794
2795                 if(this.target){
2796                     a.target = _this.target;
2797                 }
2798             }
2799             
2800             cfg.cn.push((_this.href) ? a : img);
2801             
2802         });
2803         
2804         return cfg;
2805     },
2806     
2807     createSingleImg : function()
2808     {
2809         var cfg = {
2810             tag: 'img',
2811             cls: (this.imgResponsive) ? 'img-responsive' : '',
2812             html : null,
2813             src : 'about:blank'  // just incase src get's set to undefined?!?
2814         };
2815         
2816         cfg.html = this.html || cfg.html;
2817         
2818         cfg.src = this.src || cfg.src;
2819         
2820         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2821             cfg.cls += ' img-' + this.border;
2822         }
2823         
2824         if(this.alt){
2825             cfg.alt = this.alt;
2826         }
2827         
2828         if(this.href){
2829             var a = {
2830                 tag: 'a',
2831                 href: this.href,
2832                 cn: [
2833                     cfg
2834                 ]
2835             };
2836             
2837             if(this.target){
2838                 a.target = this.target;
2839             }
2840             
2841         }
2842         
2843         return (this.href) ? a : cfg;
2844     },
2845     
2846     initEvents: function() 
2847     {
2848         if(!this.href){
2849             this.el.on('click', this.onClick, this);
2850         }
2851         
2852     },
2853     
2854     onClick : function(e)
2855     {
2856         Roo.log('img onclick');
2857         this.fireEvent('click', this, e);
2858     },
2859     /**
2860      * Sets the url of the image - used to update it
2861      * @param {String} url the url of the image
2862      */
2863     
2864     setSrc : function(url)
2865     {
2866         this.src =  url;
2867         
2868         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2869             this.el.dom.src =  url;
2870             return;
2871         }
2872         
2873         this.el.select('img', true).first().dom.src =  url;
2874     }
2875     
2876     
2877    
2878 });
2879
2880  /*
2881  * - LGPL
2882  *
2883  * image
2884  * 
2885  */
2886
2887
2888 /**
2889  * @class Roo.bootstrap.Link
2890  * @extends Roo.bootstrap.Component
2891  * Bootstrap Link Class
2892  * @cfg {String} alt image alternative text
2893  * @cfg {String} href a tag href
2894  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
2895  * @cfg {String} html the content of the link.
2896  * @cfg {String} anchor name for the anchor link
2897  * @cfg {String} fa - favicon
2898
2899  * @cfg {Boolean} preventDefault (true | false) default false
2900
2901  * 
2902  * @constructor
2903  * Create a new Input
2904  * @param {Object} config The config object
2905  */
2906
2907 Roo.bootstrap.Link = function(config){
2908     Roo.bootstrap.Link.superclass.constructor.call(this, config);
2909     
2910     this.addEvents({
2911         // img events
2912         /**
2913          * @event click
2914          * The img click event for the img.
2915          * @param {Roo.EventObject} e
2916          */
2917         "click" : true
2918     });
2919 };
2920
2921 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
2922     
2923     href: false,
2924     target: false,
2925     preventDefault: false,
2926     anchor : false,
2927     alt : false,
2928     fa: false,
2929
2930
2931     getAutoCreate : function()
2932     {
2933         var html = this.html || '';
2934         
2935         if (this.fa !== false) {
2936             html = '<i class="fa fa-' + this.fa + '"></i>';
2937         }
2938         var cfg = {
2939             tag: 'a'
2940         };
2941         // anchor's do not require html/href...
2942         if (this.anchor === false) {
2943             cfg.html = html;
2944             cfg.href = this.href || '#';
2945         } else {
2946             cfg.name = this.anchor;
2947             if (this.html !== false || this.fa !== false) {
2948                 cfg.html = html;
2949             }
2950             if (this.href !== false) {
2951                 cfg.href = this.href;
2952             }
2953         }
2954         
2955         if(this.alt !== false){
2956             cfg.alt = this.alt;
2957         }
2958         
2959         
2960         if(this.target !== false) {
2961             cfg.target = this.target;
2962         }
2963         
2964         return cfg;
2965     },
2966     
2967     initEvents: function() {
2968         
2969         if(!this.href || this.preventDefault){
2970             this.el.on('click', this.onClick, this);
2971         }
2972     },
2973     
2974     onClick : function(e)
2975     {
2976         if(this.preventDefault){
2977             e.preventDefault();
2978         }
2979         //Roo.log('img onclick');
2980         this.fireEvent('click', this, e);
2981     }
2982    
2983 });
2984
2985  /*
2986  * - LGPL
2987  *
2988  * header
2989  * 
2990  */
2991
2992 /**
2993  * @class Roo.bootstrap.Header
2994  * @extends Roo.bootstrap.Component
2995  * Bootstrap Header class
2996  * @cfg {String} html content of header
2997  * @cfg {Number} level (1|2|3|4|5|6) default 1
2998  * 
2999  * @constructor
3000  * Create a new Header
3001  * @param {Object} config The config object
3002  */
3003
3004
3005 Roo.bootstrap.Header  = function(config){
3006     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3007 };
3008
3009 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3010     
3011     //href : false,
3012     html : false,
3013     level : 1,
3014     
3015     
3016     
3017     getAutoCreate : function(){
3018         
3019         
3020         
3021         var cfg = {
3022             tag: 'h' + (1 *this.level),
3023             html: this.html || ''
3024         } ;
3025         
3026         return cfg;
3027     }
3028    
3029 });
3030
3031  
3032
3033  /*
3034  * Based on:
3035  * Ext JS Library 1.1.1
3036  * Copyright(c) 2006-2007, Ext JS, LLC.
3037  *
3038  * Originally Released Under LGPL - original licence link has changed is not relivant.
3039  *
3040  * Fork - LGPL
3041  * <script type="text/javascript">
3042  */
3043  
3044 /**
3045  * @class Roo.bootstrap.MenuMgr
3046  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3047  * @singleton
3048  */
3049 Roo.bootstrap.MenuMgr = function(){
3050    var menus, active, groups = {}, attached = false, lastShow = new Date();
3051
3052    // private - called when first menu is created
3053    function init(){
3054        menus = {};
3055        active = new Roo.util.MixedCollection();
3056        Roo.get(document).addKeyListener(27, function(){
3057            if(active.length > 0){
3058                hideAll();
3059            }
3060        });
3061    }
3062
3063    // private
3064    function hideAll(){
3065        if(active && active.length > 0){
3066            var c = active.clone();
3067            c.each(function(m){
3068                m.hide();
3069            });
3070        }
3071    }
3072
3073    // private
3074    function onHide(m){
3075        active.remove(m);
3076        if(active.length < 1){
3077            Roo.get(document).un("mouseup", onMouseDown);
3078             
3079            attached = false;
3080        }
3081    }
3082
3083    // private
3084    function onShow(m){
3085        var last = active.last();
3086        lastShow = new Date();
3087        active.add(m);
3088        if(!attached){
3089           Roo.get(document).on("mouseup", onMouseDown);
3090            
3091            attached = true;
3092        }
3093        if(m.parentMenu){
3094           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3095           m.parentMenu.activeChild = m;
3096        }else if(last && last.isVisible()){
3097           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3098        }
3099    }
3100
3101    // private
3102    function onBeforeHide(m){
3103        if(m.activeChild){
3104            m.activeChild.hide();
3105        }
3106        if(m.autoHideTimer){
3107            clearTimeout(m.autoHideTimer);
3108            delete m.autoHideTimer;
3109        }
3110    }
3111
3112    // private
3113    function onBeforeShow(m){
3114        var pm = m.parentMenu;
3115        if(!pm && !m.allowOtherMenus){
3116            hideAll();
3117        }else if(pm && pm.activeChild && active != m){
3118            pm.activeChild.hide();
3119        }
3120    }
3121
3122    // private this should really trigger on mouseup..
3123    function onMouseDown(e){
3124         Roo.log("on Mouse Up");
3125         
3126         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3127             Roo.log("MenuManager hideAll");
3128             hideAll();
3129             e.stopEvent();
3130         }
3131         
3132         
3133    }
3134
3135    // private
3136    function onBeforeCheck(mi, state){
3137        if(state){
3138            var g = groups[mi.group];
3139            for(var i = 0, l = g.length; i < l; i++){
3140                if(g[i] != mi){
3141                    g[i].setChecked(false);
3142                }
3143            }
3144        }
3145    }
3146
3147    return {
3148
3149        /**
3150         * Hides all menus that are currently visible
3151         */
3152        hideAll : function(){
3153             hideAll();  
3154        },
3155
3156        // private
3157        register : function(menu){
3158            if(!menus){
3159                init();
3160            }
3161            menus[menu.id] = menu;
3162            menu.on("beforehide", onBeforeHide);
3163            menu.on("hide", onHide);
3164            menu.on("beforeshow", onBeforeShow);
3165            menu.on("show", onShow);
3166            var g = menu.group;
3167            if(g && menu.events["checkchange"]){
3168                if(!groups[g]){
3169                    groups[g] = [];
3170                }
3171                groups[g].push(menu);
3172                menu.on("checkchange", onCheck);
3173            }
3174        },
3175
3176         /**
3177          * Returns a {@link Roo.menu.Menu} object
3178          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3179          * be used to generate and return a new Menu instance.
3180          */
3181        get : function(menu){
3182            if(typeof menu == "string"){ // menu id
3183                return menus[menu];
3184            }else if(menu.events){  // menu instance
3185                return menu;
3186            }
3187            /*else if(typeof menu.length == 'number'){ // array of menu items?
3188                return new Roo.bootstrap.Menu({items:menu});
3189            }else{ // otherwise, must be a config
3190                return new Roo.bootstrap.Menu(menu);
3191            }
3192            */
3193            return false;
3194        },
3195
3196        // private
3197        unregister : function(menu){
3198            delete menus[menu.id];
3199            menu.un("beforehide", onBeforeHide);
3200            menu.un("hide", onHide);
3201            menu.un("beforeshow", onBeforeShow);
3202            menu.un("show", onShow);
3203            var g = menu.group;
3204            if(g && menu.events["checkchange"]){
3205                groups[g].remove(menu);
3206                menu.un("checkchange", onCheck);
3207            }
3208        },
3209
3210        // private
3211        registerCheckable : function(menuItem){
3212            var g = menuItem.group;
3213            if(g){
3214                if(!groups[g]){
3215                    groups[g] = [];
3216                }
3217                groups[g].push(menuItem);
3218                menuItem.on("beforecheckchange", onBeforeCheck);
3219            }
3220        },
3221
3222        // private
3223        unregisterCheckable : function(menuItem){
3224            var g = menuItem.group;
3225            if(g){
3226                groups[g].remove(menuItem);
3227                menuItem.un("beforecheckchange", onBeforeCheck);
3228            }
3229        }
3230    };
3231 }();/*
3232  * - LGPL
3233  *
3234  * menu
3235  * 
3236  */
3237
3238 /**
3239  * @class Roo.bootstrap.Menu
3240  * @extends Roo.bootstrap.Component
3241  * Bootstrap Menu class - container for MenuItems
3242  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3243  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3244  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3245  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3246  * 
3247  * @constructor
3248  * Create a new Menu
3249  * @param {Object} config The config object
3250  */
3251
3252
3253 Roo.bootstrap.Menu = function(config){
3254     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3255     if (this.registerMenu && this.type != 'treeview')  {
3256         Roo.bootstrap.MenuMgr.register(this);
3257     }
3258     
3259     
3260     this.addEvents({
3261         /**
3262          * @event beforeshow
3263          * Fires before this menu is displayed (return false to block)
3264          * @param {Roo.menu.Menu} this
3265          */
3266         beforeshow : true,
3267         /**
3268          * @event beforehide
3269          * Fires before this menu is hidden (return false to block)
3270          * @param {Roo.menu.Menu} this
3271          */
3272         beforehide : true,
3273         /**
3274          * @event show
3275          * Fires after this menu is displayed
3276          * @param {Roo.menu.Menu} this
3277          */
3278         show : true,
3279         /**
3280          * @event hide
3281          * Fires after this menu is hidden
3282          * @param {Roo.menu.Menu} this
3283          */
3284         hide : true,
3285         /**
3286          * @event click
3287          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3288          * @param {Roo.menu.Menu} this
3289          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3290          * @param {Roo.EventObject} e
3291          */
3292         click : true,
3293         /**
3294          * @event mouseover
3295          * Fires when the mouse is hovering over this menu
3296          * @param {Roo.menu.Menu} this
3297          * @param {Roo.EventObject} e
3298          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3299          */
3300         mouseover : true,
3301         /**
3302          * @event mouseout
3303          * Fires when the mouse exits this menu
3304          * @param {Roo.menu.Menu} this
3305          * @param {Roo.EventObject} e
3306          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3307          */
3308         mouseout : true,
3309         /**
3310          * @event itemclick
3311          * Fires when a menu item contained in this menu is clicked
3312          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3313          * @param {Roo.EventObject} e
3314          */
3315         itemclick: true
3316     });
3317     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3318 };
3319
3320 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3321     
3322    /// html : false,
3323     //align : '',
3324     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3325     type: false,
3326     /**
3327      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3328      */
3329     registerMenu : true,
3330     
3331     menuItems :false, // stores the menu items..
3332     
3333     hidden:true,
3334         
3335     parentMenu : false,
3336     
3337     stopEvent : true,
3338     
3339     isLink : false,
3340     
3341     getChildContainer : function() {
3342         return this.el;  
3343     },
3344     
3345     getAutoCreate : function(){
3346          
3347         //if (['right'].indexOf(this.align)!==-1) {
3348         //    cfg.cn[1].cls += ' pull-right'
3349         //}
3350         
3351         
3352         var cfg = {
3353             tag : 'ul',
3354             cls : 'dropdown-menu' ,
3355             style : 'z-index:1000'
3356             
3357         };
3358         
3359         if (this.type === 'submenu') {
3360             cfg.cls = 'submenu active';
3361         }
3362         if (this.type === 'treeview') {
3363             cfg.cls = 'treeview-menu';
3364         }
3365         
3366         return cfg;
3367     },
3368     initEvents : function() {
3369         
3370        // Roo.log("ADD event");
3371        // Roo.log(this.triggerEl.dom);
3372         
3373         this.triggerEl.on('click', this.onTriggerClick, this);
3374         
3375         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3376         
3377         
3378         if (this.triggerEl.hasClass('nav-item')) {
3379             // dropdown toggle on the 'a' in BS4?
3380             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3381         } else {
3382             this.triggerEl.addClass('dropdown-toggle');
3383         }
3384         if (Roo.isTouch) {
3385             this.el.on('touchstart'  , this.onTouch, this);
3386         }
3387         this.el.on('click' , this.onClick, this);
3388
3389         this.el.on("mouseover", this.onMouseOver, this);
3390         this.el.on("mouseout", this.onMouseOut, this);
3391         
3392     },
3393     
3394     findTargetItem : function(e)
3395     {
3396         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3397         if(!t){
3398             return false;
3399         }
3400         //Roo.log(t);         Roo.log(t.id);
3401         if(t && t.id){
3402             //Roo.log(this.menuitems);
3403             return this.menuitems.get(t.id);
3404             
3405             //return this.items.get(t.menuItemId);
3406         }
3407         
3408         return false;
3409     },
3410     
3411     onTouch : function(e) 
3412     {
3413         Roo.log("menu.onTouch");
3414         //e.stopEvent(); this make the user popdown broken
3415         this.onClick(e);
3416     },
3417     
3418     onClick : function(e)
3419     {
3420         Roo.log("menu.onClick");
3421         
3422         var t = this.findTargetItem(e);
3423         if(!t || t.isContainer){
3424             return;
3425         }
3426         Roo.log(e);
3427         /*
3428         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3429             if(t == this.activeItem && t.shouldDeactivate(e)){
3430                 this.activeItem.deactivate();
3431                 delete this.activeItem;
3432                 return;
3433             }
3434             if(t.canActivate){
3435                 this.setActiveItem(t, true);
3436             }
3437             return;
3438             
3439             
3440         }
3441         */
3442        
3443         Roo.log('pass click event');
3444         
3445         t.onClick(e);
3446         
3447         this.fireEvent("click", this, t, e);
3448         
3449         var _this = this;
3450         
3451         if(!t.href.length || t.href == '#'){
3452             (function() { _this.hide(); }).defer(100);
3453         }
3454         
3455     },
3456     
3457     onMouseOver : function(e){
3458         var t  = this.findTargetItem(e);
3459         //Roo.log(t);
3460         //if(t){
3461         //    if(t.canActivate && !t.disabled){
3462         //        this.setActiveItem(t, true);
3463         //    }
3464         //}
3465         
3466         this.fireEvent("mouseover", this, e, t);
3467     },
3468     isVisible : function(){
3469         return !this.hidden;
3470     },
3471     onMouseOut : function(e){
3472         var t  = this.findTargetItem(e);
3473         
3474         //if(t ){
3475         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3476         //        this.activeItem.deactivate();
3477         //        delete this.activeItem;
3478         //    }
3479         //}
3480         this.fireEvent("mouseout", this, e, t);
3481     },
3482     
3483     
3484     /**
3485      * Displays this menu relative to another element
3486      * @param {String/HTMLElement/Roo.Element} element The element to align to
3487      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3488      * the element (defaults to this.defaultAlign)
3489      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3490      */
3491     show : function(el, pos, parentMenu)
3492     {
3493         if (false === this.fireEvent("beforeshow", this)) {
3494             Roo.log("show canceled");
3495             return;
3496         }
3497         this.parentMenu = parentMenu;
3498         if(!this.el){
3499             this.render();
3500         }
3501         
3502         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3503     },
3504      /**
3505      * Displays this menu at a specific xy position
3506      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3507      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3508      */
3509     showAt : function(xy, parentMenu, /* private: */_e){
3510         this.parentMenu = parentMenu;
3511         if(!this.el){
3512             this.render();
3513         }
3514         if(_e !== false){
3515             this.fireEvent("beforeshow", this);
3516             //xy = this.el.adjustForConstraints(xy);
3517         }
3518         
3519         //this.el.show();
3520         this.hideMenuItems();
3521         this.hidden = false;
3522         this.triggerEl.addClass('open');
3523         this.el.addClass('show');
3524         
3525         // reassign x when hitting right
3526         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3527             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3528         }
3529         
3530         // reassign y when hitting bottom
3531         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3532             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3533         }
3534         
3535         // but the list may align on trigger left or trigger top... should it be a properity?
3536         
3537         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3538             this.el.setXY(xy);
3539         }
3540         
3541         this.focus();
3542         this.fireEvent("show", this);
3543     },
3544     
3545     focus : function(){
3546         return;
3547         if(!this.hidden){
3548             this.doFocus.defer(50, this);
3549         }
3550     },
3551
3552     doFocus : function(){
3553         if(!this.hidden){
3554             this.focusEl.focus();
3555         }
3556     },
3557
3558     /**
3559      * Hides this menu and optionally all parent menus
3560      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3561      */
3562     hide : function(deep)
3563     {
3564         if (false === this.fireEvent("beforehide", this)) {
3565             Roo.log("hide canceled");
3566             return;
3567         }
3568         this.hideMenuItems();
3569         if(this.el && this.isVisible()){
3570            
3571             if(this.activeItem){
3572                 this.activeItem.deactivate();
3573                 this.activeItem = null;
3574             }
3575             this.triggerEl.removeClass('open');;
3576             this.el.removeClass('show');
3577             this.hidden = true;
3578             this.fireEvent("hide", this);
3579         }
3580         if(deep === true && this.parentMenu){
3581             this.parentMenu.hide(true);
3582         }
3583     },
3584     
3585     onTriggerClick : function(e)
3586     {
3587         Roo.log('trigger click');
3588         
3589         var target = e.getTarget();
3590         
3591         Roo.log(target.nodeName.toLowerCase());
3592         
3593         if(target.nodeName.toLowerCase() === 'i'){
3594             e.preventDefault();
3595         }
3596         
3597     },
3598     
3599     onTriggerPress  : function(e)
3600     {
3601         Roo.log('trigger press');
3602         //Roo.log(e.getTarget());
3603        // Roo.log(this.triggerEl.dom);
3604        
3605         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3606         var pel = Roo.get(e.getTarget());
3607         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3608             Roo.log('is treeview or dropdown?');
3609             return;
3610         }
3611         
3612         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3613             return;
3614         }
3615         
3616         if (this.isVisible()) {
3617             Roo.log('hide');
3618             this.hide();
3619         } else {
3620             Roo.log('show');
3621             this.show(this.triggerEl, '?', false);
3622         }
3623         
3624         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3625             e.stopEvent();
3626         }
3627         
3628     },
3629        
3630     
3631     hideMenuItems : function()
3632     {
3633         Roo.log("hide Menu Items");
3634         if (!this.el) { 
3635             return;
3636         }
3637         
3638         this.el.select('.open',true).each(function(aa) {
3639             
3640             aa.removeClass('open');
3641          
3642         });
3643     },
3644     addxtypeChild : function (tree, cntr) {
3645         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3646           
3647         this.menuitems.add(comp);
3648         return comp;
3649
3650     },
3651     getEl : function()
3652     {
3653         Roo.log(this.el);
3654         return this.el;
3655     },
3656     
3657     clear : function()
3658     {
3659         this.getEl().dom.innerHTML = '';
3660         this.menuitems.clear();
3661     }
3662 });
3663
3664  
3665  /*
3666  * - LGPL
3667  *
3668  * menu item
3669  * 
3670  */
3671
3672
3673 /**
3674  * @class Roo.bootstrap.MenuItem
3675  * @extends Roo.bootstrap.Component
3676  * Bootstrap MenuItem class
3677  * @cfg {String} html the menu label
3678  * @cfg {String} href the link
3679  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3680  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3681  * @cfg {Boolean} active  used on sidebars to highlight active itesm
3682  * @cfg {String} fa favicon to show on left of menu item.
3683  * @cfg {Roo.bootsrap.Menu} menu the child menu.
3684  * 
3685  * 
3686  * @constructor
3687  * Create a new MenuItem
3688  * @param {Object} config The config object
3689  */
3690
3691
3692 Roo.bootstrap.MenuItem = function(config){
3693     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3694     this.addEvents({
3695         // raw events
3696         /**
3697          * @event click
3698          * The raw click event for the entire grid.
3699          * @param {Roo.bootstrap.MenuItem} this
3700          * @param {Roo.EventObject} e
3701          */
3702         "click" : true
3703     });
3704 };
3705
3706 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
3707     
3708     href : false,
3709     html : false,
3710     preventDefault: false,
3711     isContainer : false,
3712     active : false,
3713     fa: false,
3714     
3715     getAutoCreate : function(){
3716         
3717         if(this.isContainer){
3718             return {
3719                 tag: 'li',
3720                 cls: 'dropdown-menu-item '
3721             };
3722         }
3723         var ctag = {
3724             tag: 'span',
3725             html: 'Link'
3726         };
3727         
3728         var anc = {
3729             tag : 'a',
3730             cls : 'dropdown-item',
3731             href : '#',
3732             cn : [  ]
3733         };
3734         
3735         if (this.fa !== false) {
3736             anc.cn.push({
3737                 tag : 'i',
3738                 cls : 'fa fa-' + this.fa
3739             });
3740         }
3741         
3742         anc.cn.push(ctag);
3743         
3744         
3745         var cfg= {
3746             tag: 'li',
3747             cls: 'dropdown-menu-item',
3748             cn: [ anc ]
3749         };
3750         if (this.parent().type == 'treeview') {
3751             cfg.cls = 'treeview-menu';
3752         }
3753         if (this.active) {
3754             cfg.cls += ' active';
3755         }
3756         
3757         
3758         
3759         anc.href = this.href || cfg.cn[0].href ;
3760         ctag.html = this.html || cfg.cn[0].html ;
3761         return cfg;
3762     },
3763     
3764     initEvents: function()
3765     {
3766         if (this.parent().type == 'treeview') {
3767             this.el.select('a').on('click', this.onClick, this);
3768         }
3769         
3770         if (this.menu) {
3771             this.menu.parentType = this.xtype;
3772             this.menu.triggerEl = this.el;
3773             this.menu = this.addxtype(Roo.apply({}, this.menu));
3774         }
3775         
3776     },
3777     onClick : function(e)
3778     {
3779         Roo.log('item on click ');
3780         
3781         if(this.preventDefault){
3782             e.preventDefault();
3783         }
3784         //this.parent().hideMenuItems();
3785         
3786         this.fireEvent('click', this, e);
3787     },
3788     getEl : function()
3789     {
3790         return this.el;
3791     } 
3792 });
3793
3794  
3795
3796  /*
3797  * - LGPL
3798  *
3799  * menu separator
3800  * 
3801  */
3802
3803
3804 /**
3805  * @class Roo.bootstrap.MenuSeparator
3806  * @extends Roo.bootstrap.Component
3807  * Bootstrap MenuSeparator class
3808  * 
3809  * @constructor
3810  * Create a new MenuItem
3811  * @param {Object} config The config object
3812  */
3813
3814
3815 Roo.bootstrap.MenuSeparator = function(config){
3816     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3817 };
3818
3819 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
3820     
3821     getAutoCreate : function(){
3822         var cfg = {
3823             cls: 'divider',
3824             tag : 'li'
3825         };
3826         
3827         return cfg;
3828     }
3829    
3830 });
3831
3832  
3833
3834  
3835 /*
3836 * Licence: LGPL
3837 */
3838
3839 /**
3840  * @class Roo.bootstrap.Modal
3841  * @extends Roo.bootstrap.Component
3842  * Bootstrap Modal class
3843  * @cfg {String} title Title of dialog
3844  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3845  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
3846  * @cfg {Boolean} specificTitle default false
3847  * @cfg {Array} buttons Array of buttons or standard button set..
3848  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3849  * @cfg {Boolean} animate default true
3850  * @cfg {Boolean} allow_close default true
3851  * @cfg {Boolean} fitwindow default false
3852  * @cfg {Number} width fixed width - usefull for chrome extension only really.
3853  * @cfg {Number} height fixed height - usefull for chrome extension only really.
3854  * @cfg {String} size (sm|lg|xl) default empty
3855  * @cfg {Number} max_width set the max width of modal
3856  * @cfg {Boolean} editableTitle can the title be edited
3857
3858  *
3859  *
3860  * @constructor
3861  * Create a new Modal Dialog
3862  * @param {Object} config The config object
3863  */
3864
3865 Roo.bootstrap.Modal = function(config){
3866     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3867     this.addEvents({
3868         // raw events
3869         /**
3870          * @event btnclick
3871          * The raw btnclick event for the button
3872          * @param {Roo.EventObject} e
3873          */
3874         "btnclick" : true,
3875         /**
3876          * @event resize
3877          * Fire when dialog resize
3878          * @param {Roo.bootstrap.Modal} this
3879          * @param {Roo.EventObject} e
3880          */
3881         "resize" : true,
3882         /**
3883          * @event titlechanged
3884          * Fire when the editable title has been changed
3885          * @param {Roo.bootstrap.Modal} this
3886          * @param {Roo.EventObject} value
3887          */
3888         "titlechanged" : true 
3889         
3890     });
3891     this.buttons = this.buttons || [];
3892
3893     if (this.tmpl) {
3894         this.tmpl = Roo.factory(this.tmpl);
3895     }
3896
3897 };
3898
3899 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
3900
3901     title : 'test dialog',
3902
3903     buttons : false,
3904
3905     // set on load...
3906
3907     html: false,
3908
3909     tmp: false,
3910
3911     specificTitle: false,
3912
3913     buttonPosition: 'right',
3914
3915     allow_close : true,
3916
3917     animate : true,
3918
3919     fitwindow: false,
3920     
3921      // private
3922     dialogEl: false,
3923     bodyEl:  false,
3924     footerEl:  false,
3925     titleEl:  false,
3926     closeEl:  false,
3927
3928     size: '',
3929     
3930     max_width: 0,
3931     
3932     max_height: 0,
3933     
3934     fit_content: false,
3935     editableTitle  : false,
3936
3937     onRender : function(ct, position)
3938     {
3939         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
3940
3941         if(!this.el){
3942             var cfg = Roo.apply({},  this.getAutoCreate());
3943             cfg.id = Roo.id();
3944             //if(!cfg.name){
3945             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
3946             //}
3947             //if (!cfg.name.length) {
3948             //    delete cfg.name;
3949            // }
3950             if (this.cls) {
3951                 cfg.cls += ' ' + this.cls;
3952             }
3953             if (this.style) {
3954                 cfg.style = this.style;
3955             }
3956             this.el = Roo.get(document.body).createChild(cfg, position);
3957         }
3958         //var type = this.el.dom.type;
3959
3960
3961         if(this.tabIndex !== undefined){
3962             this.el.dom.setAttribute('tabIndex', this.tabIndex);
3963         }
3964
3965         this.dialogEl = this.el.select('.modal-dialog',true).first();
3966         this.bodyEl = this.el.select('.modal-body',true).first();
3967         this.closeEl = this.el.select('.modal-header .close', true).first();
3968         this.headerEl = this.el.select('.modal-header',true).first();
3969         this.titleEl = this.el.select('.modal-title',true).first();
3970         this.footerEl = this.el.select('.modal-footer',true).first();
3971
3972         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
3973         
3974         //this.el.addClass("x-dlg-modal");
3975
3976         if (this.buttons.length) {
3977             Roo.each(this.buttons, function(bb) {
3978                 var b = Roo.apply({}, bb);
3979                 b.xns = b.xns || Roo.bootstrap;
3980                 b.xtype = b.xtype || 'Button';
3981                 if (typeof(b.listeners) == 'undefined') {
3982                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
3983                 }
3984
3985                 var btn = Roo.factory(b);
3986
3987                 btn.render(this.getButtonContainer());
3988
3989             },this);
3990         }
3991         // render the children.
3992         var nitems = [];
3993
3994         if(typeof(this.items) != 'undefined'){
3995             var items = this.items;
3996             delete this.items;
3997
3998             for(var i =0;i < items.length;i++) {
3999                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4000             }
4001         }
4002
4003         this.items = nitems;
4004
4005         // where are these used - they used to be body/close/footer
4006
4007
4008         this.initEvents();
4009         //this.el.addClass([this.fieldClass, this.cls]);
4010
4011     },
4012
4013     getAutoCreate : function()
4014     {
4015         // we will default to modal-body-overflow - might need to remove or make optional later.
4016         var bdy = {
4017                 cls : 'modal-body enable-modal-body-overflow ', 
4018                 html : this.html || ''
4019         };
4020
4021         var title = {
4022             tag: 'h4',
4023             cls : 'modal-title',
4024             html : this.title
4025         };
4026
4027         if(this.specificTitle){ // WTF is this?
4028             title = this.title;
4029         }
4030
4031         var header = [];
4032         if (this.allow_close && Roo.bootstrap.version == 3) {
4033             header.push({
4034                 tag: 'button',
4035                 cls : 'close',
4036                 html : '&times'
4037             });
4038         }
4039
4040         header.push(title);
4041
4042         if (this.editableTitle) {
4043             header.push({
4044                 cls: 'form-control roo-editable-title d-none',
4045                 tag: 'input',
4046                 type: 'text'
4047             });
4048         }
4049         
4050         if (this.allow_close && Roo.bootstrap.version == 4) {
4051             header.push({
4052                 tag: 'button',
4053                 cls : 'close',
4054                 html : '&times'
4055             });
4056         }
4057         
4058         var size = '';
4059
4060         if(this.size.length){
4061             size = 'modal-' + this.size;
4062         }
4063         
4064         var footer = Roo.bootstrap.version == 3 ?
4065             {
4066                 cls : 'modal-footer',
4067                 cn : [
4068                     {
4069                         tag: 'div',
4070                         cls: 'btn-' + this.buttonPosition
4071                     }
4072                 ]
4073
4074             } :
4075             {  // BS4 uses mr-auto on left buttons....
4076                 cls : 'modal-footer'
4077             };
4078
4079             
4080
4081         
4082         
4083         var modal = {
4084             cls: "modal",
4085              cn : [
4086                 {
4087                     cls: "modal-dialog " + size,
4088                     cn : [
4089                         {
4090                             cls : "modal-content",
4091                             cn : [
4092                                 {
4093                                     cls : 'modal-header',
4094                                     cn : header
4095                                 },
4096                                 bdy,
4097                                 footer
4098                             ]
4099
4100                         }
4101                     ]
4102
4103                 }
4104             ]
4105         };
4106
4107         if(this.animate){
4108             modal.cls += ' fade';
4109         }
4110
4111         return modal;
4112
4113     },
4114     getChildContainer : function() {
4115
4116          return this.bodyEl;
4117
4118     },
4119     getButtonContainer : function() {
4120         
4121          return Roo.bootstrap.version == 4 ?
4122             this.el.select('.modal-footer',true).first()
4123             : this.el.select('.modal-footer div',true).first();
4124
4125     },
4126     initEvents : function()
4127     {
4128         if (this.allow_close) {
4129             this.closeEl.on('click', this.hide, this);
4130         }
4131         Roo.EventManager.onWindowResize(this.resize, this, true);
4132         if (this.editableTitle) {
4133             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4134             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4135             this.headerEditEl.on('keyup', function(e) {
4136                     if(e.isNavKeyPress()){
4137                             this.toggleHeaderInput(false)
4138                     }
4139                 }, this);
4140             this.headerEditEl.on('blur', function(e) {
4141                 this.toggleHeaderInput(false)
4142             },this);
4143         }
4144
4145     },
4146   
4147
4148     resize : function()
4149     {
4150         this.maskEl.setSize(
4151             Roo.lib.Dom.getViewWidth(true),
4152             Roo.lib.Dom.getViewHeight(true)
4153         );
4154         
4155         if (this.fitwindow) {
4156             
4157            
4158             this.setSize(
4159                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4160                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4161             );
4162             return;
4163         }
4164         
4165         if(this.max_width !== 0) {
4166             
4167             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4168             
4169             if(this.height) {
4170                 this.setSize(w, this.height);
4171                 return;
4172             }
4173             
4174             if(this.max_height) {
4175                 this.setSize(w,Math.min(
4176                     this.max_height,
4177                     Roo.lib.Dom.getViewportHeight(true) - 60
4178                 ));
4179                 
4180                 return;
4181             }
4182             
4183             if(!this.fit_content) {
4184                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4185                 return;
4186             }
4187             
4188             this.setSize(w, Math.min(
4189                 60 +
4190                 this.headerEl.getHeight() + 
4191                 this.footerEl.getHeight() + 
4192                 this.getChildHeight(this.bodyEl.dom.childNodes),
4193                 Roo.lib.Dom.getViewportHeight(true) - 60)
4194             );
4195         }
4196         
4197     },
4198
4199     setSize : function(w,h)
4200     {
4201         if (!w && !h) {
4202             return;
4203         }
4204         
4205         this.resizeTo(w,h);
4206     },
4207
4208     show : function() {
4209
4210         if (!this.rendered) {
4211             this.render();
4212         }
4213
4214         //this.el.setStyle('display', 'block');
4215         this.el.removeClass('hideing');
4216         this.el.dom.style.display='block';
4217         
4218         Roo.get(document.body).addClass('modal-open');
4219  
4220         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4221             
4222             (function(){
4223                 this.el.addClass('show');
4224                 this.el.addClass('in');
4225             }).defer(50, this);
4226         }else{
4227             this.el.addClass('show');
4228             this.el.addClass('in');
4229         }
4230
4231         // not sure how we can show data in here..
4232         //if (this.tmpl) {
4233         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4234         //}
4235
4236         Roo.get(document.body).addClass("x-body-masked");
4237         
4238         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4239         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4240         this.maskEl.dom.style.display = 'block';
4241         this.maskEl.addClass('show');
4242         
4243         
4244         this.resize();
4245         
4246         this.fireEvent('show', this);
4247
4248         // set zindex here - otherwise it appears to be ignored...
4249         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4250
4251         (function () {
4252             this.items.forEach( function(e) {
4253                 e.layout ? e.layout() : false;
4254
4255             });
4256         }).defer(100,this);
4257
4258     },
4259     hide : function()
4260     {
4261         if(this.fireEvent("beforehide", this) !== false){
4262             
4263             this.maskEl.removeClass('show');
4264             
4265             this.maskEl.dom.style.display = '';
4266             Roo.get(document.body).removeClass("x-body-masked");
4267             this.el.removeClass('in');
4268             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4269
4270             if(this.animate){ // why
4271                 this.el.addClass('hideing');
4272                 this.el.removeClass('show');
4273                 (function(){
4274                     if (!this.el.hasClass('hideing')) {
4275                         return; // it's been shown again...
4276                     }
4277                     
4278                     this.el.dom.style.display='';
4279
4280                     Roo.get(document.body).removeClass('modal-open');
4281                     this.el.removeClass('hideing');
4282                 }).defer(150,this);
4283                 
4284             }else{
4285                 this.el.removeClass('show');
4286                 this.el.dom.style.display='';
4287                 Roo.get(document.body).removeClass('modal-open');
4288
4289             }
4290             this.fireEvent('hide', this);
4291         }
4292     },
4293     isVisible : function()
4294     {
4295         
4296         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4297         
4298     },
4299
4300     addButton : function(str, cb)
4301     {
4302
4303
4304         var b = Roo.apply({}, { html : str } );
4305         b.xns = b.xns || Roo.bootstrap;
4306         b.xtype = b.xtype || 'Button';
4307         if (typeof(b.listeners) == 'undefined') {
4308             b.listeners = { click : cb.createDelegate(this)  };
4309         }
4310
4311         var btn = Roo.factory(b);
4312
4313         btn.render(this.getButtonContainer());
4314
4315         return btn;
4316
4317     },
4318
4319     setDefaultButton : function(btn)
4320     {
4321         //this.el.select('.modal-footer').()
4322     },
4323
4324     resizeTo: function(w,h)
4325     {
4326         this.dialogEl.setWidth(w);
4327         
4328         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4329
4330         this.bodyEl.setHeight(h - diff);
4331         
4332         this.fireEvent('resize', this);
4333     },
4334     
4335     setContentSize  : function(w, h)
4336     {
4337
4338     },
4339     onButtonClick: function(btn,e)
4340     {
4341         //Roo.log([a,b,c]);
4342         this.fireEvent('btnclick', btn.name, e);
4343     },
4344      /**
4345      * Set the title of the Dialog
4346      * @param {String} str new Title
4347      */
4348     setTitle: function(str) {
4349         this.titleEl.dom.innerHTML = str;
4350         this.title = str;
4351     },
4352     /**
4353      * Set the body of the Dialog
4354      * @param {String} str new Title
4355      */
4356     setBody: function(str) {
4357         this.bodyEl.dom.innerHTML = str;
4358     },
4359     /**
4360      * Set the body of the Dialog using the template
4361      * @param {Obj} data - apply this data to the template and replace the body contents.
4362      */
4363     applyBody: function(obj)
4364     {
4365         if (!this.tmpl) {
4366             Roo.log("Error - using apply Body without a template");
4367             //code
4368         }
4369         this.tmpl.overwrite(this.bodyEl, obj);
4370     },
4371     
4372     getChildHeight : function(child_nodes)
4373     {
4374         if(
4375             !child_nodes ||
4376             child_nodes.length == 0
4377         ) {
4378             return 0;
4379         }
4380         
4381         var child_height = 0;
4382         
4383         for(var i = 0; i < child_nodes.length; i++) {
4384             
4385             /*
4386             * for modal with tabs...
4387             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4388                 
4389                 var layout_childs = child_nodes[i].childNodes;
4390                 
4391                 for(var j = 0; j < layout_childs.length; j++) {
4392                     
4393                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4394                         
4395                         var layout_body_childs = layout_childs[j].childNodes;
4396                         
4397                         for(var k = 0; k < layout_body_childs.length; k++) {
4398                             
4399                             if(layout_body_childs[k].classList.contains('navbar')) {
4400                                 child_height += layout_body_childs[k].offsetHeight;
4401                                 continue;
4402                             }
4403                             
4404                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4405                                 
4406                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4407                                 
4408                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4409                                     
4410                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4411                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4412                                         continue;
4413                                     }
4414                                     
4415                                 }
4416                                 
4417                             }
4418                             
4419                         }
4420                     }
4421                 }
4422                 continue;
4423             }
4424             */
4425             
4426             child_height += child_nodes[i].offsetHeight;
4427             // Roo.log(child_nodes[i].offsetHeight);
4428         }
4429         
4430         return child_height;
4431     },
4432     toggleHeaderInput : function(is_edit)
4433     {
4434         
4435         if (is_edit && this.is_header_editing) {
4436             return; // already editing..
4437         }
4438         if (is_edit) {
4439     
4440             this.headerEditEl.dom.value = this.title;
4441             this.headerEditEl.removeClass('d-none');
4442             this.headerEditEl.dom.focus();
4443             this.titleEl.addClass('d-none');
4444             
4445             this.is_header_editing = true;
4446             return
4447         }
4448         // flip back to not editing.
4449         this.title = this.headerEditEl.dom.value;
4450         this.headerEditEl.addClass('d-none');
4451         this.titleEl.removeClass('d-none');
4452         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4453         this.is_header_editing = false;
4454         this.fireEvent('titlechanged', this, this.title);
4455     
4456             
4457         
4458     }
4459
4460 });
4461
4462
4463 Roo.apply(Roo.bootstrap.Modal,  {
4464     /**
4465          * Button config that displays a single OK button
4466          * @type Object
4467          */
4468         OK :  [{
4469             name : 'ok',
4470             weight : 'primary',
4471             html : 'OK'
4472         }],
4473         /**
4474          * Button config that displays Yes and No buttons
4475          * @type Object
4476          */
4477         YESNO : [
4478             {
4479                 name  : 'no',
4480                 html : 'No'
4481             },
4482             {
4483                 name  :'yes',
4484                 weight : 'primary',
4485                 html : 'Yes'
4486             }
4487         ],
4488
4489         /**
4490          * Button config that displays OK and Cancel buttons
4491          * @type Object
4492          */
4493         OKCANCEL : [
4494             {
4495                name : 'cancel',
4496                 html : 'Cancel'
4497             },
4498             {
4499                 name : 'ok',
4500                 weight : 'primary',
4501                 html : 'OK'
4502             }
4503         ],
4504         /**
4505          * Button config that displays Yes, No and Cancel buttons
4506          * @type Object
4507          */
4508         YESNOCANCEL : [
4509             {
4510                 name : 'yes',
4511                 weight : 'primary',
4512                 html : 'Yes'
4513             },
4514             {
4515                 name : 'no',
4516                 html : 'No'
4517             },
4518             {
4519                 name : 'cancel',
4520                 html : 'Cancel'
4521             }
4522         ],
4523         
4524         zIndex : 10001
4525 });
4526
4527 /*
4528  * - LGPL
4529  *
4530  * messagebox - can be used as a replace
4531  * 
4532  */
4533 /**
4534  * @class Roo.MessageBox
4535  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4536  * Example usage:
4537  *<pre><code>
4538 // Basic alert:
4539 Roo.Msg.alert('Status', 'Changes saved successfully.');
4540
4541 // Prompt for user data:
4542 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4543     if (btn == 'ok'){
4544         // process text value...
4545     }
4546 });
4547
4548 // Show a dialog using config options:
4549 Roo.Msg.show({
4550    title:'Save Changes?',
4551    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4552    buttons: Roo.Msg.YESNOCANCEL,
4553    fn: processResult,
4554    animEl: 'elId'
4555 });
4556 </code></pre>
4557  * @singleton
4558  */
4559 Roo.bootstrap.MessageBox = function(){
4560     var dlg, opt, mask, waitTimer;
4561     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4562     var buttons, activeTextEl, bwidth;
4563
4564     
4565     // private
4566     var handleButton = function(button){
4567         dlg.hide();
4568         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4569     };
4570
4571     // private
4572     var handleHide = function(){
4573         if(opt && opt.cls){
4574             dlg.el.removeClass(opt.cls);
4575         }
4576         //if(waitTimer){
4577         //    Roo.TaskMgr.stop(waitTimer);
4578         //    waitTimer = null;
4579         //}
4580     };
4581
4582     // private
4583     var updateButtons = function(b){
4584         var width = 0;
4585         if(!b){
4586             buttons["ok"].hide();
4587             buttons["cancel"].hide();
4588             buttons["yes"].hide();
4589             buttons["no"].hide();
4590             dlg.footerEl.hide();
4591             
4592             return width;
4593         }
4594         dlg.footerEl.show();
4595         for(var k in buttons){
4596             if(typeof buttons[k] != "function"){
4597                 if(b[k]){
4598                     buttons[k].show();
4599                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4600                     width += buttons[k].el.getWidth()+15;
4601                 }else{
4602                     buttons[k].hide();
4603                 }
4604             }
4605         }
4606         return width;
4607     };
4608
4609     // private
4610     var handleEsc = function(d, k, e){
4611         if(opt && opt.closable !== false){
4612             dlg.hide();
4613         }
4614         if(e){
4615             e.stopEvent();
4616         }
4617     };
4618
4619     return {
4620         /**
4621          * Returns a reference to the underlying {@link Roo.BasicDialog} element
4622          * @return {Roo.BasicDialog} The BasicDialog element
4623          */
4624         getDialog : function(){
4625            if(!dlg){
4626                 dlg = new Roo.bootstrap.Modal( {
4627                     //draggable: true,
4628                     //resizable:false,
4629                     //constraintoviewport:false,
4630                     //fixedcenter:true,
4631                     //collapsible : false,
4632                     //shim:true,
4633                     //modal: true,
4634                 //    width: 'auto',
4635                   //  height:100,
4636                     //buttonAlign:"center",
4637                     closeClick : function(){
4638                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4639                             handleButton("no");
4640                         }else{
4641                             handleButton("cancel");
4642                         }
4643                     }
4644                 });
4645                 dlg.render();
4646                 dlg.on("hide", handleHide);
4647                 mask = dlg.mask;
4648                 //dlg.addKeyListener(27, handleEsc);
4649                 buttons = {};
4650                 this.buttons = buttons;
4651                 var bt = this.buttonText;
4652                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4653                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4654                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4655                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4656                 //Roo.log(buttons);
4657                 bodyEl = dlg.bodyEl.createChild({
4658
4659                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4660                         '<textarea class="roo-mb-textarea"></textarea>' +
4661                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
4662                 });
4663                 msgEl = bodyEl.dom.firstChild;
4664                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4665                 textboxEl.enableDisplayMode();
4666                 textboxEl.addKeyListener([10,13], function(){
4667                     if(dlg.isVisible() && opt && opt.buttons){
4668                         if(opt.buttons.ok){
4669                             handleButton("ok");
4670                         }else if(opt.buttons.yes){
4671                             handleButton("yes");
4672                         }
4673                     }
4674                 });
4675                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4676                 textareaEl.enableDisplayMode();
4677                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4678                 progressEl.enableDisplayMode();
4679                 
4680                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4681                 var pf = progressEl.dom.firstChild;
4682                 if (pf) {
4683                     pp = Roo.get(pf.firstChild);
4684                     pp.setHeight(pf.offsetHeight);
4685                 }
4686                 
4687             }
4688             return dlg;
4689         },
4690
4691         /**
4692          * Updates the message box body text
4693          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4694          * the XHTML-compliant non-breaking space character '&amp;#160;')
4695          * @return {Roo.MessageBox} This message box
4696          */
4697         updateText : function(text)
4698         {
4699             if(!dlg.isVisible() && !opt.width){
4700                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4701                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4702             }
4703             msgEl.innerHTML = text || '&#160;';
4704       
4705             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4706             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4707             var w = Math.max(
4708                     Math.min(opt.width || cw , this.maxWidth), 
4709                     Math.max(opt.minWidth || this.minWidth, bwidth)
4710             );
4711             if(opt.prompt){
4712                 activeTextEl.setWidth(w);
4713             }
4714             if(dlg.isVisible()){
4715                 dlg.fixedcenter = false;
4716             }
4717             // to big, make it scroll. = But as usual stupid IE does not support
4718             // !important..
4719             
4720             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4721                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4722                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4723             } else {
4724                 bodyEl.dom.style.height = '';
4725                 bodyEl.dom.style.overflowY = '';
4726             }
4727             if (cw > w) {
4728                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4729             } else {
4730                 bodyEl.dom.style.overflowX = '';
4731             }
4732             
4733             dlg.setContentSize(w, bodyEl.getHeight());
4734             if(dlg.isVisible()){
4735                 dlg.fixedcenter = true;
4736             }
4737             return this;
4738         },
4739
4740         /**
4741          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
4742          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4743          * @param {Number} value Any number between 0 and 1 (e.g., .5)
4744          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4745          * @return {Roo.MessageBox} This message box
4746          */
4747         updateProgress : function(value, text){
4748             if(text){
4749                 this.updateText(text);
4750             }
4751             
4752             if (pp) { // weird bug on my firefox - for some reason this is not defined
4753                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4754                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4755             }
4756             return this;
4757         },        
4758
4759         /**
4760          * Returns true if the message box is currently displayed
4761          * @return {Boolean} True if the message box is visible, else false
4762          */
4763         isVisible : function(){
4764             return dlg && dlg.isVisible();  
4765         },
4766
4767         /**
4768          * Hides the message box if it is displayed
4769          */
4770         hide : function(){
4771             if(this.isVisible()){
4772                 dlg.hide();
4773             }  
4774         },
4775
4776         /**
4777          * Displays a new message box, or reinitializes an existing message box, based on the config options
4778          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4779          * The following config object properties are supported:
4780          * <pre>
4781 Property    Type             Description
4782 ----------  ---------------  ------------------------------------------------------------------------------------
4783 animEl            String/Element   An id or Element from which the message box should animate as it opens and
4784                                    closes (defaults to undefined)
4785 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4786                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
4787 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
4788                                    progress and wait dialogs will ignore this property and always hide the
4789                                    close button as they can only be closed programmatically.
4790 cls               String           A custom CSS class to apply to the message box element
4791 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
4792                                    displayed (defaults to 75)
4793 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
4794                                    function will be btn (the name of the button that was clicked, if applicable,
4795                                    e.g. "ok"), and text (the value of the active text field, if applicable).
4796                                    Progress and wait dialogs will ignore this option since they do not respond to
4797                                    user actions and can only be closed programmatically, so any required function
4798                                    should be called by the same code after it closes the dialog.
4799 icon              String           A CSS class that provides a background image to be used as an icon for
4800                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4801 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
4802 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
4803 modal             Boolean          False to allow user interaction with the page while the message box is
4804                                    displayed (defaults to true)
4805 msg               String           A string that will replace the existing message box body text (defaults
4806                                    to the XHTML-compliant non-breaking space character '&#160;')
4807 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
4808 progress          Boolean          True to display a progress bar (defaults to false)
4809 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
4810 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
4811 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
4812 title             String           The title text
4813 value             String           The string value to set into the active textbox element if displayed
4814 wait              Boolean          True to display a progress bar (defaults to false)
4815 width             Number           The width of the dialog in pixels
4816 </pre>
4817          *
4818          * Example usage:
4819          * <pre><code>
4820 Roo.Msg.show({
4821    title: 'Address',
4822    msg: 'Please enter your address:',
4823    width: 300,
4824    buttons: Roo.MessageBox.OKCANCEL,
4825    multiline: true,
4826    fn: saveAddress,
4827    animEl: 'addAddressBtn'
4828 });
4829 </code></pre>
4830          * @param {Object} config Configuration options
4831          * @return {Roo.MessageBox} This message box
4832          */
4833         show : function(options)
4834         {
4835             
4836             // this causes nightmares if you show one dialog after another
4837             // especially on callbacks..
4838              
4839             if(this.isVisible()){
4840                 
4841                 this.hide();
4842                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4843                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
4844                 Roo.log("New Dialog Message:" +  options.msg )
4845                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4846                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4847                 
4848             }
4849             var d = this.getDialog();
4850             opt = options;
4851             d.setTitle(opt.title || "&#160;");
4852             d.closeEl.setDisplayed(opt.closable !== false);
4853             activeTextEl = textboxEl;
4854             opt.prompt = opt.prompt || (opt.multiline ? true : false);
4855             if(opt.prompt){
4856                 if(opt.multiline){
4857                     textboxEl.hide();
4858                     textareaEl.show();
4859                     textareaEl.setHeight(typeof opt.multiline == "number" ?
4860                         opt.multiline : this.defaultTextHeight);
4861                     activeTextEl = textareaEl;
4862                 }else{
4863                     textboxEl.show();
4864                     textareaEl.hide();
4865                 }
4866             }else{
4867                 textboxEl.hide();
4868                 textareaEl.hide();
4869             }
4870             progressEl.setDisplayed(opt.progress === true);
4871             if (opt.progress) {
4872                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4873             }
4874             this.updateProgress(0);
4875             activeTextEl.dom.value = opt.value || "";
4876             if(opt.prompt){
4877                 dlg.setDefaultButton(activeTextEl);
4878             }else{
4879                 var bs = opt.buttons;
4880                 var db = null;
4881                 if(bs && bs.ok){
4882                     db = buttons["ok"];
4883                 }else if(bs && bs.yes){
4884                     db = buttons["yes"];
4885                 }
4886                 dlg.setDefaultButton(db);
4887             }
4888             bwidth = updateButtons(opt.buttons);
4889             this.updateText(opt.msg);
4890             if(opt.cls){
4891                 d.el.addClass(opt.cls);
4892             }
4893             d.proxyDrag = opt.proxyDrag === true;
4894             d.modal = opt.modal !== false;
4895             d.mask = opt.modal !== false ? mask : false;
4896             if(!d.isVisible()){
4897                 // force it to the end of the z-index stack so it gets a cursor in FF
4898                 document.body.appendChild(dlg.el.dom);
4899                 d.animateTarget = null;
4900                 d.show(options.animEl);
4901             }
4902             return this;
4903         },
4904
4905         /**
4906          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
4907          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
4908          * and closing the message box when the process is complete.
4909          * @param {String} title The title bar text
4910          * @param {String} msg The message box body text
4911          * @return {Roo.MessageBox} This message box
4912          */
4913         progress : function(title, msg){
4914             this.show({
4915                 title : title,
4916                 msg : msg,
4917                 buttons: false,
4918                 progress:true,
4919                 closable:false,
4920                 minWidth: this.minProgressWidth,
4921                 modal : true
4922             });
4923             return this;
4924         },
4925
4926         /**
4927          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
4928          * If a callback function is passed it will be called after the user clicks the button, and the
4929          * id of the button that was clicked will be passed as the only parameter to the callback
4930          * (could also be the top-right close button).
4931          * @param {String} title The title bar text
4932          * @param {String} msg The message box body text
4933          * @param {Function} fn (optional) The callback function invoked after the message box is closed
4934          * @param {Object} scope (optional) The scope of the callback function
4935          * @return {Roo.MessageBox} This message box
4936          */
4937         alert : function(title, msg, fn, scope)
4938         {
4939             this.show({
4940                 title : title,
4941                 msg : msg,
4942                 buttons: this.OK,
4943                 fn: fn,
4944                 closable : false,
4945                 scope : scope,
4946                 modal : true
4947             });
4948             return this;
4949         },
4950
4951         /**
4952          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
4953          * interaction while waiting for a long-running process to complete that does not have defined intervals.
4954          * You are responsible for closing the message box when the process is complete.
4955          * @param {String} msg The message box body text
4956          * @param {String} title (optional) The title bar text
4957          * @return {Roo.MessageBox} This message box
4958          */
4959         wait : function(msg, title){
4960             this.show({
4961                 title : title,
4962                 msg : msg,
4963                 buttons: false,
4964                 closable:false,
4965                 progress:true,
4966                 modal:true,
4967                 width:300,
4968                 wait:true
4969             });
4970             waitTimer = Roo.TaskMgr.start({
4971                 run: function(i){
4972                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
4973                 },
4974                 interval: 1000
4975             });
4976             return this;
4977         },
4978
4979         /**
4980          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
4981          * If a callback function is passed it will be called after the user clicks either button, and the id of the
4982          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
4983          * @param {String} title The title bar text
4984          * @param {String} msg The message box body text
4985          * @param {Function} fn (optional) The callback function invoked after the message box is closed
4986          * @param {Object} scope (optional) The scope of the callback function
4987          * @return {Roo.MessageBox} This message box
4988          */
4989         confirm : function(title, msg, fn, scope){
4990             this.show({
4991                 title : title,
4992                 msg : msg,
4993                 buttons: this.YESNO,
4994                 fn: fn,
4995                 scope : scope,
4996                 modal : true
4997             });
4998             return this;
4999         },
5000
5001         /**
5002          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5003          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5004          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5005          * (could also be the top-right close button) and the text that was entered will be passed as the two
5006          * parameters to the callback.
5007          * @param {String} title The title bar text
5008          * @param {String} msg The message box body text
5009          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5010          * @param {Object} scope (optional) The scope of the callback function
5011          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5012          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5013          * @return {Roo.MessageBox} This message box
5014          */
5015         prompt : function(title, msg, fn, scope, multiline){
5016             this.show({
5017                 title : title,
5018                 msg : msg,
5019                 buttons: this.OKCANCEL,
5020                 fn: fn,
5021                 minWidth:250,
5022                 scope : scope,
5023                 prompt:true,
5024                 multiline: multiline,
5025                 modal : true
5026             });
5027             return this;
5028         },
5029
5030         /**
5031          * Button config that displays a single OK button
5032          * @type Object
5033          */
5034         OK : {ok:true},
5035         /**
5036          * Button config that displays Yes and No buttons
5037          * @type Object
5038          */
5039         YESNO : {yes:true, no:true},
5040         /**
5041          * Button config that displays OK and Cancel buttons
5042          * @type Object
5043          */
5044         OKCANCEL : {ok:true, cancel:true},
5045         /**
5046          * Button config that displays Yes, No and Cancel buttons
5047          * @type Object
5048          */
5049         YESNOCANCEL : {yes:true, no:true, cancel:true},
5050
5051         /**
5052          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5053          * @type Number
5054          */
5055         defaultTextHeight : 75,
5056         /**
5057          * The maximum width in pixels of the message box (defaults to 600)
5058          * @type Number
5059          */
5060         maxWidth : 600,
5061         /**
5062          * The minimum width in pixels of the message box (defaults to 100)
5063          * @type Number
5064          */
5065         minWidth : 100,
5066         /**
5067          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5068          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5069          * @type Number
5070          */
5071         minProgressWidth : 250,
5072         /**
5073          * An object containing the default button text strings that can be overriden for localized language support.
5074          * Supported properties are: ok, cancel, yes and no.
5075          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5076          * @type Object
5077          */
5078         buttonText : {
5079             ok : "OK",
5080             cancel : "Cancel",
5081             yes : "Yes",
5082             no : "No"
5083         }
5084     };
5085 }();
5086
5087 /**
5088  * Shorthand for {@link Roo.MessageBox}
5089  */
5090 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5091 Roo.Msg = Roo.Msg || Roo.MessageBox;
5092 /*
5093  * - LGPL
5094  *
5095  * navbar
5096  * 
5097  */
5098
5099 /**
5100  * @class Roo.bootstrap.Navbar
5101  * @extends Roo.bootstrap.Component
5102  * Bootstrap Navbar class
5103
5104  * @constructor
5105  * Create a new Navbar
5106  * @param {Object} config The config object
5107  */
5108
5109
5110 Roo.bootstrap.Navbar = function(config){
5111     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5112     this.addEvents({
5113         // raw events
5114         /**
5115          * @event beforetoggle
5116          * Fire before toggle the menu
5117          * @param {Roo.EventObject} e
5118          */
5119         "beforetoggle" : true
5120     });
5121 };
5122
5123 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5124     
5125     
5126    
5127     // private
5128     navItems : false,
5129     loadMask : false,
5130     
5131     
5132     getAutoCreate : function(){
5133         
5134         
5135         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5136         
5137     },
5138     
5139     initEvents :function ()
5140     {
5141         //Roo.log(this.el.select('.navbar-toggle',true));
5142         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5143         
5144         var mark = {
5145             tag: "div",
5146             cls:"x-dlg-mask"
5147         };
5148         
5149         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5150         
5151         var size = this.el.getSize();
5152         this.maskEl.setSize(size.width, size.height);
5153         this.maskEl.enableDisplayMode("block");
5154         this.maskEl.hide();
5155         
5156         if(this.loadMask){
5157             this.maskEl.show();
5158         }
5159     },
5160     
5161     
5162     getChildContainer : function()
5163     {
5164         if (this.el && this.el.select('.collapse').getCount()) {
5165             return this.el.select('.collapse',true).first();
5166         }
5167         
5168         return this.el;
5169     },
5170     
5171     mask : function()
5172     {
5173         this.maskEl.show();
5174     },
5175     
5176     unmask : function()
5177     {
5178         this.maskEl.hide();
5179     },
5180     onToggle : function()
5181     {
5182         
5183         if(this.fireEvent('beforetoggle', this) === false){
5184             return;
5185         }
5186         var ce = this.el.select('.navbar-collapse',true).first();
5187       
5188         if (!ce.hasClass('show')) {
5189            this.expand();
5190         } else {
5191             this.collapse();
5192         }
5193         
5194         
5195     
5196     },
5197     /**
5198      * Expand the navbar pulldown 
5199      */
5200     expand : function ()
5201     {
5202        
5203         var ce = this.el.select('.navbar-collapse',true).first();
5204         if (ce.hasClass('collapsing')) {
5205             return;
5206         }
5207         ce.dom.style.height = '';
5208                // show it...
5209         ce.addClass('in'); // old...
5210         ce.removeClass('collapse');
5211         ce.addClass('show');
5212         var h = ce.getHeight();
5213         Roo.log(h);
5214         ce.removeClass('show');
5215         // at this point we should be able to see it..
5216         ce.addClass('collapsing');
5217         
5218         ce.setHeight(0); // resize it ...
5219         ce.on('transitionend', function() {
5220             //Roo.log('done transition');
5221             ce.removeClass('collapsing');
5222             ce.addClass('show');
5223             ce.removeClass('collapse');
5224
5225             ce.dom.style.height = '';
5226         }, this, { single: true} );
5227         ce.setHeight(h);
5228         ce.dom.scrollTop = 0;
5229     },
5230     /**
5231      * Collapse the navbar pulldown 
5232      */
5233     collapse : function()
5234     {
5235          var ce = this.el.select('.navbar-collapse',true).first();
5236        
5237         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5238             // it's collapsed or collapsing..
5239             return;
5240         }
5241         ce.removeClass('in'); // old...
5242         ce.setHeight(ce.getHeight());
5243         ce.removeClass('show');
5244         ce.addClass('collapsing');
5245         
5246         ce.on('transitionend', function() {
5247             ce.dom.style.height = '';
5248             ce.removeClass('collapsing');
5249             ce.addClass('collapse');
5250         }, this, { single: true} );
5251         ce.setHeight(0);
5252     }
5253     
5254     
5255     
5256 });
5257
5258
5259
5260  
5261
5262  /*
5263  * - LGPL
5264  *
5265  * navbar
5266  * 
5267  */
5268
5269 /**
5270  * @class Roo.bootstrap.NavSimplebar
5271  * @extends Roo.bootstrap.Navbar
5272  * Bootstrap Sidebar class
5273  *
5274  * @cfg {Boolean} inverse is inverted color
5275  * 
5276  * @cfg {String} type (nav | pills | tabs)
5277  * @cfg {Boolean} arrangement stacked | justified
5278  * @cfg {String} align (left | right) alignment
5279  * 
5280  * @cfg {Boolean} main (true|false) main nav bar? default false
5281  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5282  * 
5283  * @cfg {String} tag (header|footer|nav|div) default is nav 
5284
5285  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5286  * 
5287  * 
5288  * @constructor
5289  * Create a new Sidebar
5290  * @param {Object} config The config object
5291  */
5292
5293
5294 Roo.bootstrap.NavSimplebar = function(config){
5295     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5296 };
5297
5298 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5299     
5300     inverse: false,
5301     
5302     type: false,
5303     arrangement: '',
5304     align : false,
5305     
5306     weight : 'light',
5307     
5308     main : false,
5309     
5310     
5311     tag : false,
5312     
5313     
5314     getAutoCreate : function(){
5315         
5316         
5317         var cfg = {
5318             tag : this.tag || 'div',
5319             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5320         };
5321         if (['light','white'].indexOf(this.weight) > -1) {
5322             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5323         }
5324         cfg.cls += ' bg-' + this.weight;
5325         
5326         if (this.inverse) {
5327             cfg.cls += ' navbar-inverse';
5328             
5329         }
5330         
5331         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5332         
5333         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5334             return cfg;
5335         }
5336         
5337         
5338     
5339         
5340         cfg.cn = [
5341             {
5342                 cls: 'nav nav-' + this.xtype,
5343                 tag : 'ul'
5344             }
5345         ];
5346         
5347          
5348         this.type = this.type || 'nav';
5349         if (['tabs','pills'].indexOf(this.type) != -1) {
5350             cfg.cn[0].cls += ' nav-' + this.type
5351         
5352         
5353         } else {
5354             if (this.type!=='nav') {
5355                 Roo.log('nav type must be nav/tabs/pills')
5356             }
5357             cfg.cn[0].cls += ' navbar-nav'
5358         }
5359         
5360         
5361         
5362         
5363         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5364             cfg.cn[0].cls += ' nav-' + this.arrangement;
5365         }
5366         
5367         
5368         if (this.align === 'right') {
5369             cfg.cn[0].cls += ' navbar-right';
5370         }
5371         
5372         
5373         
5374         
5375         return cfg;
5376     
5377         
5378     }
5379     
5380     
5381     
5382 });
5383
5384
5385
5386  
5387
5388  
5389        /*
5390  * - LGPL
5391  *
5392  * navbar
5393  * navbar-fixed-top
5394  * navbar-expand-md  fixed-top 
5395  */
5396
5397 /**
5398  * @class Roo.bootstrap.NavHeaderbar
5399  * @extends Roo.bootstrap.NavSimplebar
5400  * Bootstrap Sidebar class
5401  *
5402  * @cfg {String} brand what is brand
5403  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5404  * @cfg {String} brand_href href of the brand
5405  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5406  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5407  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5408  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5409  * 
5410  * @constructor
5411  * Create a new Sidebar
5412  * @param {Object} config The config object
5413  */
5414
5415
5416 Roo.bootstrap.NavHeaderbar = function(config){
5417     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5418       
5419 };
5420
5421 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5422     
5423     position: '',
5424     brand: '',
5425     brand_href: false,
5426     srButton : true,
5427     autohide : false,
5428     desktopCenter : false,
5429    
5430     
5431     getAutoCreate : function(){
5432         
5433         var   cfg = {
5434             tag: this.nav || 'nav',
5435             cls: 'navbar navbar-expand-md',
5436             role: 'navigation',
5437             cn: []
5438         };
5439         
5440         var cn = cfg.cn;
5441         if (this.desktopCenter) {
5442             cn.push({cls : 'container', cn : []});
5443             cn = cn[0].cn;
5444         }
5445         
5446         if(this.srButton){
5447             var btn = {
5448                 tag: 'button',
5449                 type: 'button',
5450                 cls: 'navbar-toggle navbar-toggler',
5451                 'data-toggle': 'collapse',
5452                 cn: [
5453                     {
5454                         tag: 'span',
5455                         cls: 'sr-only',
5456                         html: 'Toggle navigation'
5457                     },
5458                     {
5459                         tag: 'span',
5460                         cls: 'icon-bar navbar-toggler-icon'
5461                     },
5462                     {
5463                         tag: 'span',
5464                         cls: 'icon-bar'
5465                     },
5466                     {
5467                         tag: 'span',
5468                         cls: 'icon-bar'
5469                     }
5470                 ]
5471             };
5472             
5473             cn.push( Roo.bootstrap.version == 4 ? btn : {
5474                 tag: 'div',
5475                 cls: 'navbar-header',
5476                 cn: [
5477                     btn
5478                 ]
5479             });
5480         }
5481         
5482         cn.push({
5483             tag: 'div',
5484             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5485             cn : []
5486         });
5487         
5488         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5489         
5490         if (['light','white'].indexOf(this.weight) > -1) {
5491             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5492         }
5493         cfg.cls += ' bg-' + this.weight;
5494         
5495         
5496         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5497             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5498             
5499             // tag can override this..
5500             
5501             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5502         }
5503         
5504         if (this.brand !== '') {
5505             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5506             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5507                 tag: 'a',
5508                 href: this.brand_href ? this.brand_href : '#',
5509                 cls: 'navbar-brand',
5510                 cn: [
5511                 this.brand
5512                 ]
5513             });
5514         }
5515         
5516         if(this.main){
5517             cfg.cls += ' main-nav';
5518         }
5519         
5520         
5521         return cfg;
5522
5523         
5524     },
5525     getHeaderChildContainer : function()
5526     {
5527         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5528             return this.el.select('.navbar-header',true).first();
5529         }
5530         
5531         return this.getChildContainer();
5532     },
5533     
5534     getChildContainer : function()
5535     {
5536          
5537         return this.el.select('.roo-navbar-collapse',true).first();
5538          
5539         
5540     },
5541     
5542     initEvents : function()
5543     {
5544         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5545         
5546         if (this.autohide) {
5547             
5548             var prevScroll = 0;
5549             var ft = this.el;
5550             
5551             Roo.get(document).on('scroll',function(e) {
5552                 var ns = Roo.get(document).getScroll().top;
5553                 var os = prevScroll;
5554                 prevScroll = ns;
5555                 
5556                 if(ns > os){
5557                     ft.removeClass('slideDown');
5558                     ft.addClass('slideUp');
5559                     return;
5560                 }
5561                 ft.removeClass('slideUp');
5562                 ft.addClass('slideDown');
5563                  
5564               
5565           },this);
5566         }
5567     }    
5568     
5569 });
5570
5571
5572
5573  
5574
5575  /*
5576  * - LGPL
5577  *
5578  * navbar
5579  * 
5580  */
5581
5582 /**
5583  * @class Roo.bootstrap.NavSidebar
5584  * @extends Roo.bootstrap.Navbar
5585  * Bootstrap Sidebar class
5586  * 
5587  * @constructor
5588  * Create a new Sidebar
5589  * @param {Object} config The config object
5590  */
5591
5592
5593 Roo.bootstrap.NavSidebar = function(config){
5594     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5595 };
5596
5597 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
5598     
5599     sidebar : true, // used by Navbar Item and NavbarGroup at present...
5600     
5601     getAutoCreate : function(){
5602         
5603         
5604         return  {
5605             tag: 'div',
5606             cls: 'sidebar sidebar-nav'
5607         };
5608     
5609         
5610     }
5611     
5612     
5613     
5614 });
5615
5616
5617
5618  
5619
5620  /*
5621  * - LGPL
5622  *
5623  * nav group
5624  * 
5625  */
5626
5627 /**
5628  * @class Roo.bootstrap.NavGroup
5629  * @extends Roo.bootstrap.Component
5630  * Bootstrap NavGroup class
5631  * @cfg {String} align (left|right)
5632  * @cfg {Boolean} inverse
5633  * @cfg {String} type (nav|pills|tab) default nav
5634  * @cfg {String} navId - reference Id for navbar.
5635
5636  * 
5637  * @constructor
5638  * Create a new nav group
5639  * @param {Object} config The config object
5640  */
5641
5642 Roo.bootstrap.NavGroup = function(config){
5643     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5644     this.navItems = [];
5645    
5646     Roo.bootstrap.NavGroup.register(this);
5647      this.addEvents({
5648         /**
5649              * @event changed
5650              * Fires when the active item changes
5651              * @param {Roo.bootstrap.NavGroup} this
5652              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5653              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
5654          */
5655         'changed': true
5656      });
5657     
5658 };
5659
5660 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
5661     
5662     align: '',
5663     inverse: false,
5664     form: false,
5665     type: 'nav',
5666     navId : '',
5667     // private
5668     
5669     navItems : false, 
5670     
5671     getAutoCreate : function()
5672     {
5673         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5674         
5675         cfg = {
5676             tag : 'ul',
5677             cls: 'nav' 
5678         };
5679         if (Roo.bootstrap.version == 4) {
5680             if (['tabs','pills'].indexOf(this.type) != -1) {
5681                 cfg.cls += ' nav-' + this.type; 
5682             } else {
5683                 // trying to remove so header bar can right align top?
5684                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5685                     // do not use on header bar... 
5686                     cfg.cls += ' navbar-nav';
5687                 }
5688             }
5689             
5690         } else {
5691             if (['tabs','pills'].indexOf(this.type) != -1) {
5692                 cfg.cls += ' nav-' + this.type
5693             } else {
5694                 if (this.type !== 'nav') {
5695                     Roo.log('nav type must be nav/tabs/pills')
5696                 }
5697                 cfg.cls += ' navbar-nav'
5698             }
5699         }
5700         
5701         if (this.parent() && this.parent().sidebar) {
5702             cfg = {
5703                 tag: 'ul',
5704                 cls: 'dashboard-menu sidebar-menu'
5705             };
5706             
5707             return cfg;
5708         }
5709         
5710         if (this.form === true) {
5711             cfg = {
5712                 tag: 'form',
5713                 cls: 'navbar-form form-inline'
5714             };
5715             //nav navbar-right ml-md-auto
5716             if (this.align === 'right') {
5717                 cfg.cls += ' navbar-right ml-md-auto';
5718             } else {
5719                 cfg.cls += ' navbar-left';
5720             }
5721         }
5722         
5723         if (this.align === 'right') {
5724             cfg.cls += ' navbar-right ml-md-auto';
5725         } else {
5726             cfg.cls += ' mr-auto';
5727         }
5728         
5729         if (this.inverse) {
5730             cfg.cls += ' navbar-inverse';
5731             
5732         }
5733         
5734         
5735         return cfg;
5736     },
5737     /**
5738     * sets the active Navigation item
5739     * @param {Roo.bootstrap.NavItem} the new current navitem
5740     */
5741     setActiveItem : function(item)
5742     {
5743         var prev = false;
5744         Roo.each(this.navItems, function(v){
5745             if (v == item) {
5746                 return ;
5747             }
5748             if (v.isActive()) {
5749                 v.setActive(false, true);
5750                 prev = v;
5751                 
5752             }
5753             
5754         });
5755
5756         item.setActive(true, true);
5757         this.fireEvent('changed', this, item, prev);
5758         
5759         
5760     },
5761     /**
5762     * gets the active Navigation item
5763     * @return {Roo.bootstrap.NavItem} the current navitem
5764     */
5765     getActive : function()
5766     {
5767         
5768         var prev = false;
5769         Roo.each(this.navItems, function(v){
5770             
5771             if (v.isActive()) {
5772                 prev = v;
5773                 
5774             }
5775             
5776         });
5777         return prev;
5778     },
5779     
5780     indexOfNav : function()
5781     {
5782         
5783         var prev = false;
5784         Roo.each(this.navItems, function(v,i){
5785             
5786             if (v.isActive()) {
5787                 prev = i;
5788                 
5789             }
5790             
5791         });
5792         return prev;
5793     },
5794     /**
5795     * adds a Navigation item
5796     * @param {Roo.bootstrap.NavItem} the navitem to add
5797     */
5798     addItem : function(cfg)
5799     {
5800         if (this.form && Roo.bootstrap.version == 4) {
5801             cfg.tag = 'div';
5802         }
5803         var cn = new Roo.bootstrap.NavItem(cfg);
5804         this.register(cn);
5805         cn.parentId = this.id;
5806         cn.onRender(this.el, null);
5807         return cn;
5808     },
5809     /**
5810     * register a Navigation item
5811     * @param {Roo.bootstrap.NavItem} the navitem to add
5812     */
5813     register : function(item)
5814     {
5815         this.navItems.push( item);
5816         item.navId = this.navId;
5817     
5818     },
5819     
5820     /**
5821     * clear all the Navigation item
5822     */
5823    
5824     clearAll : function()
5825     {
5826         this.navItems = [];
5827         this.el.dom.innerHTML = '';
5828     },
5829     
5830     getNavItem: function(tabId)
5831     {
5832         var ret = false;
5833         Roo.each(this.navItems, function(e) {
5834             if (e.tabId == tabId) {
5835                ret =  e;
5836                return false;
5837             }
5838             return true;
5839             
5840         });
5841         return ret;
5842     },
5843     
5844     setActiveNext : function()
5845     {
5846         var i = this.indexOfNav(this.getActive());
5847         if (i > this.navItems.length) {
5848             return;
5849         }
5850         this.setActiveItem(this.navItems[i+1]);
5851     },
5852     setActivePrev : function()
5853     {
5854         var i = this.indexOfNav(this.getActive());
5855         if (i  < 1) {
5856             return;
5857         }
5858         this.setActiveItem(this.navItems[i-1]);
5859     },
5860     clearWasActive : function(except) {
5861         Roo.each(this.navItems, function(e) {
5862             if (e.tabId != except.tabId && e.was_active) {
5863                e.was_active = false;
5864                return false;
5865             }
5866             return true;
5867             
5868         });
5869     },
5870     getWasActive : function ()
5871     {
5872         var r = false;
5873         Roo.each(this.navItems, function(e) {
5874             if (e.was_active) {
5875                r = e;
5876                return false;
5877             }
5878             return true;
5879             
5880         });
5881         return r;
5882     }
5883     
5884     
5885 });
5886
5887  
5888 Roo.apply(Roo.bootstrap.NavGroup, {
5889     
5890     groups: {},
5891      /**
5892     * register a Navigation Group
5893     * @param {Roo.bootstrap.NavGroup} the navgroup to add
5894     */
5895     register : function(navgrp)
5896     {
5897         this.groups[navgrp.navId] = navgrp;
5898         
5899     },
5900     /**
5901     * fetch a Navigation Group based on the navigation ID
5902     * @param {string} the navgroup to add
5903     * @returns {Roo.bootstrap.NavGroup} the navgroup 
5904     */
5905     get: function(navId) {
5906         if (typeof(this.groups[navId]) == 'undefined') {
5907             return false;
5908             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
5909         }
5910         return this.groups[navId] ;
5911     }
5912     
5913     
5914     
5915 });
5916
5917  /*
5918  * - LGPL
5919  *
5920  * row
5921  * 
5922  */
5923
5924 /**
5925  * @class Roo.bootstrap.NavItem
5926  * @extends Roo.bootstrap.Component
5927  * Bootstrap Navbar.NavItem class
5928  * @cfg {String} href  link to
5929  * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
5930
5931  * @cfg {String} html content of button
5932  * @cfg {String} badge text inside badge
5933  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
5934  * @cfg {String} glyphicon DEPRICATED - use fa
5935  * @cfg {String} icon DEPRICATED - use fa
5936  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
5937  * @cfg {Boolean} active Is item active
5938  * @cfg {Boolean} disabled Is item disabled
5939  
5940  * @cfg {Boolean} preventDefault (true | false) default false
5941  * @cfg {String} tabId the tab that this item activates.
5942  * @cfg {String} tagtype (a|span) render as a href or span?
5943  * @cfg {Boolean} animateRef (true|false) link to element default false  
5944   
5945  * @constructor
5946  * Create a new Navbar Item
5947  * @param {Object} config The config object
5948  */
5949 Roo.bootstrap.NavItem = function(config){
5950     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
5951     this.addEvents({
5952         // raw events
5953         /**
5954          * @event click
5955          * The raw click event for the entire grid.
5956          * @param {Roo.EventObject} e
5957          */
5958         "click" : true,
5959          /**
5960             * @event changed
5961             * Fires when the active item active state changes
5962             * @param {Roo.bootstrap.NavItem} this
5963             * @param {boolean} state the new state
5964              
5965          */
5966         'changed': true,
5967         /**
5968             * @event scrollto
5969             * Fires when scroll to element
5970             * @param {Roo.bootstrap.NavItem} this
5971             * @param {Object} options
5972             * @param {Roo.EventObject} e
5973              
5974          */
5975         'scrollto': true
5976     });
5977    
5978 };
5979
5980 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
5981     
5982     href: false,
5983     html: '',
5984     badge: '',
5985     icon: false,
5986     fa : false,
5987     glyphicon: false,
5988     active: false,
5989     preventDefault : false,
5990     tabId : false,
5991     tagtype : 'a',
5992     tag: 'li',
5993     disabled : false,
5994     animateRef : false,
5995     was_active : false,
5996     button_weight : '',
5997     button_outline : false,
5998     
5999     navLink: false,
6000     
6001     getAutoCreate : function(){
6002          
6003         var cfg = {
6004             tag: this.tag,
6005             cls: 'nav-item'
6006         };
6007         
6008         if (this.active) {
6009             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
6010         }
6011         if (this.disabled) {
6012             cfg.cls += ' disabled';
6013         }
6014         
6015         // BS4 only?
6016         if (this.button_weight.length) {
6017             cfg.tag = this.href ? 'a' : 'button';
6018             cfg.html = this.html || '';
6019             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6020             if (this.href) {
6021                 cfg.href = this.href;
6022             }
6023             if (this.fa) {
6024                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6025             }
6026             
6027             // menu .. should add dropdown-menu class - so no need for carat..
6028             
6029             if (this.badge !== '') {
6030                  
6031                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6032             }
6033             return cfg;
6034         }
6035         
6036         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6037             cfg.cn = [
6038                 {
6039                     tag: this.tagtype,
6040                     href : this.href || "#",
6041                     html: this.html || ''
6042                 }
6043             ];
6044             if (this.tagtype == 'a') {
6045                 cfg.cn[0].cls = 'nav-link';
6046             }
6047             if (this.icon) {
6048                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6049             }
6050             if (this.fa) {
6051                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6052             }
6053             if(this.glyphicon) {
6054                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6055             }
6056             
6057             if (this.menu) {
6058                 
6059                 cfg.cn[0].html += " <span class='caret'></span>";
6060              
6061             }
6062             
6063             if (this.badge !== '') {
6064                  
6065                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6066             }
6067         }
6068         
6069         
6070         
6071         return cfg;
6072     },
6073     onRender : function(ct, position)
6074     {
6075        // Roo.log("Call onRender: " + this.xtype);
6076         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6077             this.tag = 'div';
6078         }
6079         
6080         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6081         this.navLink = this.el.select('.nav-link',true).first();
6082         return ret;
6083     },
6084       
6085     
6086     initEvents: function() 
6087     {
6088         if (typeof (this.menu) != 'undefined') {
6089             this.menu.parentType = this.xtype;
6090             this.menu.triggerEl = this.el;
6091             this.menu = this.addxtype(Roo.apply({}, this.menu));
6092         }
6093         
6094         this.el.select('a',true).on('click', this.onClick, this);
6095         
6096         if(this.tagtype == 'span'){
6097             this.el.select('span',true).on('click', this.onClick, this);
6098         }
6099        
6100         // at this point parent should be available..
6101         this.parent().register(this);
6102     },
6103     
6104     onClick : function(e)
6105     {
6106         if (e.getTarget('.dropdown-menu-item')) {
6107             // did you click on a menu itemm.... - then don't trigger onclick..
6108             return;
6109         }
6110         
6111         if(
6112                 this.preventDefault || 
6113                 this.href == '#' 
6114         ){
6115             Roo.log("NavItem - prevent Default?");
6116             e.preventDefault();
6117         }
6118         
6119         if (this.disabled) {
6120             return;
6121         }
6122         
6123         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6124         if (tg && tg.transition) {
6125             Roo.log("waiting for the transitionend");
6126             return;
6127         }
6128         
6129         
6130         
6131         //Roo.log("fire event clicked");
6132         if(this.fireEvent('click', this, e) === false){
6133             return;
6134         };
6135         
6136         if(this.tagtype == 'span'){
6137             return;
6138         }
6139         
6140         //Roo.log(this.href);
6141         var ael = this.el.select('a',true).first();
6142         //Roo.log(ael);
6143         
6144         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6145             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6146             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6147                 return; // ignore... - it's a 'hash' to another page.
6148             }
6149             Roo.log("NavItem - prevent Default?");
6150             e.preventDefault();
6151             this.scrollToElement(e);
6152         }
6153         
6154         
6155         var p =  this.parent();
6156    
6157         if (['tabs','pills'].indexOf(p.type)!==-1) {
6158             if (typeof(p.setActiveItem) !== 'undefined') {
6159                 p.setActiveItem(this);
6160             }
6161         }
6162         
6163         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6164         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6165             // remove the collapsed menu expand...
6166             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6167         }
6168     },
6169     
6170     isActive: function () {
6171         return this.active
6172     },
6173     setActive : function(state, fire, is_was_active)
6174     {
6175         if (this.active && !state && this.navId) {
6176             this.was_active = true;
6177             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6178             if (nv) {
6179                 nv.clearWasActive(this);
6180             }
6181             
6182         }
6183         this.active = state;
6184         
6185         if (!state ) {
6186             this.el.removeClass('active');
6187             this.navLink ? this.navLink.removeClass('active') : false;
6188         } else if (!this.el.hasClass('active')) {
6189             
6190             this.el.addClass('active');
6191             if (Roo.bootstrap.version == 4 && this.navLink ) {
6192                 this.navLink.addClass('active');
6193             }
6194             
6195         }
6196         if (fire) {
6197             this.fireEvent('changed', this, state);
6198         }
6199         
6200         // show a panel if it's registered and related..
6201         
6202         if (!this.navId || !this.tabId || !state || is_was_active) {
6203             return;
6204         }
6205         
6206         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6207         if (!tg) {
6208             return;
6209         }
6210         var pan = tg.getPanelByName(this.tabId);
6211         if (!pan) {
6212             return;
6213         }
6214         // if we can not flip to new panel - go back to old nav highlight..
6215         if (false == tg.showPanel(pan)) {
6216             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6217             if (nv) {
6218                 var onav = nv.getWasActive();
6219                 if (onav) {
6220                     onav.setActive(true, false, true);
6221                 }
6222             }
6223             
6224         }
6225         
6226         
6227         
6228     },
6229      // this should not be here...
6230     setDisabled : function(state)
6231     {
6232         this.disabled = state;
6233         if (!state ) {
6234             this.el.removeClass('disabled');
6235         } else if (!this.el.hasClass('disabled')) {
6236             this.el.addClass('disabled');
6237         }
6238         
6239     },
6240     
6241     /**
6242      * Fetch the element to display the tooltip on.
6243      * @return {Roo.Element} defaults to this.el
6244      */
6245     tooltipEl : function()
6246     {
6247         return this.el.select('' + this.tagtype + '', true).first();
6248     },
6249     
6250     scrollToElement : function(e)
6251     {
6252         var c = document.body;
6253         
6254         /*
6255          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6256          */
6257         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6258             c = document.documentElement;
6259         }
6260         
6261         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6262         
6263         if(!target){
6264             return;
6265         }
6266
6267         var o = target.calcOffsetsTo(c);
6268         
6269         var options = {
6270             target : target,
6271             value : o[1]
6272         };
6273         
6274         this.fireEvent('scrollto', this, options, e);
6275         
6276         Roo.get(c).scrollTo('top', options.value, true);
6277         
6278         return;
6279     }
6280 });
6281  
6282
6283  /*
6284  * - LGPL
6285  *
6286  * sidebar item
6287  *
6288  *  li
6289  *    <span> icon </span>
6290  *    <span> text </span>
6291  *    <span>badge </span>
6292  */
6293
6294 /**
6295  * @class Roo.bootstrap.NavSidebarItem
6296  * @extends Roo.bootstrap.NavItem
6297  * Bootstrap Navbar.NavSidebarItem class
6298  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6299  * {Boolean} open is the menu open
6300  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6301  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6302  * {String} buttonSize (sm|md|lg)the extra classes for the button
6303  * {Boolean} showArrow show arrow next to the text (default true)
6304  * @constructor
6305  * Create a new Navbar Button
6306  * @param {Object} config The config object
6307  */
6308 Roo.bootstrap.NavSidebarItem = function(config){
6309     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6310     this.addEvents({
6311         // raw events
6312         /**
6313          * @event click
6314          * The raw click event for the entire grid.
6315          * @param {Roo.EventObject} e
6316          */
6317         "click" : true,
6318          /**
6319             * @event changed
6320             * Fires when the active item active state changes
6321             * @param {Roo.bootstrap.NavSidebarItem} this
6322             * @param {boolean} state the new state
6323              
6324          */
6325         'changed': true
6326     });
6327    
6328 };
6329
6330 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6331     
6332     badgeWeight : 'default',
6333     
6334     open: false,
6335     
6336     buttonView : false,
6337     
6338     buttonWeight : 'default',
6339     
6340     buttonSize : 'md',
6341     
6342     showArrow : true,
6343     
6344     getAutoCreate : function(){
6345         
6346         
6347         var a = {
6348                 tag: 'a',
6349                 href : this.href || '#',
6350                 cls: '',
6351                 html : '',
6352                 cn : []
6353         };
6354         
6355         if(this.buttonView){
6356             a = {
6357                 tag: 'button',
6358                 href : this.href || '#',
6359                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6360                 html : this.html,
6361                 cn : []
6362             };
6363         }
6364         
6365         var cfg = {
6366             tag: 'li',
6367             cls: '',
6368             cn: [ a ]
6369         };
6370         
6371         if (this.active) {
6372             cfg.cls += ' active';
6373         }
6374         
6375         if (this.disabled) {
6376             cfg.cls += ' disabled';
6377         }
6378         if (this.open) {
6379             cfg.cls += ' open x-open';
6380         }
6381         // left icon..
6382         if (this.glyphicon || this.icon) {
6383             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6384             a.cn.push({ tag : 'i', cls : c }) ;
6385         }
6386         
6387         if(!this.buttonView){
6388             var span = {
6389                 tag: 'span',
6390                 html : this.html || ''
6391             };
6392
6393             a.cn.push(span);
6394             
6395         }
6396         
6397         if (this.badge !== '') {
6398             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6399         }
6400         
6401         if (this.menu) {
6402             
6403             if(this.showArrow){
6404                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6405             }
6406             
6407             a.cls += ' dropdown-toggle treeview' ;
6408         }
6409         
6410         return cfg;
6411     },
6412     
6413     initEvents : function()
6414     { 
6415         if (typeof (this.menu) != 'undefined') {
6416             this.menu.parentType = this.xtype;
6417             this.menu.triggerEl = this.el;
6418             this.menu = this.addxtype(Roo.apply({}, this.menu));
6419         }
6420         
6421         this.el.on('click', this.onClick, this);
6422         
6423         if(this.badge !== ''){
6424             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6425         }
6426         
6427     },
6428     
6429     onClick : function(e)
6430     {
6431         if(this.disabled){
6432             e.preventDefault();
6433             return;
6434         }
6435         
6436         if(this.preventDefault){
6437             e.preventDefault();
6438         }
6439         
6440         this.fireEvent('click', this, e);
6441     },
6442     
6443     disable : function()
6444     {
6445         this.setDisabled(true);
6446     },
6447     
6448     enable : function()
6449     {
6450         this.setDisabled(false);
6451     },
6452     
6453     setDisabled : function(state)
6454     {
6455         if(this.disabled == state){
6456             return;
6457         }
6458         
6459         this.disabled = state;
6460         
6461         if (state) {
6462             this.el.addClass('disabled');
6463             return;
6464         }
6465         
6466         this.el.removeClass('disabled');
6467         
6468         return;
6469     },
6470     
6471     setActive : function(state)
6472     {
6473         if(this.active == state){
6474             return;
6475         }
6476         
6477         this.active = state;
6478         
6479         if (state) {
6480             this.el.addClass('active');
6481             return;
6482         }
6483         
6484         this.el.removeClass('active');
6485         
6486         return;
6487     },
6488     
6489     isActive: function () 
6490     {
6491         return this.active;
6492     },
6493     
6494     setBadge : function(str)
6495     {
6496         if(!this.badgeEl){
6497             return;
6498         }
6499         
6500         this.badgeEl.dom.innerHTML = str;
6501     }
6502     
6503    
6504      
6505  
6506 });
6507  
6508
6509  /*
6510  * - LGPL
6511  *
6512  * row
6513  * 
6514  */
6515
6516 /**
6517  * @class Roo.bootstrap.Row
6518  * @extends Roo.bootstrap.Component
6519  * Bootstrap Row class (contains columns...)
6520  * 
6521  * @constructor
6522  * Create a new Row
6523  * @param {Object} config The config object
6524  */
6525
6526 Roo.bootstrap.Row = function(config){
6527     Roo.bootstrap.Row.superclass.constructor.call(this, config);
6528 };
6529
6530 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
6531     
6532     getAutoCreate : function(){
6533        return {
6534             cls: 'row clearfix'
6535        };
6536     }
6537     
6538     
6539 });
6540
6541  
6542
6543  /*
6544  * - LGPL
6545  *
6546  * pagination
6547  * 
6548  */
6549
6550 /**
6551  * @class Roo.bootstrap.Pagination
6552  * @extends Roo.bootstrap.Component
6553  * Bootstrap Pagination class
6554  * @cfg {String} size xs | sm | md | lg
6555  * @cfg {Boolean} inverse false | true
6556  * 
6557  * @constructor
6558  * Create a new Pagination
6559  * @param {Object} config The config object
6560  */
6561
6562 Roo.bootstrap.Pagination = function(config){
6563     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6564 };
6565
6566 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
6567     
6568     cls: false,
6569     size: false,
6570     inverse: false,
6571     
6572     getAutoCreate : function(){
6573         var cfg = {
6574             tag: 'ul',
6575                 cls: 'pagination'
6576         };
6577         if (this.inverse) {
6578             cfg.cls += ' inverse';
6579         }
6580         if (this.html) {
6581             cfg.html=this.html;
6582         }
6583         if (this.cls) {
6584             cfg.cls += " " + this.cls;
6585         }
6586         return cfg;
6587     }
6588    
6589 });
6590
6591  
6592
6593  /*
6594  * - LGPL
6595  *
6596  * Pagination item
6597  * 
6598  */
6599
6600
6601 /**
6602  * @class Roo.bootstrap.PaginationItem
6603  * @extends Roo.bootstrap.Component
6604  * Bootstrap PaginationItem class
6605  * @cfg {String} html text
6606  * @cfg {String} href the link
6607  * @cfg {Boolean} preventDefault (true | false) default true
6608  * @cfg {Boolean} active (true | false) default false
6609  * @cfg {Boolean} disabled default false
6610  * 
6611  * 
6612  * @constructor
6613  * Create a new PaginationItem
6614  * @param {Object} config The config object
6615  */
6616
6617
6618 Roo.bootstrap.PaginationItem = function(config){
6619     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6620     this.addEvents({
6621         // raw events
6622         /**
6623          * @event click
6624          * The raw click event for the entire grid.
6625          * @param {Roo.EventObject} e
6626          */
6627         "click" : true
6628     });
6629 };
6630
6631 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
6632     
6633     href : false,
6634     html : false,
6635     preventDefault: true,
6636     active : false,
6637     cls : false,
6638     disabled: false,
6639     
6640     getAutoCreate : function(){
6641         var cfg= {
6642             tag: 'li',
6643             cn: [
6644                 {
6645                     tag : 'a',
6646                     href : this.href ? this.href : '#',
6647                     html : this.html ? this.html : ''
6648                 }
6649             ]
6650         };
6651         
6652         if(this.cls){
6653             cfg.cls = this.cls;
6654         }
6655         
6656         if(this.disabled){
6657             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6658         }
6659         
6660         if(this.active){
6661             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6662         }
6663         
6664         return cfg;
6665     },
6666     
6667     initEvents: function() {
6668         
6669         this.el.on('click', this.onClick, this);
6670         
6671     },
6672     onClick : function(e)
6673     {
6674         Roo.log('PaginationItem on click ');
6675         if(this.preventDefault){
6676             e.preventDefault();
6677         }
6678         
6679         if(this.disabled){
6680             return;
6681         }
6682         
6683         this.fireEvent('click', this, e);
6684     }
6685    
6686 });
6687
6688  
6689
6690  /*
6691  * - LGPL
6692  *
6693  * slider
6694  * 
6695  */
6696
6697
6698 /**
6699  * @class Roo.bootstrap.Slider
6700  * @extends Roo.bootstrap.Component
6701  * Bootstrap Slider class
6702  *    
6703  * @constructor
6704  * Create a new Slider
6705  * @param {Object} config The config object
6706  */
6707
6708 Roo.bootstrap.Slider = function(config){
6709     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6710 };
6711
6712 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
6713     
6714     getAutoCreate : function(){
6715         
6716         var cfg = {
6717             tag: 'div',
6718             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6719             cn: [
6720                 {
6721                     tag: 'a',
6722                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
6723                 }
6724             ]
6725         };
6726         
6727         return cfg;
6728     }
6729    
6730 });
6731
6732  /*
6733  * Based on:
6734  * Ext JS Library 1.1.1
6735  * Copyright(c) 2006-2007, Ext JS, LLC.
6736  *
6737  * Originally Released Under LGPL - original licence link has changed is not relivant.
6738  *
6739  * Fork - LGPL
6740  * <script type="text/javascript">
6741  */
6742  
6743
6744 /**
6745  * @class Roo.grid.ColumnModel
6746  * @extends Roo.util.Observable
6747  * This is the default implementation of a ColumnModel used by the Grid. It defines
6748  * the columns in the grid.
6749  * <br>Usage:<br>
6750  <pre><code>
6751  var colModel = new Roo.grid.ColumnModel([
6752         {header: "Ticker", width: 60, sortable: true, locked: true},
6753         {header: "Company Name", width: 150, sortable: true},
6754         {header: "Market Cap.", width: 100, sortable: true},
6755         {header: "$ Sales", width: 100, sortable: true, renderer: money},
6756         {header: "Employees", width: 100, sortable: true, resizable: false}
6757  ]);
6758  </code></pre>
6759  * <p>
6760  
6761  * The config options listed for this class are options which may appear in each
6762  * individual column definition.
6763  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
6764  * @constructor
6765  * @param {Object} config An Array of column config objects. See this class's
6766  * config objects for details.
6767 */
6768 Roo.grid.ColumnModel = function(config){
6769         /**
6770      * The config passed into the constructor
6771      */
6772     this.config = config;
6773     this.lookup = {};
6774
6775     // if no id, create one
6776     // if the column does not have a dataIndex mapping,
6777     // map it to the order it is in the config
6778     for(var i = 0, len = config.length; i < len; i++){
6779         var c = config[i];
6780         if(typeof c.dataIndex == "undefined"){
6781             c.dataIndex = i;
6782         }
6783         if(typeof c.renderer == "string"){
6784             c.renderer = Roo.util.Format[c.renderer];
6785         }
6786         if(typeof c.id == "undefined"){
6787             c.id = Roo.id();
6788         }
6789         if(c.editor && c.editor.xtype){
6790             c.editor  = Roo.factory(c.editor, Roo.grid);
6791         }
6792         if(c.editor && c.editor.isFormField){
6793             c.editor = new Roo.grid.GridEditor(c.editor);
6794         }
6795         this.lookup[c.id] = c;
6796     }
6797
6798     /**
6799      * The width of columns which have no width specified (defaults to 100)
6800      * @type Number
6801      */
6802     this.defaultWidth = 100;
6803
6804     /**
6805      * Default sortable of columns which have no sortable specified (defaults to false)
6806      * @type Boolean
6807      */
6808     this.defaultSortable = false;
6809
6810     this.addEvents({
6811         /**
6812              * @event widthchange
6813              * Fires when the width of a column changes.
6814              * @param {ColumnModel} this
6815              * @param {Number} columnIndex The column index
6816              * @param {Number} newWidth The new width
6817              */
6818             "widthchange": true,
6819         /**
6820              * @event headerchange
6821              * Fires when the text of a header changes.
6822              * @param {ColumnModel} this
6823              * @param {Number} columnIndex The column index
6824              * @param {Number} newText The new header text
6825              */
6826             "headerchange": true,
6827         /**
6828              * @event hiddenchange
6829              * Fires when a column is hidden or "unhidden".
6830              * @param {ColumnModel} this
6831              * @param {Number} columnIndex The column index
6832              * @param {Boolean} hidden true if hidden, false otherwise
6833              */
6834             "hiddenchange": true,
6835             /**
6836          * @event columnmoved
6837          * Fires when a column is moved.
6838          * @param {ColumnModel} this
6839          * @param {Number} oldIndex
6840          * @param {Number} newIndex
6841          */
6842         "columnmoved" : true,
6843         /**
6844          * @event columlockchange
6845          * Fires when a column's locked state is changed
6846          * @param {ColumnModel} this
6847          * @param {Number} colIndex
6848          * @param {Boolean} locked true if locked
6849          */
6850         "columnlockchange" : true
6851     });
6852     Roo.grid.ColumnModel.superclass.constructor.call(this);
6853 };
6854 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
6855     /**
6856      * @cfg {String} header The header text to display in the Grid view.
6857      */
6858     /**
6859      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
6860      * {@link Roo.data.Record} definition from which to draw the column's value. If not
6861      * specified, the column's index is used as an index into the Record's data Array.
6862      */
6863     /**
6864      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
6865      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
6866      */
6867     /**
6868      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
6869      * Defaults to the value of the {@link #defaultSortable} property.
6870      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
6871      */
6872     /**
6873      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
6874      */
6875     /**
6876      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
6877      */
6878     /**
6879      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
6880      */
6881     /**
6882      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
6883      */
6884     /**
6885      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
6886      * given the cell's data value. See {@link #setRenderer}. If not specified, the
6887      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
6888      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
6889      */
6890        /**
6891      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
6892      */
6893     /**
6894      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
6895      */
6896     /**
6897      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
6898      */
6899     /**
6900      * @cfg {String} cursor (Optional)
6901      */
6902     /**
6903      * @cfg {String} tooltip (Optional)
6904      */
6905     /**
6906      * @cfg {Number} xs (Optional)
6907      */
6908     /**
6909      * @cfg {Number} sm (Optional)
6910      */
6911     /**
6912      * @cfg {Number} md (Optional)
6913      */
6914     /**
6915      * @cfg {Number} lg (Optional)
6916      */
6917     /**
6918      * Returns the id of the column at the specified index.
6919      * @param {Number} index The column index
6920      * @return {String} the id
6921      */
6922     getColumnId : function(index){
6923         return this.config[index].id;
6924     },
6925
6926     /**
6927      * Returns the column for a specified id.
6928      * @param {String} id The column id
6929      * @return {Object} the column
6930      */
6931     getColumnById : function(id){
6932         return this.lookup[id];
6933     },
6934
6935     
6936     /**
6937      * Returns the column for a specified dataIndex.
6938      * @param {String} dataIndex The column dataIndex
6939      * @return {Object|Boolean} the column or false if not found
6940      */
6941     getColumnByDataIndex: function(dataIndex){
6942         var index = this.findColumnIndex(dataIndex);
6943         return index > -1 ? this.config[index] : false;
6944     },
6945     
6946     /**
6947      * Returns the index for a specified column id.
6948      * @param {String} id The column id
6949      * @return {Number} the index, or -1 if not found
6950      */
6951     getIndexById : function(id){
6952         for(var i = 0, len = this.config.length; i < len; i++){
6953             if(this.config[i].id == id){
6954                 return i;
6955             }
6956         }
6957         return -1;
6958     },
6959     
6960     /**
6961      * Returns the index for a specified column dataIndex.
6962      * @param {String} dataIndex The column dataIndex
6963      * @return {Number} the index, or -1 if not found
6964      */
6965     
6966     findColumnIndex : function(dataIndex){
6967         for(var i = 0, len = this.config.length; i < len; i++){
6968             if(this.config[i].dataIndex == dataIndex){
6969                 return i;
6970             }
6971         }
6972         return -1;
6973     },
6974     
6975     
6976     moveColumn : function(oldIndex, newIndex){
6977         var c = this.config[oldIndex];
6978         this.config.splice(oldIndex, 1);
6979         this.config.splice(newIndex, 0, c);
6980         this.dataMap = null;
6981         this.fireEvent("columnmoved", this, oldIndex, newIndex);
6982     },
6983
6984     isLocked : function(colIndex){
6985         return this.config[colIndex].locked === true;
6986     },
6987
6988     setLocked : function(colIndex, value, suppressEvent){
6989         if(this.isLocked(colIndex) == value){
6990             return;
6991         }
6992         this.config[colIndex].locked = value;
6993         if(!suppressEvent){
6994             this.fireEvent("columnlockchange", this, colIndex, value);
6995         }
6996     },
6997
6998     getTotalLockedWidth : function(){
6999         var totalWidth = 0;
7000         for(var i = 0; i < this.config.length; i++){
7001             if(this.isLocked(i) && !this.isHidden(i)){
7002                 this.totalWidth += this.getColumnWidth(i);
7003             }
7004         }
7005         return totalWidth;
7006     },
7007
7008     getLockedCount : function(){
7009         for(var i = 0, len = this.config.length; i < len; i++){
7010             if(!this.isLocked(i)){
7011                 return i;
7012             }
7013         }
7014         
7015         return this.config.length;
7016     },
7017
7018     /**
7019      * Returns the number of columns.
7020      * @return {Number}
7021      */
7022     getColumnCount : function(visibleOnly){
7023         if(visibleOnly === true){
7024             var c = 0;
7025             for(var i = 0, len = this.config.length; i < len; i++){
7026                 if(!this.isHidden(i)){
7027                     c++;
7028                 }
7029             }
7030             return c;
7031         }
7032         return this.config.length;
7033     },
7034
7035     /**
7036      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7037      * @param {Function} fn
7038      * @param {Object} scope (optional)
7039      * @return {Array} result
7040      */
7041     getColumnsBy : function(fn, scope){
7042         var r = [];
7043         for(var i = 0, len = this.config.length; i < len; i++){
7044             var c = this.config[i];
7045             if(fn.call(scope||this, c, i) === true){
7046                 r[r.length] = c;
7047             }
7048         }
7049         return r;
7050     },
7051
7052     /**
7053      * Returns true if the specified column is sortable.
7054      * @param {Number} col The column index
7055      * @return {Boolean}
7056      */
7057     isSortable : function(col){
7058         if(typeof this.config[col].sortable == "undefined"){
7059             return this.defaultSortable;
7060         }
7061         return this.config[col].sortable;
7062     },
7063
7064     /**
7065      * Returns the rendering (formatting) function defined for the column.
7066      * @param {Number} col The column index.
7067      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7068      */
7069     getRenderer : function(col){
7070         if(!this.config[col].renderer){
7071             return Roo.grid.ColumnModel.defaultRenderer;
7072         }
7073         return this.config[col].renderer;
7074     },
7075
7076     /**
7077      * Sets the rendering (formatting) function for a column.
7078      * @param {Number} col The column index
7079      * @param {Function} fn The function to use to process the cell's raw data
7080      * to return HTML markup for the grid view. The render function is called with
7081      * the following parameters:<ul>
7082      * <li>Data value.</li>
7083      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7084      * <li>css A CSS style string to apply to the table cell.</li>
7085      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7086      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7087      * <li>Row index</li>
7088      * <li>Column index</li>
7089      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7090      */
7091     setRenderer : function(col, fn){
7092         this.config[col].renderer = fn;
7093     },
7094
7095     /**
7096      * Returns the width for the specified column.
7097      * @param {Number} col The column index
7098      * @return {Number}
7099      */
7100     getColumnWidth : function(col){
7101         return this.config[col].width * 1 || this.defaultWidth;
7102     },
7103
7104     /**
7105      * Sets the width for a column.
7106      * @param {Number} col The column index
7107      * @param {Number} width The new width
7108      */
7109     setColumnWidth : function(col, width, suppressEvent){
7110         this.config[col].width = width;
7111         this.totalWidth = null;
7112         if(!suppressEvent){
7113              this.fireEvent("widthchange", this, col, width);
7114         }
7115     },
7116
7117     /**
7118      * Returns the total width of all columns.
7119      * @param {Boolean} includeHidden True to include hidden column widths
7120      * @return {Number}
7121      */
7122     getTotalWidth : function(includeHidden){
7123         if(!this.totalWidth){
7124             this.totalWidth = 0;
7125             for(var i = 0, len = this.config.length; i < len; i++){
7126                 if(includeHidden || !this.isHidden(i)){
7127                     this.totalWidth += this.getColumnWidth(i);
7128                 }
7129             }
7130         }
7131         return this.totalWidth;
7132     },
7133
7134     /**
7135      * Returns the header for the specified column.
7136      * @param {Number} col The column index
7137      * @return {String}
7138      */
7139     getColumnHeader : function(col){
7140         return this.config[col].header;
7141     },
7142
7143     /**
7144      * Sets the header for a column.
7145      * @param {Number} col The column index
7146      * @param {String} header The new header
7147      */
7148     setColumnHeader : function(col, header){
7149         this.config[col].header = header;
7150         this.fireEvent("headerchange", this, col, header);
7151     },
7152
7153     /**
7154      * Returns the tooltip for the specified column.
7155      * @param {Number} col The column index
7156      * @return {String}
7157      */
7158     getColumnTooltip : function(col){
7159             return this.config[col].tooltip;
7160     },
7161     /**
7162      * Sets the tooltip for a column.
7163      * @param {Number} col The column index
7164      * @param {String} tooltip The new tooltip
7165      */
7166     setColumnTooltip : function(col, tooltip){
7167             this.config[col].tooltip = tooltip;
7168     },
7169
7170     /**
7171      * Returns the dataIndex for the specified column.
7172      * @param {Number} col The column index
7173      * @return {Number}
7174      */
7175     getDataIndex : function(col){
7176         return this.config[col].dataIndex;
7177     },
7178
7179     /**
7180      * Sets the dataIndex for a column.
7181      * @param {Number} col The column index
7182      * @param {Number} dataIndex The new dataIndex
7183      */
7184     setDataIndex : function(col, dataIndex){
7185         this.config[col].dataIndex = dataIndex;
7186     },
7187
7188     
7189     
7190     /**
7191      * Returns true if the cell is editable.
7192      * @param {Number} colIndex The column index
7193      * @param {Number} rowIndex The row index - this is nto actually used..?
7194      * @return {Boolean}
7195      */
7196     isCellEditable : function(colIndex, rowIndex){
7197         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7198     },
7199
7200     /**
7201      * Returns the editor defined for the cell/column.
7202      * return false or null to disable editing.
7203      * @param {Number} colIndex The column index
7204      * @param {Number} rowIndex The row index
7205      * @return {Object}
7206      */
7207     getCellEditor : function(colIndex, rowIndex){
7208         return this.config[colIndex].editor;
7209     },
7210
7211     /**
7212      * Sets if a column is editable.
7213      * @param {Number} col The column index
7214      * @param {Boolean} editable True if the column is editable
7215      */
7216     setEditable : function(col, editable){
7217         this.config[col].editable = editable;
7218     },
7219
7220
7221     /**
7222      * Returns true if the column is hidden.
7223      * @param {Number} colIndex The column index
7224      * @return {Boolean}
7225      */
7226     isHidden : function(colIndex){
7227         return this.config[colIndex].hidden;
7228     },
7229
7230
7231     /**
7232      * Returns true if the column width cannot be changed
7233      */
7234     isFixed : function(colIndex){
7235         return this.config[colIndex].fixed;
7236     },
7237
7238     /**
7239      * Returns true if the column can be resized
7240      * @return {Boolean}
7241      */
7242     isResizable : function(colIndex){
7243         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7244     },
7245     /**
7246      * Sets if a column is hidden.
7247      * @param {Number} colIndex The column index
7248      * @param {Boolean} hidden True if the column is hidden
7249      */
7250     setHidden : function(colIndex, hidden){
7251         this.config[colIndex].hidden = hidden;
7252         this.totalWidth = null;
7253         this.fireEvent("hiddenchange", this, colIndex, hidden);
7254     },
7255
7256     /**
7257      * Sets the editor for a column.
7258      * @param {Number} col The column index
7259      * @param {Object} editor The editor object
7260      */
7261     setEditor : function(col, editor){
7262         this.config[col].editor = editor;
7263     }
7264 });
7265
7266 Roo.grid.ColumnModel.defaultRenderer = function(value)
7267 {
7268     if(typeof value == "object") {
7269         return value;
7270     }
7271         if(typeof value == "string" && value.length < 1){
7272             return "&#160;";
7273         }
7274     
7275         return String.format("{0}", value);
7276 };
7277
7278 // Alias for backwards compatibility
7279 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7280 /*
7281  * Based on:
7282  * Ext JS Library 1.1.1
7283  * Copyright(c) 2006-2007, Ext JS, LLC.
7284  *
7285  * Originally Released Under LGPL - original licence link has changed is not relivant.
7286  *
7287  * Fork - LGPL
7288  * <script type="text/javascript">
7289  */
7290  
7291 /**
7292  * @class Roo.LoadMask
7293  * A simple utility class for generically masking elements while loading data.  If the element being masked has
7294  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7295  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
7296  * element's UpdateManager load indicator and will be destroyed after the initial load.
7297  * @constructor
7298  * Create a new LoadMask
7299  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7300  * @param {Object} config The config object
7301  */
7302 Roo.LoadMask = function(el, config){
7303     this.el = Roo.get(el);
7304     Roo.apply(this, config);
7305     if(this.store){
7306         this.store.on('beforeload', this.onBeforeLoad, this);
7307         this.store.on('load', this.onLoad, this);
7308         this.store.on('loadexception', this.onLoadException, this);
7309         this.removeMask = false;
7310     }else{
7311         var um = this.el.getUpdateManager();
7312         um.showLoadIndicator = false; // disable the default indicator
7313         um.on('beforeupdate', this.onBeforeLoad, this);
7314         um.on('update', this.onLoad, this);
7315         um.on('failure', this.onLoad, this);
7316         this.removeMask = true;
7317     }
7318 };
7319
7320 Roo.LoadMask.prototype = {
7321     /**
7322      * @cfg {Boolean} removeMask
7323      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7324      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7325      */
7326     /**
7327      * @cfg {String} msg
7328      * The text to display in a centered loading message box (defaults to 'Loading...')
7329      */
7330     msg : 'Loading...',
7331     /**
7332      * @cfg {String} msgCls
7333      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7334      */
7335     msgCls : 'x-mask-loading',
7336
7337     /**
7338      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7339      * @type Boolean
7340      */
7341     disabled: false,
7342
7343     /**
7344      * Disables the mask to prevent it from being displayed
7345      */
7346     disable : function(){
7347        this.disabled = true;
7348     },
7349
7350     /**
7351      * Enables the mask so that it can be displayed
7352      */
7353     enable : function(){
7354         this.disabled = false;
7355     },
7356     
7357     onLoadException : function()
7358     {
7359         Roo.log(arguments);
7360         
7361         if (typeof(arguments[3]) != 'undefined') {
7362             Roo.MessageBox.alert("Error loading",arguments[3]);
7363         } 
7364         /*
7365         try {
7366             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7367                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7368             }   
7369         } catch(e) {
7370             
7371         }
7372         */
7373     
7374         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7375     },
7376     // private
7377     onLoad : function()
7378     {
7379         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7380     },
7381
7382     // private
7383     onBeforeLoad : function(){
7384         if(!this.disabled){
7385             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7386         }
7387     },
7388
7389     // private
7390     destroy : function(){
7391         if(this.store){
7392             this.store.un('beforeload', this.onBeforeLoad, this);
7393             this.store.un('load', this.onLoad, this);
7394             this.store.un('loadexception', this.onLoadException, this);
7395         }else{
7396             var um = this.el.getUpdateManager();
7397             um.un('beforeupdate', this.onBeforeLoad, this);
7398             um.un('update', this.onLoad, this);
7399             um.un('failure', this.onLoad, this);
7400         }
7401     }
7402 };/*
7403  * - LGPL
7404  *
7405  * table
7406  * 
7407  */
7408
7409 /**
7410  * @class Roo.bootstrap.Table
7411  * @extends Roo.bootstrap.Component
7412  * Bootstrap Table class
7413  * @cfg {String} cls table class
7414  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7415  * @cfg {String} bgcolor Specifies the background color for a table
7416  * @cfg {Number} border Specifies whether the table cells should have borders or not
7417  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7418  * @cfg {Number} cellspacing Specifies the space between cells
7419  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7420  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7421  * @cfg {String} sortable Specifies that the table should be sortable
7422  * @cfg {String} summary Specifies a summary of the content of a table
7423  * @cfg {Number} width Specifies the width of a table
7424  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7425  * 
7426  * @cfg {boolean} striped Should the rows be alternative striped
7427  * @cfg {boolean} bordered Add borders to the table
7428  * @cfg {boolean} hover Add hover highlighting
7429  * @cfg {boolean} condensed Format condensed
7430  * @cfg {boolean} responsive Format condensed
7431  * @cfg {Boolean} loadMask (true|false) default false
7432  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7433  * @cfg {Boolean} headerShow (true|false) generate thead, default true
7434  * @cfg {Boolean} rowSelection (true|false) default false
7435  * @cfg {Boolean} cellSelection (true|false) default false
7436  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7437  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
7438  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
7439  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
7440  
7441  * 
7442  * @constructor
7443  * Create a new Table
7444  * @param {Object} config The config object
7445  */
7446
7447 Roo.bootstrap.Table = function(config){
7448     Roo.bootstrap.Table.superclass.constructor.call(this, config);
7449     
7450   
7451     
7452     // BC...
7453     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7454     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7455     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7456     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7457     
7458     this.sm = this.sm || {xtype: 'RowSelectionModel'};
7459     if (this.sm) {
7460         this.sm.grid = this;
7461         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7462         this.sm = this.selModel;
7463         this.sm.xmodule = this.xmodule || false;
7464     }
7465     
7466     if (this.cm && typeof(this.cm.config) == 'undefined') {
7467         this.colModel = new Roo.grid.ColumnModel(this.cm);
7468         this.cm = this.colModel;
7469         this.cm.xmodule = this.xmodule || false;
7470     }
7471     if (this.store) {
7472         this.store= Roo.factory(this.store, Roo.data);
7473         this.ds = this.store;
7474         this.ds.xmodule = this.xmodule || false;
7475          
7476     }
7477     if (this.footer && this.store) {
7478         this.footer.dataSource = this.ds;
7479         this.footer = Roo.factory(this.footer);
7480     }
7481     
7482     /** @private */
7483     this.addEvents({
7484         /**
7485          * @event cellclick
7486          * Fires when a cell is clicked
7487          * @param {Roo.bootstrap.Table} this
7488          * @param {Roo.Element} el
7489          * @param {Number} rowIndex
7490          * @param {Number} columnIndex
7491          * @param {Roo.EventObject} e
7492          */
7493         "cellclick" : true,
7494         /**
7495          * @event celldblclick
7496          * Fires when a cell is double clicked
7497          * @param {Roo.bootstrap.Table} this
7498          * @param {Roo.Element} el
7499          * @param {Number} rowIndex
7500          * @param {Number} columnIndex
7501          * @param {Roo.EventObject} e
7502          */
7503         "celldblclick" : true,
7504         /**
7505          * @event rowclick
7506          * Fires when a row is clicked
7507          * @param {Roo.bootstrap.Table} this
7508          * @param {Roo.Element} el
7509          * @param {Number} rowIndex
7510          * @param {Roo.EventObject} e
7511          */
7512         "rowclick" : true,
7513         /**
7514          * @event rowdblclick
7515          * Fires when a row is double clicked
7516          * @param {Roo.bootstrap.Table} this
7517          * @param {Roo.Element} el
7518          * @param {Number} rowIndex
7519          * @param {Roo.EventObject} e
7520          */
7521         "rowdblclick" : true,
7522         /**
7523          * @event mouseover
7524          * Fires when a mouseover occur
7525          * @param {Roo.bootstrap.Table} this
7526          * @param {Roo.Element} el
7527          * @param {Number} rowIndex
7528          * @param {Number} columnIndex
7529          * @param {Roo.EventObject} e
7530          */
7531         "mouseover" : true,
7532         /**
7533          * @event mouseout
7534          * Fires when a mouseout occur
7535          * @param {Roo.bootstrap.Table} this
7536          * @param {Roo.Element} el
7537          * @param {Number} rowIndex
7538          * @param {Number} columnIndex
7539          * @param {Roo.EventObject} e
7540          */
7541         "mouseout" : true,
7542         /**
7543          * @event rowclass
7544          * Fires when a row is rendered, so you can change add a style to it.
7545          * @param {Roo.bootstrap.Table} this
7546          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
7547          */
7548         'rowclass' : true,
7549           /**
7550          * @event rowsrendered
7551          * Fires when all the  rows have been rendered
7552          * @param {Roo.bootstrap.Table} this
7553          */
7554         'rowsrendered' : true,
7555         /**
7556          * @event contextmenu
7557          * The raw contextmenu event for the entire grid.
7558          * @param {Roo.EventObject} e
7559          */
7560         "contextmenu" : true,
7561         /**
7562          * @event rowcontextmenu
7563          * Fires when a row is right clicked
7564          * @param {Roo.bootstrap.Table} this
7565          * @param {Number} rowIndex
7566          * @param {Roo.EventObject} e
7567          */
7568         "rowcontextmenu" : true,
7569         /**
7570          * @event cellcontextmenu
7571          * Fires when a cell is right clicked
7572          * @param {Roo.bootstrap.Table} this
7573          * @param {Number} rowIndex
7574          * @param {Number} cellIndex
7575          * @param {Roo.EventObject} e
7576          */
7577          "cellcontextmenu" : true,
7578          /**
7579          * @event headercontextmenu
7580          * Fires when a header is right clicked
7581          * @param {Roo.bootstrap.Table} this
7582          * @param {Number} columnIndex
7583          * @param {Roo.EventObject} e
7584          */
7585         "headercontextmenu" : true
7586     });
7587 };
7588
7589 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
7590     
7591     cls: false,
7592     align: false,
7593     bgcolor: false,
7594     border: false,
7595     cellpadding: false,
7596     cellspacing: false,
7597     frame: false,
7598     rules: false,
7599     sortable: false,
7600     summary: false,
7601     width: false,
7602     striped : false,
7603     scrollBody : false,
7604     bordered: false,
7605     hover:  false,
7606     condensed : false,
7607     responsive : false,
7608     sm : false,
7609     cm : false,
7610     store : false,
7611     loadMask : false,
7612     footerShow : true,
7613     headerShow : true,
7614   
7615     rowSelection : false,
7616     cellSelection : false,
7617     layout : false,
7618     
7619     // Roo.Element - the tbody
7620     mainBody: false,
7621     // Roo.Element - thead element
7622     mainHead: false,
7623     
7624     container: false, // used by gridpanel...
7625     
7626     lazyLoad : false,
7627     
7628     CSS : Roo.util.CSS,
7629     
7630     auto_hide_footer : false,
7631     
7632     getAutoCreate : function()
7633     {
7634         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7635         
7636         cfg = {
7637             tag: 'table',
7638             cls : 'table',
7639             cn : []
7640         };
7641         if (this.scrollBody) {
7642             cfg.cls += ' table-body-fixed';
7643         }    
7644         if (this.striped) {
7645             cfg.cls += ' table-striped';
7646         }
7647         
7648         if (this.hover) {
7649             cfg.cls += ' table-hover';
7650         }
7651         if (this.bordered) {
7652             cfg.cls += ' table-bordered';
7653         }
7654         if (this.condensed) {
7655             cfg.cls += ' table-condensed';
7656         }
7657         if (this.responsive) {
7658             cfg.cls += ' table-responsive';
7659         }
7660         
7661         if (this.cls) {
7662             cfg.cls+=  ' ' +this.cls;
7663         }
7664         
7665         // this lot should be simplifed...
7666         var _t = this;
7667         var cp = [
7668             'align',
7669             'bgcolor',
7670             'border',
7671             'cellpadding',
7672             'cellspacing',
7673             'frame',
7674             'rules',
7675             'sortable',
7676             'summary',
7677             'width'
7678         ].forEach(function(k) {
7679             if (_t[k]) {
7680                 cfg[k] = _t[k];
7681             }
7682         });
7683         
7684         
7685         if (this.layout) {
7686             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7687         }
7688         
7689         if(this.store || this.cm){
7690             if(this.headerShow){
7691                 cfg.cn.push(this.renderHeader());
7692             }
7693             
7694             cfg.cn.push(this.renderBody());
7695             
7696             if(this.footerShow){
7697                 cfg.cn.push(this.renderFooter());
7698             }
7699             // where does this come from?
7700             //cfg.cls+=  ' TableGrid';
7701         }
7702         
7703         return { cn : [ cfg ] };
7704     },
7705     
7706     initEvents : function()
7707     {   
7708         if(!this.store || !this.cm){
7709             return;
7710         }
7711         if (this.selModel) {
7712             this.selModel.initEvents();
7713         }
7714         
7715         
7716         //Roo.log('initEvents with ds!!!!');
7717         
7718         this.mainBody = this.el.select('tbody', true).first();
7719         this.mainHead = this.el.select('thead', true).first();
7720         this.mainFoot = this.el.select('tfoot', true).first();
7721         
7722         
7723         
7724         var _this = this;
7725         
7726         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7727             e.on('click', _this.sort, _this);
7728         });
7729         
7730         this.mainBody.on("click", this.onClick, this);
7731         this.mainBody.on("dblclick", this.onDblClick, this);
7732         
7733         // why is this done????? = it breaks dialogs??
7734         //this.parent().el.setStyle('position', 'relative');
7735         
7736         
7737         if (this.footer) {
7738             this.footer.parentId = this.id;
7739             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
7740             
7741             if(this.lazyLoad){
7742                 this.el.select('tfoot tr td').first().addClass('hide');
7743             }
7744         } 
7745         
7746         if(this.loadMask) {
7747             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
7748         }
7749         
7750         this.store.on('load', this.onLoad, this);
7751         this.store.on('beforeload', this.onBeforeLoad, this);
7752         this.store.on('update', this.onUpdate, this);
7753         this.store.on('add', this.onAdd, this);
7754         this.store.on("clear", this.clear, this);
7755         
7756         this.el.on("contextmenu", this.onContextMenu, this);
7757         
7758         this.mainBody.on('scroll', this.onBodyScroll, this);
7759         
7760         this.cm.on("headerchange", this.onHeaderChange, this);
7761         
7762         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
7763         
7764     },
7765     
7766     onContextMenu : function(e, t)
7767     {
7768         this.processEvent("contextmenu", e);
7769     },
7770     
7771     processEvent : function(name, e)
7772     {
7773         if (name != 'touchstart' ) {
7774             this.fireEvent(name, e);    
7775         }
7776         
7777         var t = e.getTarget();
7778         
7779         var cell = Roo.get(t);
7780         
7781         if(!cell){
7782             return;
7783         }
7784         
7785         if(cell.findParent('tfoot', false, true)){
7786             return;
7787         }
7788         
7789         if(cell.findParent('thead', false, true)){
7790             
7791             if(e.getTarget().nodeName.toLowerCase() != 'th'){
7792                 cell = Roo.get(t).findParent('th', false, true);
7793                 if (!cell) {
7794                     Roo.log("failed to find th in thead?");
7795                     Roo.log(e.getTarget());
7796                     return;
7797                 }
7798             }
7799             
7800             var cellIndex = cell.dom.cellIndex;
7801             
7802             var ename = name == 'touchstart' ? 'click' : name;
7803             this.fireEvent("header" + ename, this, cellIndex, e);
7804             
7805             return;
7806         }
7807         
7808         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7809             cell = Roo.get(t).findParent('td', false, true);
7810             if (!cell) {
7811                 Roo.log("failed to find th in tbody?");
7812                 Roo.log(e.getTarget());
7813                 return;
7814             }
7815         }
7816         
7817         var row = cell.findParent('tr', false, true);
7818         var cellIndex = cell.dom.cellIndex;
7819         var rowIndex = row.dom.rowIndex - 1;
7820         
7821         if(row !== false){
7822             
7823             this.fireEvent("row" + name, this, rowIndex, e);
7824             
7825             if(cell !== false){
7826             
7827                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
7828             }
7829         }
7830         
7831     },
7832     
7833     onMouseover : function(e, el)
7834     {
7835         var cell = Roo.get(el);
7836         
7837         if(!cell){
7838             return;
7839         }
7840         
7841         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7842             cell = cell.findParent('td', false, true);
7843         }
7844         
7845         var row = cell.findParent('tr', false, true);
7846         var cellIndex = cell.dom.cellIndex;
7847         var rowIndex = row.dom.rowIndex - 1; // start from 0
7848         
7849         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
7850         
7851     },
7852     
7853     onMouseout : function(e, el)
7854     {
7855         var cell = Roo.get(el);
7856         
7857         if(!cell){
7858             return;
7859         }
7860         
7861         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7862             cell = cell.findParent('td', false, true);
7863         }
7864         
7865         var row = cell.findParent('tr', false, true);
7866         var cellIndex = cell.dom.cellIndex;
7867         var rowIndex = row.dom.rowIndex - 1; // start from 0
7868         
7869         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
7870         
7871     },
7872     
7873     onClick : function(e, el)
7874     {
7875         var cell = Roo.get(el);
7876         
7877         if(!cell || (!this.cellSelection && !this.rowSelection)){
7878             return;
7879         }
7880         
7881         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7882             cell = cell.findParent('td', false, true);
7883         }
7884         
7885         if(!cell || typeof(cell) == 'undefined'){
7886             return;
7887         }
7888         
7889         var row = cell.findParent('tr', false, true);
7890         
7891         if(!row || typeof(row) == 'undefined'){
7892             return;
7893         }
7894         
7895         var cellIndex = cell.dom.cellIndex;
7896         var rowIndex = this.getRowIndex(row);
7897         
7898         // why??? - should these not be based on SelectionModel?
7899         if(this.cellSelection){
7900             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
7901         }
7902         
7903         if(this.rowSelection){
7904             this.fireEvent('rowclick', this, row, rowIndex, e);
7905         }
7906         
7907         
7908     },
7909         
7910     onDblClick : function(e,el)
7911     {
7912         var cell = Roo.get(el);
7913         
7914         if(!cell || (!this.cellSelection && !this.rowSelection)){
7915             return;
7916         }
7917         
7918         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7919             cell = cell.findParent('td', false, true);
7920         }
7921         
7922         if(!cell || typeof(cell) == 'undefined'){
7923             return;
7924         }
7925         
7926         var row = cell.findParent('tr', false, true);
7927         
7928         if(!row || typeof(row) == 'undefined'){
7929             return;
7930         }
7931         
7932         var cellIndex = cell.dom.cellIndex;
7933         var rowIndex = this.getRowIndex(row);
7934         
7935         if(this.cellSelection){
7936             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
7937         }
7938         
7939         if(this.rowSelection){
7940             this.fireEvent('rowdblclick', this, row, rowIndex, e);
7941         }
7942     },
7943     
7944     sort : function(e,el)
7945     {
7946         var col = Roo.get(el);
7947         
7948         if(!col.hasClass('sortable')){
7949             return;
7950         }
7951         
7952         var sort = col.attr('sort');
7953         var dir = 'ASC';
7954         
7955         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
7956             dir = 'DESC';
7957         }
7958         
7959         this.store.sortInfo = {field : sort, direction : dir};
7960         
7961         if (this.footer) {
7962             Roo.log("calling footer first");
7963             this.footer.onClick('first');
7964         } else {
7965         
7966             this.store.load({ params : { start : 0 } });
7967         }
7968     },
7969     
7970     renderHeader : function()
7971     {
7972         var header = {
7973             tag: 'thead',
7974             cn : []
7975         };
7976         
7977         var cm = this.cm;
7978         this.totalWidth = 0;
7979         
7980         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7981             
7982             var config = cm.config[i];
7983             
7984             var c = {
7985                 tag: 'th',
7986                 cls : 'x-hcol-' + i,
7987                 style : '',
7988                 html: cm.getColumnHeader(i)
7989             };
7990             
7991             var hh = '';
7992             
7993             if(typeof(config.sortable) != 'undefined' && config.sortable){
7994                 c.cls = 'sortable';
7995                 c.html = '<i class="glyphicon"></i>' + c.html;
7996             }
7997             
7998             // could use BS4 hidden-..-down 
7999             
8000             if(typeof(config.lgHeader) != 'undefined'){
8001                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8002             }
8003             
8004             if(typeof(config.mdHeader) != 'undefined'){
8005                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8006             }
8007             
8008             if(typeof(config.smHeader) != 'undefined'){
8009                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8010             }
8011             
8012             if(typeof(config.xsHeader) != 'undefined'){
8013                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8014             }
8015             
8016             if(hh.length){
8017                 c.html = hh;
8018             }
8019             
8020             if(typeof(config.tooltip) != 'undefined'){
8021                 c.tooltip = config.tooltip;
8022             }
8023             
8024             if(typeof(config.colspan) != 'undefined'){
8025                 c.colspan = config.colspan;
8026             }
8027             
8028             if(typeof(config.hidden) != 'undefined' && config.hidden){
8029                 c.style += ' display:none;';
8030             }
8031             
8032             if(typeof(config.dataIndex) != 'undefined'){
8033                 c.sort = config.dataIndex;
8034             }
8035             
8036            
8037             
8038             if(typeof(config.align) != 'undefined' && config.align.length){
8039                 c.style += ' text-align:' + config.align + ';';
8040             }
8041             
8042             if(typeof(config.width) != 'undefined'){
8043                 c.style += ' width:' + config.width + 'px;';
8044                 this.totalWidth += config.width;
8045             } else {
8046                 this.totalWidth += 100; // assume minimum of 100 per column?
8047             }
8048             
8049             if(typeof(config.cls) != 'undefined'){
8050                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8051             }
8052             
8053             ['xs','sm','md','lg'].map(function(size){
8054                 
8055                 if(typeof(config[size]) == 'undefined'){
8056                     return;
8057                 }
8058                  
8059                 if (!config[size]) { // 0 = hidden
8060                     // BS 4 '0' is treated as hide that column and below.
8061                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8062                     return;
8063                 }
8064                 
8065                 c.cls += ' col-' + size + '-' + config[size] + (
8066                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8067                 );
8068                 
8069                 
8070             });
8071             
8072             header.cn.push(c)
8073         }
8074         
8075         return header;
8076     },
8077     
8078     renderBody : function()
8079     {
8080         var body = {
8081             tag: 'tbody',
8082             cn : [
8083                 {
8084                     tag: 'tr',
8085                     cn : [
8086                         {
8087                             tag : 'td',
8088                             colspan :  this.cm.getColumnCount()
8089                         }
8090                     ]
8091                 }
8092             ]
8093         };
8094         
8095         return body;
8096     },
8097     
8098     renderFooter : function()
8099     {
8100         var footer = {
8101             tag: 'tfoot',
8102             cn : [
8103                 {
8104                     tag: 'tr',
8105                     cn : [
8106                         {
8107                             tag : 'td',
8108                             colspan :  this.cm.getColumnCount()
8109                         }
8110                     ]
8111                 }
8112             ]
8113         };
8114         
8115         return footer;
8116     },
8117     
8118     
8119     
8120     onLoad : function()
8121     {
8122 //        Roo.log('ds onload');
8123         this.clear();
8124         
8125         var _this = this;
8126         var cm = this.cm;
8127         var ds = this.store;
8128         
8129         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8130             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8131             if (_this.store.sortInfo) {
8132                     
8133                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8134                     e.select('i', true).addClass(['glyphicon-arrow-up']);
8135                 }
8136                 
8137                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8138                     e.select('i', true).addClass(['glyphicon-arrow-down']);
8139                 }
8140             }
8141         });
8142         
8143         var tbody =  this.mainBody;
8144               
8145         if(ds.getCount() > 0){
8146             ds.data.each(function(d,rowIndex){
8147                 var row =  this.renderRow(cm, ds, rowIndex);
8148                 
8149                 tbody.createChild(row);
8150                 
8151                 var _this = this;
8152                 
8153                 if(row.cellObjects.length){
8154                     Roo.each(row.cellObjects, function(r){
8155                         _this.renderCellObject(r);
8156                     })
8157                 }
8158                 
8159             }, this);
8160         }
8161         
8162         var tfoot = this.el.select('tfoot', true).first();
8163         
8164         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8165             
8166             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8167             
8168             var total = this.ds.getTotalCount();
8169             
8170             if(this.footer.pageSize < total){
8171                 this.mainFoot.show();
8172             }
8173         }
8174         
8175         Roo.each(this.el.select('tbody td', true).elements, function(e){
8176             e.on('mouseover', _this.onMouseover, _this);
8177         });
8178         
8179         Roo.each(this.el.select('tbody td', true).elements, function(e){
8180             e.on('mouseout', _this.onMouseout, _this);
8181         });
8182         this.fireEvent('rowsrendered', this);
8183         
8184         this.autoSize();
8185     },
8186     
8187     
8188     onUpdate : function(ds,record)
8189     {
8190         this.refreshRow(record);
8191         this.autoSize();
8192     },
8193     
8194     onRemove : function(ds, record, index, isUpdate){
8195         if(isUpdate !== true){
8196             this.fireEvent("beforerowremoved", this, index, record);
8197         }
8198         var bt = this.mainBody.dom;
8199         
8200         var rows = this.el.select('tbody > tr', true).elements;
8201         
8202         if(typeof(rows[index]) != 'undefined'){
8203             bt.removeChild(rows[index].dom);
8204         }
8205         
8206 //        if(bt.rows[index]){
8207 //            bt.removeChild(bt.rows[index]);
8208 //        }
8209         
8210         if(isUpdate !== true){
8211             //this.stripeRows(index);
8212             //this.syncRowHeights(index, index);
8213             //this.layout();
8214             this.fireEvent("rowremoved", this, index, record);
8215         }
8216     },
8217     
8218     onAdd : function(ds, records, rowIndex)
8219     {
8220         //Roo.log('on Add called');
8221         // - note this does not handle multiple adding very well..
8222         var bt = this.mainBody.dom;
8223         for (var i =0 ; i < records.length;i++) {
8224             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8225             //Roo.log(records[i]);
8226             //Roo.log(this.store.getAt(rowIndex+i));
8227             this.insertRow(this.store, rowIndex + i, false);
8228             return;
8229         }
8230         
8231     },
8232     
8233     
8234     refreshRow : function(record){
8235         var ds = this.store, index;
8236         if(typeof record == 'number'){
8237             index = record;
8238             record = ds.getAt(index);
8239         }else{
8240             index = ds.indexOf(record);
8241         }
8242         this.insertRow(ds, index, true);
8243         this.autoSize();
8244         this.onRemove(ds, record, index+1, true);
8245         this.autoSize();
8246         //this.syncRowHeights(index, index);
8247         //this.layout();
8248         this.fireEvent("rowupdated", this, index, record);
8249     },
8250     
8251     insertRow : function(dm, rowIndex, isUpdate){
8252         
8253         if(!isUpdate){
8254             this.fireEvent("beforerowsinserted", this, rowIndex);
8255         }
8256             //var s = this.getScrollState();
8257         var row = this.renderRow(this.cm, this.store, rowIndex);
8258         // insert before rowIndex..
8259         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8260         
8261         var _this = this;
8262                 
8263         if(row.cellObjects.length){
8264             Roo.each(row.cellObjects, function(r){
8265                 _this.renderCellObject(r);
8266             })
8267         }
8268             
8269         if(!isUpdate){
8270             this.fireEvent("rowsinserted", this, rowIndex);
8271             //this.syncRowHeights(firstRow, lastRow);
8272             //this.stripeRows(firstRow);
8273             //this.layout();
8274         }
8275         
8276     },
8277     
8278     
8279     getRowDom : function(rowIndex)
8280     {
8281         var rows = this.el.select('tbody > tr', true).elements;
8282         
8283         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8284         
8285     },
8286     // returns the object tree for a tr..
8287   
8288     
8289     renderRow : function(cm, ds, rowIndex) 
8290     {
8291         var d = ds.getAt(rowIndex);
8292         
8293         var row = {
8294             tag : 'tr',
8295             cls : 'x-row-' + rowIndex,
8296             cn : []
8297         };
8298             
8299         var cellObjects = [];
8300         
8301         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8302             var config = cm.config[i];
8303             
8304             var renderer = cm.getRenderer(i);
8305             var value = '';
8306             var id = false;
8307             
8308             if(typeof(renderer) !== 'undefined'){
8309                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8310             }
8311             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8312             // and are rendered into the cells after the row is rendered - using the id for the element.
8313             
8314             if(typeof(value) === 'object'){
8315                 id = Roo.id();
8316                 cellObjects.push({
8317                     container : id,
8318                     cfg : value 
8319                 })
8320             }
8321             
8322             var rowcfg = {
8323                 record: d,
8324                 rowIndex : rowIndex,
8325                 colIndex : i,
8326                 rowClass : ''
8327             };
8328
8329             this.fireEvent('rowclass', this, rowcfg);
8330             
8331             var td = {
8332                 tag: 'td',
8333                 cls : rowcfg.rowClass + ' x-col-' + i,
8334                 style: '',
8335                 html: (typeof(value) === 'object') ? '' : value
8336             };
8337             
8338             if (id) {
8339                 td.id = id;
8340             }
8341             
8342             if(typeof(config.colspan) != 'undefined'){
8343                 td.colspan = config.colspan;
8344             }
8345             
8346             if(typeof(config.hidden) != 'undefined' && config.hidden){
8347                 td.style += ' display:none;';
8348             }
8349             
8350             if(typeof(config.align) != 'undefined' && config.align.length){
8351                 td.style += ' text-align:' + config.align + ';';
8352             }
8353             if(typeof(config.valign) != 'undefined' && config.valign.length){
8354                 td.style += ' vertical-align:' + config.valign + ';';
8355             }
8356             
8357             if(typeof(config.width) != 'undefined'){
8358                 td.style += ' width:' +  config.width + 'px;';
8359             }
8360             
8361             if(typeof(config.cursor) != 'undefined'){
8362                 td.style += ' cursor:' +  config.cursor + ';';
8363             }
8364             
8365             if(typeof(config.cls) != 'undefined'){
8366                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8367             }
8368             
8369             ['xs','sm','md','lg'].map(function(size){
8370                 
8371                 if(typeof(config[size]) == 'undefined'){
8372                     return;
8373                 }
8374                 
8375                 
8376                   
8377                 if (!config[size]) { // 0 = hidden
8378                     // BS 4 '0' is treated as hide that column and below.
8379                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8380                     return;
8381                 }
8382                 
8383                 td.cls += ' col-' + size + '-' + config[size] + (
8384                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8385                 );
8386                  
8387
8388             });
8389             
8390             row.cn.push(td);
8391            
8392         }
8393         
8394         row.cellObjects = cellObjects;
8395         
8396         return row;
8397           
8398     },
8399     
8400     
8401     
8402     onBeforeLoad : function()
8403     {
8404         
8405     },
8406      /**
8407      * Remove all rows
8408      */
8409     clear : function()
8410     {
8411         this.el.select('tbody', true).first().dom.innerHTML = '';
8412     },
8413     /**
8414      * Show or hide a row.
8415      * @param {Number} rowIndex to show or hide
8416      * @param {Boolean} state hide
8417      */
8418     setRowVisibility : function(rowIndex, state)
8419     {
8420         var bt = this.mainBody.dom;
8421         
8422         var rows = this.el.select('tbody > tr', true).elements;
8423         
8424         if(typeof(rows[rowIndex]) == 'undefined'){
8425             return;
8426         }
8427         rows[rowIndex].dom.style.display = state ? '' : 'none';
8428     },
8429     
8430     
8431     getSelectionModel : function(){
8432         if(!this.selModel){
8433             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8434         }
8435         return this.selModel;
8436     },
8437     /*
8438      * Render the Roo.bootstrap object from renderder
8439      */
8440     renderCellObject : function(r)
8441     {
8442         var _this = this;
8443         
8444         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8445         
8446         var t = r.cfg.render(r.container);
8447         
8448         if(r.cfg.cn){
8449             Roo.each(r.cfg.cn, function(c){
8450                 var child = {
8451                     container: t.getChildContainer(),
8452                     cfg: c
8453                 };
8454                 _this.renderCellObject(child);
8455             })
8456         }
8457     },
8458     
8459     getRowIndex : function(row)
8460     {
8461         var rowIndex = -1;
8462         
8463         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8464             if(el != row){
8465                 return;
8466             }
8467             
8468             rowIndex = index;
8469         });
8470         
8471         return rowIndex;
8472     },
8473      /**
8474      * Returns the grid's underlying element = used by panel.Grid
8475      * @return {Element} The element
8476      */
8477     getGridEl : function(){
8478         return this.el;
8479     },
8480      /**
8481      * Forces a resize - used by panel.Grid
8482      * @return {Element} The element
8483      */
8484     autoSize : function()
8485     {
8486         //var ctr = Roo.get(this.container.dom.parentElement);
8487         var ctr = Roo.get(this.el.dom);
8488         
8489         var thd = this.getGridEl().select('thead',true).first();
8490         var tbd = this.getGridEl().select('tbody', true).first();
8491         var tfd = this.getGridEl().select('tfoot', true).first();
8492         
8493         var cw = ctr.getWidth();
8494         
8495         if (tbd) {
8496             
8497             tbd.setWidth(ctr.getWidth());
8498             // if the body has a max height - and then scrolls - we should perhaps set up the height here
8499             // this needs fixing for various usage - currently only hydra job advers I think..
8500             //tdb.setHeight(
8501             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8502             //); 
8503             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8504             cw -= barsize;
8505         }
8506         cw = Math.max(cw, this.totalWidth);
8507         this.getGridEl().select('tr',true).setWidth(cw);
8508         // resize 'expandable coloumn?
8509         
8510         return; // we doe not have a view in this design..
8511         
8512     },
8513     onBodyScroll: function()
8514     {
8515         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8516         if(this.mainHead){
8517             this.mainHead.setStyle({
8518                 'position' : 'relative',
8519                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8520             });
8521         }
8522         
8523         if(this.lazyLoad){
8524             
8525             var scrollHeight = this.mainBody.dom.scrollHeight;
8526             
8527             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8528             
8529             var height = this.mainBody.getHeight();
8530             
8531             if(scrollHeight - height == scrollTop) {
8532                 
8533                 var total = this.ds.getTotalCount();
8534                 
8535                 if(this.footer.cursor + this.footer.pageSize < total){
8536                     
8537                     this.footer.ds.load({
8538                         params : {
8539                             start : this.footer.cursor + this.footer.pageSize,
8540                             limit : this.footer.pageSize
8541                         },
8542                         add : true
8543                     });
8544                 }
8545             }
8546             
8547         }
8548     },
8549     
8550     onHeaderChange : function()
8551     {
8552         var header = this.renderHeader();
8553         var table = this.el.select('table', true).first();
8554         
8555         this.mainHead.remove();
8556         this.mainHead = table.createChild(header, this.mainBody, false);
8557     },
8558     
8559     onHiddenChange : function(colModel, colIndex, hidden)
8560     {
8561         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8562         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8563         
8564         this.CSS.updateRule(thSelector, "display", "");
8565         this.CSS.updateRule(tdSelector, "display", "");
8566         
8567         if(hidden){
8568             this.CSS.updateRule(thSelector, "display", "none");
8569             this.CSS.updateRule(tdSelector, "display", "none");
8570         }
8571         
8572         this.onHeaderChange();
8573         this.onLoad();
8574     },
8575     
8576     setColumnWidth: function(col_index, width)
8577     {
8578         // width = "md-2 xs-2..."
8579         if(!this.colModel.config[col_index]) {
8580             return;
8581         }
8582         
8583         var w = width.split(" ");
8584         
8585         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8586         
8587         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8588         
8589         
8590         for(var j = 0; j < w.length; j++) {
8591             
8592             if(!w[j]) {
8593                 continue;
8594             }
8595             
8596             var size_cls = w[j].split("-");
8597             
8598             if(!Number.isInteger(size_cls[1] * 1)) {
8599                 continue;
8600             }
8601             
8602             if(!this.colModel.config[col_index][size_cls[0]]) {
8603                 continue;
8604             }
8605             
8606             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8607                 continue;
8608             }
8609             
8610             h_row[0].classList.replace(
8611                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8612                 "col-"+size_cls[0]+"-"+size_cls[1]
8613             );
8614             
8615             for(var i = 0; i < rows.length; i++) {
8616                 
8617                 var size_cls = w[j].split("-");
8618                 
8619                 if(!Number.isInteger(size_cls[1] * 1)) {
8620                     continue;
8621                 }
8622                 
8623                 if(!this.colModel.config[col_index][size_cls[0]]) {
8624                     continue;
8625                 }
8626                 
8627                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8628                     continue;
8629                 }
8630                 
8631                 rows[i].classList.replace(
8632                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8633                     "col-"+size_cls[0]+"-"+size_cls[1]
8634                 );
8635             }
8636             
8637             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8638         }
8639     }
8640 });
8641
8642  
8643
8644  /*
8645  * - LGPL
8646  *
8647  * table cell
8648  * 
8649  */
8650
8651 /**
8652  * @class Roo.bootstrap.TableCell
8653  * @extends Roo.bootstrap.Component
8654  * Bootstrap TableCell class
8655  * @cfg {String} html cell contain text
8656  * @cfg {String} cls cell class
8657  * @cfg {String} tag cell tag (td|th) default td
8658  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8659  * @cfg {String} align Aligns the content in a cell
8660  * @cfg {String} axis Categorizes cells
8661  * @cfg {String} bgcolor Specifies the background color of a cell
8662  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8663  * @cfg {Number} colspan Specifies the number of columns a cell should span
8664  * @cfg {String} headers Specifies one or more header cells a cell is related to
8665  * @cfg {Number} height Sets the height of a cell
8666  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8667  * @cfg {Number} rowspan Sets the number of rows a cell should span
8668  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8669  * @cfg {String} valign Vertical aligns the content in a cell
8670  * @cfg {Number} width Specifies the width of a cell
8671  * 
8672  * @constructor
8673  * Create a new TableCell
8674  * @param {Object} config The config object
8675  */
8676
8677 Roo.bootstrap.TableCell = function(config){
8678     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8679 };
8680
8681 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
8682     
8683     html: false,
8684     cls: false,
8685     tag: false,
8686     abbr: false,
8687     align: false,
8688     axis: false,
8689     bgcolor: false,
8690     charoff: false,
8691     colspan: false,
8692     headers: false,
8693     height: false,
8694     nowrap: false,
8695     rowspan: false,
8696     scope: false,
8697     valign: false,
8698     width: false,
8699     
8700     
8701     getAutoCreate : function(){
8702         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8703         
8704         cfg = {
8705             tag: 'td'
8706         };
8707         
8708         if(this.tag){
8709             cfg.tag = this.tag;
8710         }
8711         
8712         if (this.html) {
8713             cfg.html=this.html
8714         }
8715         if (this.cls) {
8716             cfg.cls=this.cls
8717         }
8718         if (this.abbr) {
8719             cfg.abbr=this.abbr
8720         }
8721         if (this.align) {
8722             cfg.align=this.align
8723         }
8724         if (this.axis) {
8725             cfg.axis=this.axis
8726         }
8727         if (this.bgcolor) {
8728             cfg.bgcolor=this.bgcolor
8729         }
8730         if (this.charoff) {
8731             cfg.charoff=this.charoff
8732         }
8733         if (this.colspan) {
8734             cfg.colspan=this.colspan
8735         }
8736         if (this.headers) {
8737             cfg.headers=this.headers
8738         }
8739         if (this.height) {
8740             cfg.height=this.height
8741         }
8742         if (this.nowrap) {
8743             cfg.nowrap=this.nowrap
8744         }
8745         if (this.rowspan) {
8746             cfg.rowspan=this.rowspan
8747         }
8748         if (this.scope) {
8749             cfg.scope=this.scope
8750         }
8751         if (this.valign) {
8752             cfg.valign=this.valign
8753         }
8754         if (this.width) {
8755             cfg.width=this.width
8756         }
8757         
8758         
8759         return cfg;
8760     }
8761    
8762 });
8763
8764  
8765
8766  /*
8767  * - LGPL
8768  *
8769  * table row
8770  * 
8771  */
8772
8773 /**
8774  * @class Roo.bootstrap.TableRow
8775  * @extends Roo.bootstrap.Component
8776  * Bootstrap TableRow class
8777  * @cfg {String} cls row class
8778  * @cfg {String} align Aligns the content in a table row
8779  * @cfg {String} bgcolor Specifies a background color for a table row
8780  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8781  * @cfg {String} valign Vertical aligns the content in a table row
8782  * 
8783  * @constructor
8784  * Create a new TableRow
8785  * @param {Object} config The config object
8786  */
8787
8788 Roo.bootstrap.TableRow = function(config){
8789     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
8790 };
8791
8792 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
8793     
8794     cls: false,
8795     align: false,
8796     bgcolor: false,
8797     charoff: false,
8798     valign: false,
8799     
8800     getAutoCreate : function(){
8801         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
8802         
8803         cfg = {
8804             tag: 'tr'
8805         };
8806             
8807         if(this.cls){
8808             cfg.cls = this.cls;
8809         }
8810         if(this.align){
8811             cfg.align = this.align;
8812         }
8813         if(this.bgcolor){
8814             cfg.bgcolor = this.bgcolor;
8815         }
8816         if(this.charoff){
8817             cfg.charoff = this.charoff;
8818         }
8819         if(this.valign){
8820             cfg.valign = this.valign;
8821         }
8822         
8823         return cfg;
8824     }
8825    
8826 });
8827
8828  
8829
8830  /*
8831  * - LGPL
8832  *
8833  * table body
8834  * 
8835  */
8836
8837 /**
8838  * @class Roo.bootstrap.TableBody
8839  * @extends Roo.bootstrap.Component
8840  * Bootstrap TableBody class
8841  * @cfg {String} cls element class
8842  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
8843  * @cfg {String} align Aligns the content inside the element
8844  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
8845  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
8846  * 
8847  * @constructor
8848  * Create a new TableBody
8849  * @param {Object} config The config object
8850  */
8851
8852 Roo.bootstrap.TableBody = function(config){
8853     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
8854 };
8855
8856 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
8857     
8858     cls: false,
8859     tag: false,
8860     align: false,
8861     charoff: false,
8862     valign: false,
8863     
8864     getAutoCreate : function(){
8865         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
8866         
8867         cfg = {
8868             tag: 'tbody'
8869         };
8870             
8871         if (this.cls) {
8872             cfg.cls=this.cls
8873         }
8874         if(this.tag){
8875             cfg.tag = this.tag;
8876         }
8877         
8878         if(this.align){
8879             cfg.align = this.align;
8880         }
8881         if(this.charoff){
8882             cfg.charoff = this.charoff;
8883         }
8884         if(this.valign){
8885             cfg.valign = this.valign;
8886         }
8887         
8888         return cfg;
8889     }
8890     
8891     
8892 //    initEvents : function()
8893 //    {
8894 //        
8895 //        if(!this.store){
8896 //            return;
8897 //        }
8898 //        
8899 //        this.store = Roo.factory(this.store, Roo.data);
8900 //        this.store.on('load', this.onLoad, this);
8901 //        
8902 //        this.store.load();
8903 //        
8904 //    },
8905 //    
8906 //    onLoad: function () 
8907 //    {   
8908 //        this.fireEvent('load', this);
8909 //    }
8910 //    
8911 //   
8912 });
8913
8914  
8915
8916  /*
8917  * Based on:
8918  * Ext JS Library 1.1.1
8919  * Copyright(c) 2006-2007, Ext JS, LLC.
8920  *
8921  * Originally Released Under LGPL - original licence link has changed is not relivant.
8922  *
8923  * Fork - LGPL
8924  * <script type="text/javascript">
8925  */
8926
8927 // as we use this in bootstrap.
8928 Roo.namespace('Roo.form');
8929  /**
8930  * @class Roo.form.Action
8931  * Internal Class used to handle form actions
8932  * @constructor
8933  * @param {Roo.form.BasicForm} el The form element or its id
8934  * @param {Object} config Configuration options
8935  */
8936
8937  
8938  
8939 // define the action interface
8940 Roo.form.Action = function(form, options){
8941     this.form = form;
8942     this.options = options || {};
8943 };
8944 /**
8945  * Client Validation Failed
8946  * @const 
8947  */
8948 Roo.form.Action.CLIENT_INVALID = 'client';
8949 /**
8950  * Server Validation Failed
8951  * @const 
8952  */
8953 Roo.form.Action.SERVER_INVALID = 'server';
8954  /**
8955  * Connect to Server Failed
8956  * @const 
8957  */
8958 Roo.form.Action.CONNECT_FAILURE = 'connect';
8959 /**
8960  * Reading Data from Server Failed
8961  * @const 
8962  */
8963 Roo.form.Action.LOAD_FAILURE = 'load';
8964
8965 Roo.form.Action.prototype = {
8966     type : 'default',
8967     failureType : undefined,
8968     response : undefined,
8969     result : undefined,
8970
8971     // interface method
8972     run : function(options){
8973
8974     },
8975
8976     // interface method
8977     success : function(response){
8978
8979     },
8980
8981     // interface method
8982     handleResponse : function(response){
8983
8984     },
8985
8986     // default connection failure
8987     failure : function(response){
8988         
8989         this.response = response;
8990         this.failureType = Roo.form.Action.CONNECT_FAILURE;
8991         this.form.afterAction(this, false);
8992     },
8993
8994     processResponse : function(response){
8995         this.response = response;
8996         if(!response.responseText){
8997             return true;
8998         }
8999         this.result = this.handleResponse(response);
9000         return this.result;
9001     },
9002
9003     // utility functions used internally
9004     getUrl : function(appendParams){
9005         var url = this.options.url || this.form.url || this.form.el.dom.action;
9006         if(appendParams){
9007             var p = this.getParams();
9008             if(p){
9009                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9010             }
9011         }
9012         return url;
9013     },
9014
9015     getMethod : function(){
9016         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9017     },
9018
9019     getParams : function(){
9020         var bp = this.form.baseParams;
9021         var p = this.options.params;
9022         if(p){
9023             if(typeof p == "object"){
9024                 p = Roo.urlEncode(Roo.applyIf(p, bp));
9025             }else if(typeof p == 'string' && bp){
9026                 p += '&' + Roo.urlEncode(bp);
9027             }
9028         }else if(bp){
9029             p = Roo.urlEncode(bp);
9030         }
9031         return p;
9032     },
9033
9034     createCallback : function(){
9035         return {
9036             success: this.success,
9037             failure: this.failure,
9038             scope: this,
9039             timeout: (this.form.timeout*1000),
9040             upload: this.form.fileUpload ? this.success : undefined
9041         };
9042     }
9043 };
9044
9045 Roo.form.Action.Submit = function(form, options){
9046     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9047 };
9048
9049 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9050     type : 'submit',
9051
9052     haveProgress : false,
9053     uploadComplete : false,
9054     
9055     // uploadProgress indicator.
9056     uploadProgress : function()
9057     {
9058         if (!this.form.progressUrl) {
9059             return;
9060         }
9061         
9062         if (!this.haveProgress) {
9063             Roo.MessageBox.progress("Uploading", "Uploading");
9064         }
9065         if (this.uploadComplete) {
9066            Roo.MessageBox.hide();
9067            return;
9068         }
9069         
9070         this.haveProgress = true;
9071    
9072         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9073         
9074         var c = new Roo.data.Connection();
9075         c.request({
9076             url : this.form.progressUrl,
9077             params: {
9078                 id : uid
9079             },
9080             method: 'GET',
9081             success : function(req){
9082                //console.log(data);
9083                 var rdata = false;
9084                 var edata;
9085                 try  {
9086                    rdata = Roo.decode(req.responseText)
9087                 } catch (e) {
9088                     Roo.log("Invalid data from server..");
9089                     Roo.log(edata);
9090                     return;
9091                 }
9092                 if (!rdata || !rdata.success) {
9093                     Roo.log(rdata);
9094                     Roo.MessageBox.alert(Roo.encode(rdata));
9095                     return;
9096                 }
9097                 var data = rdata.data;
9098                 
9099                 if (this.uploadComplete) {
9100                    Roo.MessageBox.hide();
9101                    return;
9102                 }
9103                    
9104                 if (data){
9105                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9106                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9107                     );
9108                 }
9109                 this.uploadProgress.defer(2000,this);
9110             },
9111        
9112             failure: function(data) {
9113                 Roo.log('progress url failed ');
9114                 Roo.log(data);
9115             },
9116             scope : this
9117         });
9118            
9119     },
9120     
9121     
9122     run : function()
9123     {
9124         // run get Values on the form, so it syncs any secondary forms.
9125         this.form.getValues();
9126         
9127         var o = this.options;
9128         var method = this.getMethod();
9129         var isPost = method == 'POST';
9130         if(o.clientValidation === false || this.form.isValid()){
9131             
9132             if (this.form.progressUrl) {
9133                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9134                     (new Date() * 1) + '' + Math.random());
9135                     
9136             } 
9137             
9138             
9139             Roo.Ajax.request(Roo.apply(this.createCallback(), {
9140                 form:this.form.el.dom,
9141                 url:this.getUrl(!isPost),
9142                 method: method,
9143                 params:isPost ? this.getParams() : null,
9144                 isUpload: this.form.fileUpload,
9145                 formData : this.form.formData
9146             }));
9147             
9148             this.uploadProgress();
9149
9150         }else if (o.clientValidation !== false){ // client validation failed
9151             this.failureType = Roo.form.Action.CLIENT_INVALID;
9152             this.form.afterAction(this, false);
9153         }
9154     },
9155
9156     success : function(response)
9157     {
9158         this.uploadComplete= true;
9159         if (this.haveProgress) {
9160             Roo.MessageBox.hide();
9161         }
9162         
9163         
9164         var result = this.processResponse(response);
9165         if(result === true || result.success){
9166             this.form.afterAction(this, true);
9167             return;
9168         }
9169         if(result.errors){
9170             this.form.markInvalid(result.errors);
9171             this.failureType = Roo.form.Action.SERVER_INVALID;
9172         }
9173         this.form.afterAction(this, false);
9174     },
9175     failure : function(response)
9176     {
9177         this.uploadComplete= true;
9178         if (this.haveProgress) {
9179             Roo.MessageBox.hide();
9180         }
9181         
9182         this.response = response;
9183         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9184         this.form.afterAction(this, false);
9185     },
9186     
9187     handleResponse : function(response){
9188         if(this.form.errorReader){
9189             var rs = this.form.errorReader.read(response);
9190             var errors = [];
9191             if(rs.records){
9192                 for(var i = 0, len = rs.records.length; i < len; i++) {
9193                     var r = rs.records[i];
9194                     errors[i] = r.data;
9195                 }
9196             }
9197             if(errors.length < 1){
9198                 errors = null;
9199             }
9200             return {
9201                 success : rs.success,
9202                 errors : errors
9203             };
9204         }
9205         var ret = false;
9206         try {
9207             ret = Roo.decode(response.responseText);
9208         } catch (e) {
9209             ret = {
9210                 success: false,
9211                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9212                 errors : []
9213             };
9214         }
9215         return ret;
9216         
9217     }
9218 });
9219
9220
9221 Roo.form.Action.Load = function(form, options){
9222     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9223     this.reader = this.form.reader;
9224 };
9225
9226 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9227     type : 'load',
9228
9229     run : function(){
9230         
9231         Roo.Ajax.request(Roo.apply(
9232                 this.createCallback(), {
9233                     method:this.getMethod(),
9234                     url:this.getUrl(false),
9235                     params:this.getParams()
9236         }));
9237     },
9238
9239     success : function(response){
9240         
9241         var result = this.processResponse(response);
9242         if(result === true || !result.success || !result.data){
9243             this.failureType = Roo.form.Action.LOAD_FAILURE;
9244             this.form.afterAction(this, false);
9245             return;
9246         }
9247         this.form.clearInvalid();
9248         this.form.setValues(result.data);
9249         this.form.afterAction(this, true);
9250     },
9251
9252     handleResponse : function(response){
9253         if(this.form.reader){
9254             var rs = this.form.reader.read(response);
9255             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9256             return {
9257                 success : rs.success,
9258                 data : data
9259             };
9260         }
9261         return Roo.decode(response.responseText);
9262     }
9263 });
9264
9265 Roo.form.Action.ACTION_TYPES = {
9266     'load' : Roo.form.Action.Load,
9267     'submit' : Roo.form.Action.Submit
9268 };/*
9269  * - LGPL
9270  *
9271  * form
9272  *
9273  */
9274
9275 /**
9276  * @class Roo.bootstrap.Form
9277  * @extends Roo.bootstrap.Component
9278  * Bootstrap Form class
9279  * @cfg {String} method  GET | POST (default POST)
9280  * @cfg {String} labelAlign top | left (default top)
9281  * @cfg {String} align left  | right - for navbars
9282  * @cfg {Boolean} loadMask load mask when submit (default true)
9283
9284  *
9285  * @constructor
9286  * Create a new Form
9287  * @param {Object} config The config object
9288  */
9289
9290
9291 Roo.bootstrap.Form = function(config){
9292     
9293     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9294     
9295     Roo.bootstrap.Form.popover.apply();
9296     
9297     this.addEvents({
9298         /**
9299          * @event clientvalidation
9300          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9301          * @param {Form} this
9302          * @param {Boolean} valid true if the form has passed client-side validation
9303          */
9304         clientvalidation: true,
9305         /**
9306          * @event beforeaction
9307          * Fires before any action is performed. Return false to cancel the action.
9308          * @param {Form} this
9309          * @param {Action} action The action to be performed
9310          */
9311         beforeaction: true,
9312         /**
9313          * @event actionfailed
9314          * Fires when an action fails.
9315          * @param {Form} this
9316          * @param {Action} action The action that failed
9317          */
9318         actionfailed : true,
9319         /**
9320          * @event actioncomplete
9321          * Fires when an action is completed.
9322          * @param {Form} this
9323          * @param {Action} action The action that completed
9324          */
9325         actioncomplete : true
9326     });
9327 };
9328
9329 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9330
9331      /**
9332      * @cfg {String} method
9333      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9334      */
9335     method : 'POST',
9336     /**
9337      * @cfg {String} url
9338      * The URL to use for form actions if one isn't supplied in the action options.
9339      */
9340     /**
9341      * @cfg {Boolean} fileUpload
9342      * Set to true if this form is a file upload.
9343      */
9344
9345     /**
9346      * @cfg {Object} baseParams
9347      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9348      */
9349
9350     /**
9351      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9352      */
9353     timeout: 30,
9354     /**
9355      * @cfg {Sting} align (left|right) for navbar forms
9356      */
9357     align : 'left',
9358
9359     // private
9360     activeAction : null,
9361
9362     /**
9363      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9364      * element by passing it or its id or mask the form itself by passing in true.
9365      * @type Mixed
9366      */
9367     waitMsgTarget : false,
9368
9369     loadMask : true,
9370     
9371     /**
9372      * @cfg {Boolean} errorMask (true|false) default false
9373      */
9374     errorMask : false,
9375     
9376     /**
9377      * @cfg {Number} maskOffset Default 100
9378      */
9379     maskOffset : 100,
9380     
9381     /**
9382      * @cfg {Boolean} maskBody
9383      */
9384     maskBody : false,
9385
9386     getAutoCreate : function(){
9387
9388         var cfg = {
9389             tag: 'form',
9390             method : this.method || 'POST',
9391             id : this.id || Roo.id(),
9392             cls : ''
9393         };
9394         if (this.parent().xtype.match(/^Nav/)) {
9395             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9396
9397         }
9398
9399         if (this.labelAlign == 'left' ) {
9400             cfg.cls += ' form-horizontal';
9401         }
9402
9403
9404         return cfg;
9405     },
9406     initEvents : function()
9407     {
9408         this.el.on('submit', this.onSubmit, this);
9409         // this was added as random key presses on the form where triggering form submit.
9410         this.el.on('keypress', function(e) {
9411             if (e.getCharCode() != 13) {
9412                 return true;
9413             }
9414             // we might need to allow it for textareas.. and some other items.
9415             // check e.getTarget().
9416
9417             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9418                 return true;
9419             }
9420
9421             Roo.log("keypress blocked");
9422
9423             e.preventDefault();
9424             return false;
9425         });
9426         
9427     },
9428     // private
9429     onSubmit : function(e){
9430         e.stopEvent();
9431     },
9432
9433      /**
9434      * Returns true if client-side validation on the form is successful.
9435      * @return Boolean
9436      */
9437     isValid : function(){
9438         var items = this.getItems();
9439         var valid = true;
9440         var target = false;
9441         
9442         items.each(function(f){
9443             
9444             if(f.validate()){
9445                 return;
9446             }
9447             
9448             Roo.log('invalid field: ' + f.name);
9449             
9450             valid = false;
9451
9452             if(!target && f.el.isVisible(true)){
9453                 target = f;
9454             }
9455            
9456         });
9457         
9458         if(this.errorMask && !valid){
9459             Roo.bootstrap.Form.popover.mask(this, target);
9460         }
9461         
9462         return valid;
9463     },
9464     
9465     /**
9466      * Returns true if any fields in this form have changed since their original load.
9467      * @return Boolean
9468      */
9469     isDirty : function(){
9470         var dirty = false;
9471         var items = this.getItems();
9472         items.each(function(f){
9473            if(f.isDirty()){
9474                dirty = true;
9475                return false;
9476            }
9477            return true;
9478         });
9479         return dirty;
9480     },
9481      /**
9482      * Performs a predefined action (submit or load) or custom actions you define on this form.
9483      * @param {String} actionName The name of the action type
9484      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
9485      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9486      * accept other config options):
9487      * <pre>
9488 Property          Type             Description
9489 ----------------  ---------------  ----------------------------------------------------------------------------------
9490 url               String           The url for the action (defaults to the form's url)
9491 method            String           The form method to use (defaults to the form's method, or POST if not defined)
9492 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
9493 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
9494                                    validate the form on the client (defaults to false)
9495      * </pre>
9496      * @return {BasicForm} this
9497      */
9498     doAction : function(action, options){
9499         if(typeof action == 'string'){
9500             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9501         }
9502         if(this.fireEvent('beforeaction', this, action) !== false){
9503             this.beforeAction(action);
9504             action.run.defer(100, action);
9505         }
9506         return this;
9507     },
9508
9509     // private
9510     beforeAction : function(action){
9511         var o = action.options;
9512         
9513         if(this.loadMask){
9514             
9515             if(this.maskBody){
9516                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9517             } else {
9518                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9519             }
9520         }
9521         // not really supported yet.. ??
9522
9523         //if(this.waitMsgTarget === true){
9524         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9525         //}else if(this.waitMsgTarget){
9526         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9527         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9528         //}else {
9529         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9530        // }
9531
9532     },
9533
9534     // private
9535     afterAction : function(action, success){
9536         this.activeAction = null;
9537         var o = action.options;
9538
9539         if(this.loadMask){
9540             
9541             if(this.maskBody){
9542                 Roo.get(document.body).unmask();
9543             } else {
9544                 this.el.unmask();
9545             }
9546         }
9547         
9548         //if(this.waitMsgTarget === true){
9549 //            this.el.unmask();
9550         //}else if(this.waitMsgTarget){
9551         //    this.waitMsgTarget.unmask();
9552         //}else{
9553         //    Roo.MessageBox.updateProgress(1);
9554         //    Roo.MessageBox.hide();
9555        // }
9556         //
9557         if(success){
9558             if(o.reset){
9559                 this.reset();
9560             }
9561             Roo.callback(o.success, o.scope, [this, action]);
9562             this.fireEvent('actioncomplete', this, action);
9563
9564         }else{
9565
9566             // failure condition..
9567             // we have a scenario where updates need confirming.
9568             // eg. if a locking scenario exists..
9569             // we look for { errors : { needs_confirm : true }} in the response.
9570             if (
9571                 (typeof(action.result) != 'undefined')  &&
9572                 (typeof(action.result.errors) != 'undefined')  &&
9573                 (typeof(action.result.errors.needs_confirm) != 'undefined')
9574            ){
9575                 var _t = this;
9576                 Roo.log("not supported yet");
9577                  /*
9578
9579                 Roo.MessageBox.confirm(
9580                     "Change requires confirmation",
9581                     action.result.errorMsg,
9582                     function(r) {
9583                         if (r != 'yes') {
9584                             return;
9585                         }
9586                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
9587                     }
9588
9589                 );
9590                 */
9591
9592
9593                 return;
9594             }
9595
9596             Roo.callback(o.failure, o.scope, [this, action]);
9597             // show an error message if no failed handler is set..
9598             if (!this.hasListener('actionfailed')) {
9599                 Roo.log("need to add dialog support");
9600                 /*
9601                 Roo.MessageBox.alert("Error",
9602                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9603                         action.result.errorMsg :
9604                         "Saving Failed, please check your entries or try again"
9605                 );
9606                 */
9607             }
9608
9609             this.fireEvent('actionfailed', this, action);
9610         }
9611
9612     },
9613     /**
9614      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9615      * @param {String} id The value to search for
9616      * @return Field
9617      */
9618     findField : function(id){
9619         var items = this.getItems();
9620         var field = items.get(id);
9621         if(!field){
9622              items.each(function(f){
9623                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9624                     field = f;
9625                     return false;
9626                 }
9627                 return true;
9628             });
9629         }
9630         return field || null;
9631     },
9632      /**
9633      * Mark fields in this form invalid in bulk.
9634      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9635      * @return {BasicForm} this
9636      */
9637     markInvalid : function(errors){
9638         if(errors instanceof Array){
9639             for(var i = 0, len = errors.length; i < len; i++){
9640                 var fieldError = errors[i];
9641                 var f = this.findField(fieldError.id);
9642                 if(f){
9643                     f.markInvalid(fieldError.msg);
9644                 }
9645             }
9646         }else{
9647             var field, id;
9648             for(id in errors){
9649                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9650                     field.markInvalid(errors[id]);
9651                 }
9652             }
9653         }
9654         //Roo.each(this.childForms || [], function (f) {
9655         //    f.markInvalid(errors);
9656         //});
9657
9658         return this;
9659     },
9660
9661     /**
9662      * Set values for fields in this form in bulk.
9663      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9664      * @return {BasicForm} this
9665      */
9666     setValues : function(values){
9667         if(values instanceof Array){ // array of objects
9668             for(var i = 0, len = values.length; i < len; i++){
9669                 var v = values[i];
9670                 var f = this.findField(v.id);
9671                 if(f){
9672                     f.setValue(v.value);
9673                     if(this.trackResetOnLoad){
9674                         f.originalValue = f.getValue();
9675                     }
9676                 }
9677             }
9678         }else{ // object hash
9679             var field, id;
9680             for(id in values){
9681                 if(typeof values[id] != 'function' && (field = this.findField(id))){
9682
9683                     if (field.setFromData &&
9684                         field.valueField &&
9685                         field.displayField &&
9686                         // combos' with local stores can
9687                         // be queried via setValue()
9688                         // to set their value..
9689                         (field.store && !field.store.isLocal)
9690                         ) {
9691                         // it's a combo
9692                         var sd = { };
9693                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9694                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9695                         field.setFromData(sd);
9696
9697                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9698                         
9699                         field.setFromData(values);
9700                         
9701                     } else {
9702                         field.setValue(values[id]);
9703                     }
9704
9705
9706                     if(this.trackResetOnLoad){
9707                         field.originalValue = field.getValue();
9708                     }
9709                 }
9710             }
9711         }
9712
9713         //Roo.each(this.childForms || [], function (f) {
9714         //    f.setValues(values);
9715         //});
9716
9717         return this;
9718     },
9719
9720     /**
9721      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9722      * they are returned as an array.
9723      * @param {Boolean} asString
9724      * @return {Object}
9725      */
9726     getValues : function(asString){
9727         //if (this.childForms) {
9728             // copy values from the child forms
9729         //    Roo.each(this.childForms, function (f) {
9730         //        this.setValues(f.getValues());
9731         //    }, this);
9732         //}
9733
9734
9735
9736         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
9737         if(asString === true){
9738             return fs;
9739         }
9740         return Roo.urlDecode(fs);
9741     },
9742
9743     /**
9744      * Returns the fields in this form as an object with key/value pairs.
9745      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
9746      * @return {Object}
9747      */
9748     getFieldValues : function(with_hidden)
9749     {
9750         var items = this.getItems();
9751         var ret = {};
9752         items.each(function(f){
9753             
9754             if (!f.getName()) {
9755                 return;
9756             }
9757             
9758             var v = f.getValue();
9759             
9760             if (f.inputType =='radio') {
9761                 if (typeof(ret[f.getName()]) == 'undefined') {
9762                     ret[f.getName()] = ''; // empty..
9763                 }
9764
9765                 if (!f.el.dom.checked) {
9766                     return;
9767
9768                 }
9769                 v = f.el.dom.value;
9770
9771             }
9772             
9773             if(f.xtype == 'MoneyField'){
9774                 ret[f.currencyName] = f.getCurrency();
9775             }
9776
9777             // not sure if this supported any more..
9778             if ((typeof(v) == 'object') && f.getRawValue) {
9779                 v = f.getRawValue() ; // dates..
9780             }
9781             // combo boxes where name != hiddenName...
9782             if (f.name !== false && f.name != '' && f.name != f.getName()) {
9783                 ret[f.name] = f.getRawValue();
9784             }
9785             ret[f.getName()] = v;
9786         });
9787
9788         return ret;
9789     },
9790
9791     /**
9792      * Clears all invalid messages in this form.
9793      * @return {BasicForm} this
9794      */
9795     clearInvalid : function(){
9796         var items = this.getItems();
9797
9798         items.each(function(f){
9799            f.clearInvalid();
9800         });
9801
9802         return this;
9803     },
9804
9805     /**
9806      * Resets this form.
9807      * @return {BasicForm} this
9808      */
9809     reset : function(){
9810         var items = this.getItems();
9811         items.each(function(f){
9812             f.reset();
9813         });
9814
9815         Roo.each(this.childForms || [], function (f) {
9816             f.reset();
9817         });
9818
9819
9820         return this;
9821     },
9822     
9823     getItems : function()
9824     {
9825         var r=new Roo.util.MixedCollection(false, function(o){
9826             return o.id || (o.id = Roo.id());
9827         });
9828         var iter = function(el) {
9829             if (el.inputEl) {
9830                 r.add(el);
9831             }
9832             if (!el.items) {
9833                 return;
9834             }
9835             Roo.each(el.items,function(e) {
9836                 iter(e);
9837             });
9838         };
9839
9840         iter(this);
9841         return r;
9842     },
9843     
9844     hideFields : function(items)
9845     {
9846         Roo.each(items, function(i){
9847             
9848             var f = this.findField(i);
9849             
9850             if(!f){
9851                 return;
9852             }
9853             
9854             f.hide();
9855             
9856         }, this);
9857     },
9858     
9859     showFields : function(items)
9860     {
9861         Roo.each(items, function(i){
9862             
9863             var f = this.findField(i);
9864             
9865             if(!f){
9866                 return;
9867             }
9868             
9869             f.show();
9870             
9871         }, this);
9872     }
9873
9874 });
9875
9876 Roo.apply(Roo.bootstrap.Form, {
9877     
9878     popover : {
9879         
9880         padding : 5,
9881         
9882         isApplied : false,
9883         
9884         isMasked : false,
9885         
9886         form : false,
9887         
9888         target : false,
9889         
9890         toolTip : false,
9891         
9892         intervalID : false,
9893         
9894         maskEl : false,
9895         
9896         apply : function()
9897         {
9898             if(this.isApplied){
9899                 return;
9900             }
9901             
9902             this.maskEl = {
9903                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
9904                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
9905                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
9906                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
9907             };
9908             
9909             this.maskEl.top.enableDisplayMode("block");
9910             this.maskEl.left.enableDisplayMode("block");
9911             this.maskEl.bottom.enableDisplayMode("block");
9912             this.maskEl.right.enableDisplayMode("block");
9913             
9914             this.toolTip = new Roo.bootstrap.Tooltip({
9915                 cls : 'roo-form-error-popover',
9916                 alignment : {
9917                     'left' : ['r-l', [-2,0], 'right'],
9918                     'right' : ['l-r', [2,0], 'left'],
9919                     'bottom' : ['tl-bl', [0,2], 'top'],
9920                     'top' : [ 'bl-tl', [0,-2], 'bottom']
9921                 }
9922             });
9923             
9924             this.toolTip.render(Roo.get(document.body));
9925
9926             this.toolTip.el.enableDisplayMode("block");
9927             
9928             Roo.get(document.body).on('click', function(){
9929                 this.unmask();
9930             }, this);
9931             
9932             Roo.get(document.body).on('touchstart', function(){
9933                 this.unmask();
9934             }, this);
9935             
9936             this.isApplied = true
9937         },
9938         
9939         mask : function(form, target)
9940         {
9941             this.form = form;
9942             
9943             this.target = target;
9944             
9945             if(!this.form.errorMask || !target.el){
9946                 return;
9947             }
9948             
9949             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
9950             
9951             Roo.log(scrollable);
9952             
9953             var ot = this.target.el.calcOffsetsTo(scrollable);
9954             
9955             var scrollTo = ot[1] - this.form.maskOffset;
9956             
9957             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
9958             
9959             scrollable.scrollTo('top', scrollTo);
9960             
9961             var box = this.target.el.getBox();
9962             Roo.log(box);
9963             var zIndex = Roo.bootstrap.Modal.zIndex++;
9964
9965             
9966             this.maskEl.top.setStyle('position', 'absolute');
9967             this.maskEl.top.setStyle('z-index', zIndex);
9968             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
9969             this.maskEl.top.setLeft(0);
9970             this.maskEl.top.setTop(0);
9971             this.maskEl.top.show();
9972             
9973             this.maskEl.left.setStyle('position', 'absolute');
9974             this.maskEl.left.setStyle('z-index', zIndex);
9975             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
9976             this.maskEl.left.setLeft(0);
9977             this.maskEl.left.setTop(box.y - this.padding);
9978             this.maskEl.left.show();
9979
9980             this.maskEl.bottom.setStyle('position', 'absolute');
9981             this.maskEl.bottom.setStyle('z-index', zIndex);
9982             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
9983             this.maskEl.bottom.setLeft(0);
9984             this.maskEl.bottom.setTop(box.bottom + this.padding);
9985             this.maskEl.bottom.show();
9986
9987             this.maskEl.right.setStyle('position', 'absolute');
9988             this.maskEl.right.setStyle('z-index', zIndex);
9989             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
9990             this.maskEl.right.setLeft(box.right + this.padding);
9991             this.maskEl.right.setTop(box.y - this.padding);
9992             this.maskEl.right.show();
9993
9994             this.toolTip.bindEl = this.target.el;
9995
9996             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
9997
9998             var tip = this.target.blankText;
9999
10000             if(this.target.getValue() !== '' ) {
10001                 
10002                 if (this.target.invalidText.length) {
10003                     tip = this.target.invalidText;
10004                 } else if (this.target.regexText.length){
10005                     tip = this.target.regexText;
10006                 }
10007             }
10008
10009             this.toolTip.show(tip);
10010
10011             this.intervalID = window.setInterval(function() {
10012                 Roo.bootstrap.Form.popover.unmask();
10013             }, 10000);
10014
10015             window.onwheel = function(){ return false;};
10016             
10017             (function(){ this.isMasked = true; }).defer(500, this);
10018             
10019         },
10020         
10021         unmask : function()
10022         {
10023             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10024                 return;
10025             }
10026             
10027             this.maskEl.top.setStyle('position', 'absolute');
10028             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10029             this.maskEl.top.hide();
10030
10031             this.maskEl.left.setStyle('position', 'absolute');
10032             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10033             this.maskEl.left.hide();
10034
10035             this.maskEl.bottom.setStyle('position', 'absolute');
10036             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10037             this.maskEl.bottom.hide();
10038
10039             this.maskEl.right.setStyle('position', 'absolute');
10040             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10041             this.maskEl.right.hide();
10042             
10043             this.toolTip.hide();
10044             
10045             this.toolTip.el.hide();
10046             
10047             window.onwheel = function(){ return true;};
10048             
10049             if(this.intervalID){
10050                 window.clearInterval(this.intervalID);
10051                 this.intervalID = false;
10052             }
10053             
10054             this.isMasked = false;
10055             
10056         }
10057         
10058     }
10059     
10060 });
10061
10062 /*
10063  * Based on:
10064  * Ext JS Library 1.1.1
10065  * Copyright(c) 2006-2007, Ext JS, LLC.
10066  *
10067  * Originally Released Under LGPL - original licence link has changed is not relivant.
10068  *
10069  * Fork - LGPL
10070  * <script type="text/javascript">
10071  */
10072 /**
10073  * @class Roo.form.VTypes
10074  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10075  * @singleton
10076  */
10077 Roo.form.VTypes = function(){
10078     // closure these in so they are only created once.
10079     var alpha = /^[a-zA-Z_]+$/;
10080     var alphanum = /^[a-zA-Z0-9_]+$/;
10081     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10082     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10083
10084     // All these messages and functions are configurable
10085     return {
10086         /**
10087          * The function used to validate email addresses
10088          * @param {String} value The email address
10089          */
10090         'email' : function(v){
10091             return email.test(v);
10092         },
10093         /**
10094          * The error text to display when the email validation function returns false
10095          * @type String
10096          */
10097         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10098         /**
10099          * The keystroke filter mask to be applied on email input
10100          * @type RegExp
10101          */
10102         'emailMask' : /[a-z0-9_\.\-@]/i,
10103
10104         /**
10105          * The function used to validate URLs
10106          * @param {String} value The URL
10107          */
10108         'url' : function(v){
10109             return url.test(v);
10110         },
10111         /**
10112          * The error text to display when the url validation function returns false
10113          * @type String
10114          */
10115         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10116         
10117         /**
10118          * The function used to validate alpha values
10119          * @param {String} value The value
10120          */
10121         'alpha' : function(v){
10122             return alpha.test(v);
10123         },
10124         /**
10125          * The error text to display when the alpha validation function returns false
10126          * @type String
10127          */
10128         'alphaText' : 'This field should only contain letters and _',
10129         /**
10130          * The keystroke filter mask to be applied on alpha input
10131          * @type RegExp
10132          */
10133         'alphaMask' : /[a-z_]/i,
10134
10135         /**
10136          * The function used to validate alphanumeric values
10137          * @param {String} value The value
10138          */
10139         'alphanum' : function(v){
10140             return alphanum.test(v);
10141         },
10142         /**
10143          * The error text to display when the alphanumeric validation function returns false
10144          * @type String
10145          */
10146         'alphanumText' : 'This field should only contain letters, numbers and _',
10147         /**
10148          * The keystroke filter mask to be applied on alphanumeric input
10149          * @type RegExp
10150          */
10151         'alphanumMask' : /[a-z0-9_]/i
10152     };
10153 }();/*
10154  * - LGPL
10155  *
10156  * Input
10157  * 
10158  */
10159
10160 /**
10161  * @class Roo.bootstrap.Input
10162  * @extends Roo.bootstrap.Component
10163  * Bootstrap Input class
10164  * @cfg {Boolean} disabled is it disabled
10165  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
10166  * @cfg {String} name name of the input
10167  * @cfg {string} fieldLabel - the label associated
10168  * @cfg {string} placeholder - placeholder to put in text.
10169  * @cfg {string}  before - input group add on before
10170  * @cfg {string} after - input group add on after
10171  * @cfg {string} size - (lg|sm) or leave empty..
10172  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10173  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10174  * @cfg {Number} md colspan out of 12 for computer-sized screens
10175  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10176  * @cfg {string} value default value of the input
10177  * @cfg {Number} labelWidth set the width of label 
10178  * @cfg {Number} labellg set the width of label (1-12)
10179  * @cfg {Number} labelmd set the width of label (1-12)
10180  * @cfg {Number} labelsm set the width of label (1-12)
10181  * @cfg {Number} labelxs set the width of label (1-12)
10182  * @cfg {String} labelAlign (top|left)
10183  * @cfg {Boolean} readOnly Specifies that the field should be read-only
10184  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10185  * @cfg {String} indicatorpos (left|right) default left
10186  * @cfg {String} capture (user|camera) use for file input only. (default empty)
10187  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10188  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10189
10190  * @cfg {String} align (left|center|right) Default left
10191  * @cfg {Boolean} forceFeedback (true|false) Default false
10192  * 
10193  * @constructor
10194  * Create a new Input
10195  * @param {Object} config The config object
10196  */
10197
10198 Roo.bootstrap.Input = function(config){
10199     
10200     Roo.bootstrap.Input.superclass.constructor.call(this, config);
10201     
10202     this.addEvents({
10203         /**
10204          * @event focus
10205          * Fires when this field receives input focus.
10206          * @param {Roo.form.Field} this
10207          */
10208         focus : true,
10209         /**
10210          * @event blur
10211          * Fires when this field loses input focus.
10212          * @param {Roo.form.Field} this
10213          */
10214         blur : true,
10215         /**
10216          * @event specialkey
10217          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
10218          * {@link Roo.EventObject#getKey} to determine which key was pressed.
10219          * @param {Roo.form.Field} this
10220          * @param {Roo.EventObject} e The event object
10221          */
10222         specialkey : true,
10223         /**
10224          * @event change
10225          * Fires just before the field blurs if the field value has changed.
10226          * @param {Roo.form.Field} this
10227          * @param {Mixed} newValue The new value
10228          * @param {Mixed} oldValue The original value
10229          */
10230         change : true,
10231         /**
10232          * @event invalid
10233          * Fires after the field has been marked as invalid.
10234          * @param {Roo.form.Field} this
10235          * @param {String} msg The validation message
10236          */
10237         invalid : true,
10238         /**
10239          * @event valid
10240          * Fires after the field has been validated with no errors.
10241          * @param {Roo.form.Field} this
10242          */
10243         valid : true,
10244          /**
10245          * @event keyup
10246          * Fires after the key up
10247          * @param {Roo.form.Field} this
10248          * @param {Roo.EventObject}  e The event Object
10249          */
10250         keyup : true
10251     });
10252 };
10253
10254 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10255      /**
10256      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10257       automatic validation (defaults to "keyup").
10258      */
10259     validationEvent : "keyup",
10260      /**
10261      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10262      */
10263     validateOnBlur : true,
10264     /**
10265      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10266      */
10267     validationDelay : 250,
10268      /**
10269      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10270      */
10271     focusClass : "x-form-focus",  // not needed???
10272     
10273        
10274     /**
10275      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10276      */
10277     invalidClass : "has-warning",
10278     
10279     /**
10280      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10281      */
10282     validClass : "has-success",
10283     
10284     /**
10285      * @cfg {Boolean} hasFeedback (true|false) default true
10286      */
10287     hasFeedback : true,
10288     
10289     /**
10290      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10291      */
10292     invalidFeedbackClass : "glyphicon-warning-sign",
10293     
10294     /**
10295      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10296      */
10297     validFeedbackClass : "glyphicon-ok",
10298     
10299     /**
10300      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10301      */
10302     selectOnFocus : false,
10303     
10304      /**
10305      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10306      */
10307     maskRe : null,
10308        /**
10309      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10310      */
10311     vtype : null,
10312     
10313       /**
10314      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10315      */
10316     disableKeyFilter : false,
10317     
10318        /**
10319      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10320      */
10321     disabled : false,
10322      /**
10323      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10324      */
10325     allowBlank : true,
10326     /**
10327      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10328      */
10329     blankText : "Please complete this mandatory field",
10330     
10331      /**
10332      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10333      */
10334     minLength : 0,
10335     /**
10336      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10337      */
10338     maxLength : Number.MAX_VALUE,
10339     /**
10340      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10341      */
10342     minLengthText : "The minimum length for this field is {0}",
10343     /**
10344      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10345      */
10346     maxLengthText : "The maximum length for this field is {0}",
10347   
10348     
10349     /**
10350      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10351      * If available, this function will be called only after the basic validators all return true, and will be passed the
10352      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10353      */
10354     validator : null,
10355     /**
10356      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10357      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10358      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10359      */
10360     regex : null,
10361     /**
10362      * @cfg {String} regexText -- Depricated - use Invalid Text
10363      */
10364     regexText : "",
10365     
10366     /**
10367      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10368      */
10369     invalidText : "",
10370     
10371     
10372     
10373     autocomplete: false,
10374     
10375     
10376     fieldLabel : '',
10377     inputType : 'text',
10378     
10379     name : false,
10380     placeholder: false,
10381     before : false,
10382     after : false,
10383     size : false,
10384     hasFocus : false,
10385     preventMark: false,
10386     isFormField : true,
10387     value : '',
10388     labelWidth : 2,
10389     labelAlign : false,
10390     readOnly : false,
10391     align : false,
10392     formatedValue : false,
10393     forceFeedback : false,
10394     
10395     indicatorpos : 'left',
10396     
10397     labellg : 0,
10398     labelmd : 0,
10399     labelsm : 0,
10400     labelxs : 0,
10401     
10402     capture : '',
10403     accept : '',
10404     
10405     parentLabelAlign : function()
10406     {
10407         var parent = this;
10408         while (parent.parent()) {
10409             parent = parent.parent();
10410             if (typeof(parent.labelAlign) !='undefined') {
10411                 return parent.labelAlign;
10412             }
10413         }
10414         return 'left';
10415         
10416     },
10417     
10418     getAutoCreate : function()
10419     {
10420         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10421         
10422         var id = Roo.id();
10423         
10424         var cfg = {};
10425         
10426         if(this.inputType != 'hidden'){
10427             cfg.cls = 'form-group' //input-group
10428         }
10429         
10430         var input =  {
10431             tag: 'input',
10432             id : id,
10433             type : this.inputType,
10434             value : this.value,
10435             cls : 'form-control',
10436             placeholder : this.placeholder || '',
10437             autocomplete : this.autocomplete || 'new-password'
10438         };
10439         
10440         if(this.capture.length){
10441             input.capture = this.capture;
10442         }
10443         
10444         if(this.accept.length){
10445             input.accept = this.accept + "/*";
10446         }
10447         
10448         if(this.align){
10449             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10450         }
10451         
10452         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10453             input.maxLength = this.maxLength;
10454         }
10455         
10456         if (this.disabled) {
10457             input.disabled=true;
10458         }
10459         
10460         if (this.readOnly) {
10461             input.readonly=true;
10462         }
10463         
10464         if (this.name) {
10465             input.name = this.name;
10466         }
10467         
10468         if (this.size) {
10469             input.cls += ' input-' + this.size;
10470         }
10471         
10472         var settings=this;
10473         ['xs','sm','md','lg'].map(function(size){
10474             if (settings[size]) {
10475                 cfg.cls += ' col-' + size + '-' + settings[size];
10476             }
10477         });
10478         
10479         var inputblock = input;
10480         
10481         var feedback = {
10482             tag: 'span',
10483             cls: 'glyphicon form-control-feedback'
10484         };
10485             
10486         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10487             
10488             inputblock = {
10489                 cls : 'has-feedback',
10490                 cn :  [
10491                     input,
10492                     feedback
10493                 ] 
10494             };  
10495         }
10496         
10497         if (this.before || this.after) {
10498             
10499             inputblock = {
10500                 cls : 'input-group',
10501                 cn :  [] 
10502             };
10503             
10504             if (this.before && typeof(this.before) == 'string') {
10505                 
10506                 inputblock.cn.push({
10507                     tag :'span',
10508                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10509                     html : this.before
10510                 });
10511             }
10512             if (this.before && typeof(this.before) == 'object') {
10513                 this.before = Roo.factory(this.before);
10514                 
10515                 inputblock.cn.push({
10516                     tag :'span',
10517                     cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
10518                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10519                 });
10520             }
10521             
10522             inputblock.cn.push(input);
10523             
10524             if (this.after && typeof(this.after) == 'string') {
10525                 inputblock.cn.push({
10526                     tag :'span',
10527                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10528                     html : this.after
10529                 });
10530             }
10531             if (this.after && typeof(this.after) == 'object') {
10532                 this.after = Roo.factory(this.after);
10533                 
10534                 inputblock.cn.push({
10535                     tag :'span',
10536                     cls : 'roo-input-after input-group-append input-group-text input-group-' +
10537                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10538                 });
10539             }
10540             
10541             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10542                 inputblock.cls += ' has-feedback';
10543                 inputblock.cn.push(feedback);
10544             }
10545         };
10546         var indicator = {
10547             tag : 'i',
10548             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10549             tooltip : 'This field is required'
10550         };
10551         if (Roo.bootstrap.version == 4) {
10552             indicator = {
10553                 tag : 'i',
10554                 style : 'display-none'
10555             };
10556         }
10557         if (align ==='left' && this.fieldLabel.length) {
10558             
10559             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10560             
10561             cfg.cn = [
10562                 indicator,
10563                 {
10564                     tag: 'label',
10565                     'for' :  id,
10566                     cls : 'control-label col-form-label',
10567                     html : this.fieldLabel
10568
10569                 },
10570                 {
10571                     cls : "", 
10572                     cn: [
10573                         inputblock
10574                     ]
10575                 }
10576             ];
10577             
10578             var labelCfg = cfg.cn[1];
10579             var contentCfg = cfg.cn[2];
10580             
10581             if(this.indicatorpos == 'right'){
10582                 cfg.cn = [
10583                     {
10584                         tag: 'label',
10585                         'for' :  id,
10586                         cls : 'control-label col-form-label',
10587                         cn : [
10588                             {
10589                                 tag : 'span',
10590                                 html : this.fieldLabel
10591                             },
10592                             indicator
10593                         ]
10594                     },
10595                     {
10596                         cls : "",
10597                         cn: [
10598                             inputblock
10599                         ]
10600                     }
10601
10602                 ];
10603                 
10604                 labelCfg = cfg.cn[0];
10605                 contentCfg = cfg.cn[1];
10606             
10607             }
10608             
10609             if(this.labelWidth > 12){
10610                 labelCfg.style = "width: " + this.labelWidth + 'px';
10611             }
10612             
10613             if(this.labelWidth < 13 && this.labelmd == 0){
10614                 this.labelmd = this.labelWidth;
10615             }
10616             
10617             if(this.labellg > 0){
10618                 labelCfg.cls += ' col-lg-' + this.labellg;
10619                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10620             }
10621             
10622             if(this.labelmd > 0){
10623                 labelCfg.cls += ' col-md-' + this.labelmd;
10624                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10625             }
10626             
10627             if(this.labelsm > 0){
10628                 labelCfg.cls += ' col-sm-' + this.labelsm;
10629                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10630             }
10631             
10632             if(this.labelxs > 0){
10633                 labelCfg.cls += ' col-xs-' + this.labelxs;
10634                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10635             }
10636             
10637             
10638         } else if ( this.fieldLabel.length) {
10639                 
10640             cfg.cn = [
10641                 {
10642                     tag : 'i',
10643                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10644                     tooltip : 'This field is required'
10645                 },
10646                 {
10647                     tag: 'label',
10648                    //cls : 'input-group-addon',
10649                     html : this.fieldLabel
10650
10651                 },
10652
10653                inputblock
10654
10655            ];
10656            
10657            if(this.indicatorpos == 'right'){
10658                 
10659                 cfg.cn = [
10660                     {
10661                         tag: 'label',
10662                        //cls : 'input-group-addon',
10663                         html : this.fieldLabel
10664
10665                     },
10666                     {
10667                         tag : 'i',
10668                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10669                         tooltip : 'This field is required'
10670                     },
10671
10672                    inputblock
10673
10674                ];
10675
10676             }
10677
10678         } else {
10679             
10680             cfg.cn = [
10681
10682                     inputblock
10683
10684             ];
10685                 
10686                 
10687         };
10688         
10689         if (this.parentType === 'Navbar' &&  this.parent().bar) {
10690            cfg.cls += ' navbar-form';
10691         }
10692         
10693         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10694             // on BS4 we do this only if not form 
10695             cfg.cls += ' navbar-form';
10696             cfg.tag = 'li';
10697         }
10698         
10699         return cfg;
10700         
10701     },
10702     /**
10703      * return the real input element.
10704      */
10705     inputEl: function ()
10706     {
10707         return this.el.select('input.form-control',true).first();
10708     },
10709     
10710     tooltipEl : function()
10711     {
10712         return this.inputEl();
10713     },
10714     
10715     indicatorEl : function()
10716     {
10717         if (Roo.bootstrap.version == 4) {
10718             return false; // not enabled in v4 yet.
10719         }
10720         
10721         var indicator = this.el.select('i.roo-required-indicator',true).first();
10722         
10723         if(!indicator){
10724             return false;
10725         }
10726         
10727         return indicator;
10728         
10729     },
10730     
10731     setDisabled : function(v)
10732     {
10733         var i  = this.inputEl().dom;
10734         if (!v) {
10735             i.removeAttribute('disabled');
10736             return;
10737             
10738         }
10739         i.setAttribute('disabled','true');
10740     },
10741     initEvents : function()
10742     {
10743           
10744         this.inputEl().on("keydown" , this.fireKey,  this);
10745         this.inputEl().on("focus", this.onFocus,  this);
10746         this.inputEl().on("blur", this.onBlur,  this);
10747         
10748         this.inputEl().relayEvent('keyup', this);
10749         
10750         this.indicator = this.indicatorEl();
10751         
10752         if(this.indicator){
10753             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
10754         }
10755  
10756         // reference to original value for reset
10757         this.originalValue = this.getValue();
10758         //Roo.form.TextField.superclass.initEvents.call(this);
10759         if(this.validationEvent == 'keyup'){
10760             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
10761             this.inputEl().on('keyup', this.filterValidation, this);
10762         }
10763         else if(this.validationEvent !== false){
10764             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
10765         }
10766         
10767         if(this.selectOnFocus){
10768             this.on("focus", this.preFocus, this);
10769             
10770         }
10771         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
10772             this.inputEl().on("keypress", this.filterKeys, this);
10773         } else {
10774             this.inputEl().relayEvent('keypress', this);
10775         }
10776        /* if(this.grow){
10777             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
10778             this.el.on("click", this.autoSize,  this);
10779         }
10780         */
10781         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
10782             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
10783         }
10784         
10785         if (typeof(this.before) == 'object') {
10786             this.before.render(this.el.select('.roo-input-before',true).first());
10787         }
10788         if (typeof(this.after) == 'object') {
10789             this.after.render(this.el.select('.roo-input-after',true).first());
10790         }
10791         
10792         this.inputEl().on('change', this.onChange, this);
10793         
10794     },
10795     filterValidation : function(e){
10796         if(!e.isNavKeyPress()){
10797             this.validationTask.delay(this.validationDelay);
10798         }
10799     },
10800      /**
10801      * Validates the field value
10802      * @return {Boolean} True if the value is valid, else false
10803      */
10804     validate : function(){
10805         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
10806         if(this.disabled || this.validateValue(this.getRawValue())){
10807             this.markValid();
10808             return true;
10809         }
10810         
10811         this.markInvalid();
10812         return false;
10813     },
10814     
10815     
10816     /**
10817      * Validates a value according to the field's validation rules and marks the field as invalid
10818      * if the validation fails
10819      * @param {Mixed} value The value to validate
10820      * @return {Boolean} True if the value is valid, else false
10821      */
10822     validateValue : function(value)
10823     {
10824         if(this.getVisibilityEl().hasClass('hidden')){
10825             return true;
10826         }
10827         
10828         if(value.length < 1)  { // if it's blank
10829             if(this.allowBlank){
10830                 return true;
10831             }
10832             return false;
10833         }
10834         
10835         if(value.length < this.minLength){
10836             return false;
10837         }
10838         if(value.length > this.maxLength){
10839             return false;
10840         }
10841         if(this.vtype){
10842             var vt = Roo.form.VTypes;
10843             if(!vt[this.vtype](value, this)){
10844                 return false;
10845             }
10846         }
10847         if(typeof this.validator == "function"){
10848             var msg = this.validator(value);
10849             if(msg !== true){
10850                 return false;
10851             }
10852             if (typeof(msg) == 'string') {
10853                 this.invalidText = msg;
10854             }
10855         }
10856         
10857         if(this.regex && !this.regex.test(value)){
10858             return false;
10859         }
10860         
10861         return true;
10862     },
10863     
10864      // private
10865     fireKey : function(e){
10866         //Roo.log('field ' + e.getKey());
10867         if(e.isNavKeyPress()){
10868             this.fireEvent("specialkey", this, e);
10869         }
10870     },
10871     focus : function (selectText){
10872         if(this.rendered){
10873             this.inputEl().focus();
10874             if(selectText === true){
10875                 this.inputEl().dom.select();
10876             }
10877         }
10878         return this;
10879     } ,
10880     
10881     onFocus : function(){
10882         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10883            // this.el.addClass(this.focusClass);
10884         }
10885         if(!this.hasFocus){
10886             this.hasFocus = true;
10887             this.startValue = this.getValue();
10888             this.fireEvent("focus", this);
10889         }
10890     },
10891     
10892     beforeBlur : Roo.emptyFn,
10893
10894     
10895     // private
10896     onBlur : function(){
10897         this.beforeBlur();
10898         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10899             //this.el.removeClass(this.focusClass);
10900         }
10901         this.hasFocus = false;
10902         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
10903             this.validate();
10904         }
10905         var v = this.getValue();
10906         if(String(v) !== String(this.startValue)){
10907             this.fireEvent('change', this, v, this.startValue);
10908         }
10909         this.fireEvent("blur", this);
10910     },
10911     
10912     onChange : function(e)
10913     {
10914         var v = this.getValue();
10915         if(String(v) !== String(this.startValue)){
10916             this.fireEvent('change', this, v, this.startValue);
10917         }
10918         
10919     },
10920     
10921     /**
10922      * Resets the current field value to the originally loaded value and clears any validation messages
10923      */
10924     reset : function(){
10925         this.setValue(this.originalValue);
10926         this.validate();
10927     },
10928      /**
10929      * Returns the name of the field
10930      * @return {Mixed} name The name field
10931      */
10932     getName: function(){
10933         return this.name;
10934     },
10935      /**
10936      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
10937      * @return {Mixed} value The field value
10938      */
10939     getValue : function(){
10940         
10941         var v = this.inputEl().getValue();
10942         
10943         return v;
10944     },
10945     /**
10946      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
10947      * @return {Mixed} value The field value
10948      */
10949     getRawValue : function(){
10950         var v = this.inputEl().getValue();
10951         
10952         return v;
10953     },
10954     
10955     /**
10956      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
10957      * @param {Mixed} value The value to set
10958      */
10959     setRawValue : function(v){
10960         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
10961     },
10962     
10963     selectText : function(start, end){
10964         var v = this.getRawValue();
10965         if(v.length > 0){
10966             start = start === undefined ? 0 : start;
10967             end = end === undefined ? v.length : end;
10968             var d = this.inputEl().dom;
10969             if(d.setSelectionRange){
10970                 d.setSelectionRange(start, end);
10971             }else if(d.createTextRange){
10972                 var range = d.createTextRange();
10973                 range.moveStart("character", start);
10974                 range.moveEnd("character", v.length-end);
10975                 range.select();
10976             }
10977         }
10978     },
10979     
10980     /**
10981      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
10982      * @param {Mixed} value The value to set
10983      */
10984     setValue : function(v){
10985         this.value = v;
10986         if(this.rendered){
10987             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
10988             this.validate();
10989         }
10990     },
10991     
10992     /*
10993     processValue : function(value){
10994         if(this.stripCharsRe){
10995             var newValue = value.replace(this.stripCharsRe, '');
10996             if(newValue !== value){
10997                 this.setRawValue(newValue);
10998                 return newValue;
10999             }
11000         }
11001         return value;
11002     },
11003   */
11004     preFocus : function(){
11005         
11006         if(this.selectOnFocus){
11007             this.inputEl().dom.select();
11008         }
11009     },
11010     filterKeys : function(e){
11011         var k = e.getKey();
11012         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11013             return;
11014         }
11015         var c = e.getCharCode(), cc = String.fromCharCode(c);
11016         if(Roo.isIE && (e.isSpecialKey() || !cc)){
11017             return;
11018         }
11019         if(!this.maskRe.test(cc)){
11020             e.stopEvent();
11021         }
11022     },
11023      /**
11024      * Clear any invalid styles/messages for this field
11025      */
11026     clearInvalid : function(){
11027         
11028         if(!this.el || this.preventMark){ // not rendered
11029             return;
11030         }
11031         
11032         
11033         this.el.removeClass([this.invalidClass, 'is-invalid']);
11034         
11035         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11036             
11037             var feedback = this.el.select('.form-control-feedback', true).first();
11038             
11039             if(feedback){
11040                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11041             }
11042             
11043         }
11044         
11045         if(this.indicator){
11046             this.indicator.removeClass('visible');
11047             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11048         }
11049         
11050         this.fireEvent('valid', this);
11051     },
11052     
11053      /**
11054      * Mark this field as valid
11055      */
11056     markValid : function()
11057     {
11058         if(!this.el  || this.preventMark){ // not rendered...
11059             return;
11060         }
11061         
11062         this.el.removeClass([this.invalidClass, this.validClass]);
11063         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11064
11065         var feedback = this.el.select('.form-control-feedback', true).first();
11066             
11067         if(feedback){
11068             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11069         }
11070         
11071         if(this.indicator){
11072             this.indicator.removeClass('visible');
11073             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11074         }
11075         
11076         if(this.disabled){
11077             return;
11078         }
11079         
11080            
11081         if(this.allowBlank && !this.getRawValue().length){
11082             return;
11083         }
11084         if (Roo.bootstrap.version == 3) {
11085             this.el.addClass(this.validClass);
11086         } else {
11087             this.inputEl().addClass('is-valid');
11088         }
11089
11090         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11091             
11092             var feedback = this.el.select('.form-control-feedback', true).first();
11093             
11094             if(feedback){
11095                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11096                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11097             }
11098             
11099         }
11100         
11101         this.fireEvent('valid', this);
11102     },
11103     
11104      /**
11105      * Mark this field as invalid
11106      * @param {String} msg The validation message
11107      */
11108     markInvalid : function(msg)
11109     {
11110         if(!this.el  || this.preventMark){ // not rendered
11111             return;
11112         }
11113         
11114         this.el.removeClass([this.invalidClass, this.validClass]);
11115         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11116         
11117         var feedback = this.el.select('.form-control-feedback', true).first();
11118             
11119         if(feedback){
11120             this.el.select('.form-control-feedback', true).first().removeClass(
11121                     [this.invalidFeedbackClass, this.validFeedbackClass]);
11122         }
11123
11124         if(this.disabled){
11125             return;
11126         }
11127         
11128         if(this.allowBlank && !this.getRawValue().length){
11129             return;
11130         }
11131         
11132         if(this.indicator){
11133             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11134             this.indicator.addClass('visible');
11135         }
11136         if (Roo.bootstrap.version == 3) {
11137             this.el.addClass(this.invalidClass);
11138         } else {
11139             this.inputEl().addClass('is-invalid');
11140         }
11141         
11142         
11143         
11144         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11145             
11146             var feedback = this.el.select('.form-control-feedback', true).first();
11147             
11148             if(feedback){
11149                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11150                 
11151                 if(this.getValue().length || this.forceFeedback){
11152                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11153                 }
11154                 
11155             }
11156             
11157         }
11158         
11159         this.fireEvent('invalid', this, msg);
11160     },
11161     // private
11162     SafariOnKeyDown : function(event)
11163     {
11164         // this is a workaround for a password hang bug on chrome/ webkit.
11165         if (this.inputEl().dom.type != 'password') {
11166             return;
11167         }
11168         
11169         var isSelectAll = false;
11170         
11171         if(this.inputEl().dom.selectionEnd > 0){
11172             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11173         }
11174         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11175             event.preventDefault();
11176             this.setValue('');
11177             return;
11178         }
11179         
11180         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11181             
11182             event.preventDefault();
11183             // this is very hacky as keydown always get's upper case.
11184             //
11185             var cc = String.fromCharCode(event.getCharCode());
11186             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
11187             
11188         }
11189     },
11190     adjustWidth : function(tag, w){
11191         tag = tag.toLowerCase();
11192         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11193             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11194                 if(tag == 'input'){
11195                     return w + 2;
11196                 }
11197                 if(tag == 'textarea'){
11198                     return w-2;
11199                 }
11200             }else if(Roo.isOpera){
11201                 if(tag == 'input'){
11202                     return w + 2;
11203                 }
11204                 if(tag == 'textarea'){
11205                     return w-2;
11206                 }
11207             }
11208         }
11209         return w;
11210     },
11211     
11212     setFieldLabel : function(v)
11213     {
11214         if(!this.rendered){
11215             return;
11216         }
11217         
11218         if(this.indicatorEl()){
11219             var ar = this.el.select('label > span',true);
11220             
11221             if (ar.elements.length) {
11222                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11223                 this.fieldLabel = v;
11224                 return;
11225             }
11226             
11227             var br = this.el.select('label',true);
11228             
11229             if(br.elements.length) {
11230                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11231                 this.fieldLabel = v;
11232                 return;
11233             }
11234             
11235             Roo.log('Cannot Found any of label > span || label in input');
11236             return;
11237         }
11238         
11239         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11240         this.fieldLabel = v;
11241         
11242         
11243     }
11244 });
11245
11246  
11247 /*
11248  * - LGPL
11249  *
11250  * Input
11251  * 
11252  */
11253
11254 /**
11255  * @class Roo.bootstrap.TextArea
11256  * @extends Roo.bootstrap.Input
11257  * Bootstrap TextArea class
11258  * @cfg {Number} cols Specifies the visible width of a text area
11259  * @cfg {Number} rows Specifies the visible number of lines in a text area
11260  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11261  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11262  * @cfg {string} html text
11263  * 
11264  * @constructor
11265  * Create a new TextArea
11266  * @param {Object} config The config object
11267  */
11268
11269 Roo.bootstrap.TextArea = function(config){
11270     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11271    
11272 };
11273
11274 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11275      
11276     cols : false,
11277     rows : 5,
11278     readOnly : false,
11279     warp : 'soft',
11280     resize : false,
11281     value: false,
11282     html: false,
11283     
11284     getAutoCreate : function(){
11285         
11286         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11287         
11288         var id = Roo.id();
11289         
11290         var cfg = {};
11291         
11292         if(this.inputType != 'hidden'){
11293             cfg.cls = 'form-group' //input-group
11294         }
11295         
11296         var input =  {
11297             tag: 'textarea',
11298             id : id,
11299             warp : this.warp,
11300             rows : this.rows,
11301             value : this.value || '',
11302             html: this.html || '',
11303             cls : 'form-control',
11304             placeholder : this.placeholder || '' 
11305             
11306         };
11307         
11308         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11309             input.maxLength = this.maxLength;
11310         }
11311         
11312         if(this.resize){
11313             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11314         }
11315         
11316         if(this.cols){
11317             input.cols = this.cols;
11318         }
11319         
11320         if (this.readOnly) {
11321             input.readonly = true;
11322         }
11323         
11324         if (this.name) {
11325             input.name = this.name;
11326         }
11327         
11328         if (this.size) {
11329             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11330         }
11331         
11332         var settings=this;
11333         ['xs','sm','md','lg'].map(function(size){
11334             if (settings[size]) {
11335                 cfg.cls += ' col-' + size + '-' + settings[size];
11336             }
11337         });
11338         
11339         var inputblock = input;
11340         
11341         if(this.hasFeedback && !this.allowBlank){
11342             
11343             var feedback = {
11344                 tag: 'span',
11345                 cls: 'glyphicon form-control-feedback'
11346             };
11347
11348             inputblock = {
11349                 cls : 'has-feedback',
11350                 cn :  [
11351                     input,
11352                     feedback
11353                 ] 
11354             };  
11355         }
11356         
11357         
11358         if (this.before || this.after) {
11359             
11360             inputblock = {
11361                 cls : 'input-group',
11362                 cn :  [] 
11363             };
11364             if (this.before) {
11365                 inputblock.cn.push({
11366                     tag :'span',
11367                     cls : 'input-group-addon',
11368                     html : this.before
11369                 });
11370             }
11371             
11372             inputblock.cn.push(input);
11373             
11374             if(this.hasFeedback && !this.allowBlank){
11375                 inputblock.cls += ' has-feedback';
11376                 inputblock.cn.push(feedback);
11377             }
11378             
11379             if (this.after) {
11380                 inputblock.cn.push({
11381                     tag :'span',
11382                     cls : 'input-group-addon',
11383                     html : this.after
11384                 });
11385             }
11386             
11387         }
11388         
11389         if (align ==='left' && this.fieldLabel.length) {
11390             cfg.cn = [
11391                 {
11392                     tag: 'label',
11393                     'for' :  id,
11394                     cls : 'control-label',
11395                     html : this.fieldLabel
11396                 },
11397                 {
11398                     cls : "",
11399                     cn: [
11400                         inputblock
11401                     ]
11402                 }
11403
11404             ];
11405             
11406             if(this.labelWidth > 12){
11407                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11408             }
11409
11410             if(this.labelWidth < 13 && this.labelmd == 0){
11411                 this.labelmd = this.labelWidth;
11412             }
11413
11414             if(this.labellg > 0){
11415                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11416                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11417             }
11418
11419             if(this.labelmd > 0){
11420                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11421                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11422             }
11423
11424             if(this.labelsm > 0){
11425                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11426                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11427             }
11428
11429             if(this.labelxs > 0){
11430                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11431                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11432             }
11433             
11434         } else if ( this.fieldLabel.length) {
11435             cfg.cn = [
11436
11437                {
11438                    tag: 'label',
11439                    //cls : 'input-group-addon',
11440                    html : this.fieldLabel
11441
11442                },
11443
11444                inputblock
11445
11446            ];
11447
11448         } else {
11449
11450             cfg.cn = [
11451
11452                 inputblock
11453
11454             ];
11455                 
11456         }
11457         
11458         if (this.disabled) {
11459             input.disabled=true;
11460         }
11461         
11462         return cfg;
11463         
11464     },
11465     /**
11466      * return the real textarea element.
11467      */
11468     inputEl: function ()
11469     {
11470         return this.el.select('textarea.form-control',true).first();
11471     },
11472     
11473     /**
11474      * Clear any invalid styles/messages for this field
11475      */
11476     clearInvalid : function()
11477     {
11478         
11479         if(!this.el || this.preventMark){ // not rendered
11480             return;
11481         }
11482         
11483         var label = this.el.select('label', true).first();
11484         var icon = this.el.select('i.fa-star', true).first();
11485         
11486         if(label && icon){
11487             icon.remove();
11488         }
11489         this.el.removeClass( this.validClass);
11490         this.inputEl().removeClass('is-invalid');
11491          
11492         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11493             
11494             var feedback = this.el.select('.form-control-feedback', true).first();
11495             
11496             if(feedback){
11497                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11498             }
11499             
11500         }
11501         
11502         this.fireEvent('valid', this);
11503     },
11504     
11505      /**
11506      * Mark this field as valid
11507      */
11508     markValid : function()
11509     {
11510         if(!this.el  || this.preventMark){ // not rendered
11511             return;
11512         }
11513         
11514         this.el.removeClass([this.invalidClass, this.validClass]);
11515         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11516         
11517         var feedback = this.el.select('.form-control-feedback', true).first();
11518             
11519         if(feedback){
11520             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11521         }
11522
11523         if(this.disabled || this.allowBlank){
11524             return;
11525         }
11526         
11527         var label = this.el.select('label', true).first();
11528         var icon = this.el.select('i.fa-star', true).first();
11529         
11530         if(label && icon){
11531             icon.remove();
11532         }
11533         if (Roo.bootstrap.version == 3) {
11534             this.el.addClass(this.validClass);
11535         } else {
11536             this.inputEl().addClass('is-valid');
11537         }
11538         
11539         
11540         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11541             
11542             var feedback = this.el.select('.form-control-feedback', true).first();
11543             
11544             if(feedback){
11545                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11546                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11547             }
11548             
11549         }
11550         
11551         this.fireEvent('valid', this);
11552     },
11553     
11554      /**
11555      * Mark this field as invalid
11556      * @param {String} msg The validation message
11557      */
11558     markInvalid : function(msg)
11559     {
11560         if(!this.el  || this.preventMark){ // not rendered
11561             return;
11562         }
11563         
11564         this.el.removeClass([this.invalidClass, this.validClass]);
11565         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11566         
11567         var feedback = this.el.select('.form-control-feedback', true).first();
11568             
11569         if(feedback){
11570             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11571         }
11572
11573         if(this.disabled || this.allowBlank){
11574             return;
11575         }
11576         
11577         var label = this.el.select('label', true).first();
11578         var icon = this.el.select('i.fa-star', true).first();
11579         
11580         if(!this.getValue().length && label && !icon){
11581             this.el.createChild({
11582                 tag : 'i',
11583                 cls : 'text-danger fa fa-lg fa-star',
11584                 tooltip : 'This field is required',
11585                 style : 'margin-right:5px;'
11586             }, label, true);
11587         }
11588         
11589         if (Roo.bootstrap.version == 3) {
11590             this.el.addClass(this.invalidClass);
11591         } else {
11592             this.inputEl().addClass('is-invalid');
11593         }
11594         
11595         // fixme ... this may be depricated need to test..
11596         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11597             
11598             var feedback = this.el.select('.form-control-feedback', true).first();
11599             
11600             if(feedback){
11601                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11602                 
11603                 if(this.getValue().length || this.forceFeedback){
11604                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11605                 }
11606                 
11607             }
11608             
11609         }
11610         
11611         this.fireEvent('invalid', this, msg);
11612     }
11613 });
11614
11615  
11616 /*
11617  * - LGPL
11618  *
11619  * trigger field - base class for combo..
11620  * 
11621  */
11622  
11623 /**
11624  * @class Roo.bootstrap.TriggerField
11625  * @extends Roo.bootstrap.Input
11626  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11627  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11628  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11629  * for which you can provide a custom implementation.  For example:
11630  * <pre><code>
11631 var trigger = new Roo.bootstrap.TriggerField();
11632 trigger.onTriggerClick = myTriggerFn;
11633 trigger.applyTo('my-field');
11634 </code></pre>
11635  *
11636  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11637  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11638  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
11639  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11640  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11641
11642  * @constructor
11643  * Create a new TriggerField.
11644  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11645  * to the base TextField)
11646  */
11647 Roo.bootstrap.TriggerField = function(config){
11648     this.mimicing = false;
11649     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11650 };
11651
11652 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
11653     /**
11654      * @cfg {String} triggerClass A CSS class to apply to the trigger
11655      */
11656      /**
11657      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11658      */
11659     hideTrigger:false,
11660
11661     /**
11662      * @cfg {Boolean} removable (true|false) special filter default false
11663      */
11664     removable : false,
11665     
11666     /** @cfg {Boolean} grow @hide */
11667     /** @cfg {Number} growMin @hide */
11668     /** @cfg {Number} growMax @hide */
11669
11670     /**
11671      * @hide 
11672      * @method
11673      */
11674     autoSize: Roo.emptyFn,
11675     // private
11676     monitorTab : true,
11677     // private
11678     deferHeight : true,
11679
11680     
11681     actionMode : 'wrap',
11682     
11683     caret : false,
11684     
11685     
11686     getAutoCreate : function(){
11687        
11688         var align = this.labelAlign || this.parentLabelAlign();
11689         
11690         var id = Roo.id();
11691         
11692         var cfg = {
11693             cls: 'form-group' //input-group
11694         };
11695         
11696         
11697         var input =  {
11698             tag: 'input',
11699             id : id,
11700             type : this.inputType,
11701             cls : 'form-control',
11702             autocomplete: 'new-password',
11703             placeholder : this.placeholder || '' 
11704             
11705         };
11706         if (this.name) {
11707             input.name = this.name;
11708         }
11709         if (this.size) {
11710             input.cls += ' input-' + this.size;
11711         }
11712         
11713         if (this.disabled) {
11714             input.disabled=true;
11715         }
11716         
11717         var inputblock = input;
11718         
11719         if(this.hasFeedback && !this.allowBlank){
11720             
11721             var feedback = {
11722                 tag: 'span',
11723                 cls: 'glyphicon form-control-feedback'
11724             };
11725             
11726             if(this.removable && !this.editable  ){
11727                 inputblock = {
11728                     cls : 'has-feedback',
11729                     cn :  [
11730                         inputblock,
11731                         {
11732                             tag: 'button',
11733                             html : 'x',
11734                             cls : 'roo-combo-removable-btn close'
11735                         },
11736                         feedback
11737                     ] 
11738                 };
11739             } else {
11740                 inputblock = {
11741                     cls : 'has-feedback',
11742                     cn :  [
11743                         inputblock,
11744                         feedback
11745                     ] 
11746                 };
11747             }
11748
11749         } else {
11750             if(this.removable && !this.editable ){
11751                 inputblock = {
11752                     cls : 'roo-removable',
11753                     cn :  [
11754                         inputblock,
11755                         {
11756                             tag: 'button',
11757                             html : 'x',
11758                             cls : 'roo-combo-removable-btn close'
11759                         }
11760                     ] 
11761                 };
11762             }
11763         }
11764         
11765         if (this.before || this.after) {
11766             
11767             inputblock = {
11768                 cls : 'input-group',
11769                 cn :  [] 
11770             };
11771             if (this.before) {
11772                 inputblock.cn.push({
11773                     tag :'span',
11774                     cls : 'input-group-addon input-group-prepend input-group-text',
11775                     html : this.before
11776                 });
11777             }
11778             
11779             inputblock.cn.push(input);
11780             
11781             if(this.hasFeedback && !this.allowBlank){
11782                 inputblock.cls += ' has-feedback';
11783                 inputblock.cn.push(feedback);
11784             }
11785             
11786             if (this.after) {
11787                 inputblock.cn.push({
11788                     tag :'span',
11789                     cls : 'input-group-addon input-group-append input-group-text',
11790                     html : this.after
11791                 });
11792             }
11793             
11794         };
11795         
11796       
11797         
11798         var ibwrap = inputblock;
11799         
11800         if(this.multiple){
11801             ibwrap = {
11802                 tag: 'ul',
11803                 cls: 'roo-select2-choices',
11804                 cn:[
11805                     {
11806                         tag: 'li',
11807                         cls: 'roo-select2-search-field',
11808                         cn: [
11809
11810                             inputblock
11811                         ]
11812                     }
11813                 ]
11814             };
11815                 
11816         }
11817         
11818         var combobox = {
11819             cls: 'roo-select2-container input-group',
11820             cn: [
11821                  {
11822                     tag: 'input',
11823                     type : 'hidden',
11824                     cls: 'form-hidden-field'
11825                 },
11826                 ibwrap
11827             ]
11828         };
11829         
11830         if(!this.multiple && this.showToggleBtn){
11831             
11832             var caret = {
11833                         tag: 'span',
11834                         cls: 'caret'
11835              };
11836             if (this.caret != false) {
11837                 caret = {
11838                      tag: 'i',
11839                      cls: 'fa fa-' + this.caret
11840                 };
11841                 
11842             }
11843             
11844             combobox.cn.push({
11845                 tag :'span',
11846                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
11847                 cn : [
11848                     Roo.bootstrap.version == 3 ? caret : '',
11849                     {
11850                         tag: 'span',
11851                         cls: 'combobox-clear',
11852                         cn  : [
11853                             {
11854                                 tag : 'i',
11855                                 cls: 'icon-remove'
11856                             }
11857                         ]
11858                     }
11859                 ]
11860
11861             })
11862         }
11863         
11864         if(this.multiple){
11865             combobox.cls += ' roo-select2-container-multi';
11866         }
11867          var indicator = {
11868             tag : 'i',
11869             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11870             tooltip : 'This field is required'
11871         };
11872         if (Roo.bootstrap.version == 4) {
11873             indicator = {
11874                 tag : 'i',
11875                 style : 'display:none'
11876             };
11877         }
11878         
11879         
11880         if (align ==='left' && this.fieldLabel.length) {
11881             
11882             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
11883
11884             cfg.cn = [
11885                 indicator,
11886                 {
11887                     tag: 'label',
11888                     'for' :  id,
11889                     cls : 'control-label',
11890                     html : this.fieldLabel
11891
11892                 },
11893                 {
11894                     cls : "", 
11895                     cn: [
11896                         combobox
11897                     ]
11898                 }
11899
11900             ];
11901             
11902             var labelCfg = cfg.cn[1];
11903             var contentCfg = cfg.cn[2];
11904             
11905             if(this.indicatorpos == 'right'){
11906                 cfg.cn = [
11907                     {
11908                         tag: 'label',
11909                         'for' :  id,
11910                         cls : 'control-label',
11911                         cn : [
11912                             {
11913                                 tag : 'span',
11914                                 html : this.fieldLabel
11915                             },
11916                             indicator
11917                         ]
11918                     },
11919                     {
11920                         cls : "", 
11921                         cn: [
11922                             combobox
11923                         ]
11924                     }
11925
11926                 ];
11927                 
11928                 labelCfg = cfg.cn[0];
11929                 contentCfg = cfg.cn[1];
11930             }
11931             
11932             if(this.labelWidth > 12){
11933                 labelCfg.style = "width: " + this.labelWidth + 'px';
11934             }
11935             
11936             if(this.labelWidth < 13 && this.labelmd == 0){
11937                 this.labelmd = this.labelWidth;
11938             }
11939             
11940             if(this.labellg > 0){
11941                 labelCfg.cls += ' col-lg-' + this.labellg;
11942                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11943             }
11944             
11945             if(this.labelmd > 0){
11946                 labelCfg.cls += ' col-md-' + this.labelmd;
11947                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11948             }
11949             
11950             if(this.labelsm > 0){
11951                 labelCfg.cls += ' col-sm-' + this.labelsm;
11952                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11953             }
11954             
11955             if(this.labelxs > 0){
11956                 labelCfg.cls += ' col-xs-' + this.labelxs;
11957                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11958             }
11959             
11960         } else if ( this.fieldLabel.length) {
11961 //                Roo.log(" label");
11962             cfg.cn = [
11963                 indicator,
11964                {
11965                    tag: 'label',
11966                    //cls : 'input-group-addon',
11967                    html : this.fieldLabel
11968
11969                },
11970
11971                combobox
11972
11973             ];
11974             
11975             if(this.indicatorpos == 'right'){
11976                 
11977                 cfg.cn = [
11978                     {
11979                        tag: 'label',
11980                        cn : [
11981                            {
11982                                tag : 'span',
11983                                html : this.fieldLabel
11984                            },
11985                            indicator
11986                        ]
11987
11988                     },
11989                     combobox
11990
11991                 ];
11992
11993             }
11994
11995         } else {
11996             
11997 //                Roo.log(" no label && no align");
11998                 cfg = combobox
11999                      
12000                 
12001         }
12002         
12003         var settings=this;
12004         ['xs','sm','md','lg'].map(function(size){
12005             if (settings[size]) {
12006                 cfg.cls += ' col-' + size + '-' + settings[size];
12007             }
12008         });
12009         
12010         return cfg;
12011         
12012     },
12013     
12014     
12015     
12016     // private
12017     onResize : function(w, h){
12018 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12019 //        if(typeof w == 'number'){
12020 //            var x = w - this.trigger.getWidth();
12021 //            this.inputEl().setWidth(this.adjustWidth('input', x));
12022 //            this.trigger.setStyle('left', x+'px');
12023 //        }
12024     },
12025
12026     // private
12027     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12028
12029     // private
12030     getResizeEl : function(){
12031         return this.inputEl();
12032     },
12033
12034     // private
12035     getPositionEl : function(){
12036         return this.inputEl();
12037     },
12038
12039     // private
12040     alignErrorIcon : function(){
12041         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12042     },
12043
12044     // private
12045     initEvents : function(){
12046         
12047         this.createList();
12048         
12049         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12050         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12051         if(!this.multiple && this.showToggleBtn){
12052             this.trigger = this.el.select('span.dropdown-toggle',true).first();
12053             if(this.hideTrigger){
12054                 this.trigger.setDisplayed(false);
12055             }
12056             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12057         }
12058         
12059         if(this.multiple){
12060             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12061         }
12062         
12063         if(this.removable && !this.editable && !this.tickable){
12064             var close = this.closeTriggerEl();
12065             
12066             if(close){
12067                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12068                 close.on('click', this.removeBtnClick, this, close);
12069             }
12070         }
12071         
12072         //this.trigger.addClassOnOver('x-form-trigger-over');
12073         //this.trigger.addClassOnClick('x-form-trigger-click');
12074         
12075         //if(!this.width){
12076         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12077         //}
12078     },
12079     
12080     closeTriggerEl : function()
12081     {
12082         var close = this.el.select('.roo-combo-removable-btn', true).first();
12083         return close ? close : false;
12084     },
12085     
12086     removeBtnClick : function(e, h, el)
12087     {
12088         e.preventDefault();
12089         
12090         if(this.fireEvent("remove", this) !== false){
12091             this.reset();
12092             this.fireEvent("afterremove", this)
12093         }
12094     },
12095     
12096     createList : function()
12097     {
12098         this.list = Roo.get(document.body).createChild({
12099             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12100             cls: 'typeahead typeahead-long dropdown-menu',
12101             style: 'display:none'
12102         });
12103         
12104         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12105         
12106     },
12107
12108     // private
12109     initTrigger : function(){
12110        
12111     },
12112
12113     // private
12114     onDestroy : function(){
12115         if(this.trigger){
12116             this.trigger.removeAllListeners();
12117           //  this.trigger.remove();
12118         }
12119         //if(this.wrap){
12120         //    this.wrap.remove();
12121         //}
12122         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12123     },
12124
12125     // private
12126     onFocus : function(){
12127         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12128         /*
12129         if(!this.mimicing){
12130             this.wrap.addClass('x-trigger-wrap-focus');
12131             this.mimicing = true;
12132             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12133             if(this.monitorTab){
12134                 this.el.on("keydown", this.checkTab, this);
12135             }
12136         }
12137         */
12138     },
12139
12140     // private
12141     checkTab : function(e){
12142         if(e.getKey() == e.TAB){
12143             this.triggerBlur();
12144         }
12145     },
12146
12147     // private
12148     onBlur : function(){
12149         // do nothing
12150     },
12151
12152     // private
12153     mimicBlur : function(e, t){
12154         /*
12155         if(!this.wrap.contains(t) && this.validateBlur()){
12156             this.triggerBlur();
12157         }
12158         */
12159     },
12160
12161     // private
12162     triggerBlur : function(){
12163         this.mimicing = false;
12164         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12165         if(this.monitorTab){
12166             this.el.un("keydown", this.checkTab, this);
12167         }
12168         //this.wrap.removeClass('x-trigger-wrap-focus');
12169         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12170     },
12171
12172     // private
12173     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12174     validateBlur : function(e, t){
12175         return true;
12176     },
12177
12178     // private
12179     onDisable : function(){
12180         this.inputEl().dom.disabled = true;
12181         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12182         //if(this.wrap){
12183         //    this.wrap.addClass('x-item-disabled');
12184         //}
12185     },
12186
12187     // private
12188     onEnable : function(){
12189         this.inputEl().dom.disabled = false;
12190         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12191         //if(this.wrap){
12192         //    this.el.removeClass('x-item-disabled');
12193         //}
12194     },
12195
12196     // private
12197     onShow : function(){
12198         var ae = this.getActionEl();
12199         
12200         if(ae){
12201             ae.dom.style.display = '';
12202             ae.dom.style.visibility = 'visible';
12203         }
12204     },
12205
12206     // private
12207     
12208     onHide : function(){
12209         var ae = this.getActionEl();
12210         ae.dom.style.display = 'none';
12211     },
12212
12213     /**
12214      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
12215      * by an implementing function.
12216      * @method
12217      * @param {EventObject} e
12218      */
12219     onTriggerClick : Roo.emptyFn
12220 });
12221  
12222 /*
12223 * Licence: LGPL
12224 */
12225
12226 /**
12227  * @class Roo.bootstrap.CardUploader
12228  * @extends Roo.bootstrap.Button
12229  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12230  * @cfg {Number} errorTimeout default 3000
12231  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
12232  * @cfg {Array}  html The button text.
12233
12234  *
12235  * @constructor
12236  * Create a new CardUploader
12237  * @param {Object} config The config object
12238  */
12239
12240 Roo.bootstrap.CardUploader = function(config){
12241     
12242  
12243     
12244     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12245     
12246     
12247     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
12248         return r.data.id
12249         });
12250     
12251     
12252 };
12253
12254 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
12255     
12256      
12257     errorTimeout : 3000,
12258      
12259     images : false,
12260    
12261     fileCollection : false,
12262     allowBlank : true,
12263     
12264     getAutoCreate : function()
12265     {
12266         
12267         var cfg =  {
12268             cls :'form-group' ,
12269             cn : [
12270                
12271                 {
12272                     tag: 'label',
12273                    //cls : 'input-group-addon',
12274                     html : this.fieldLabel
12275
12276                 },
12277
12278                 {
12279                     tag: 'input',
12280                     type : 'hidden',
12281                     value : this.value,
12282                     cls : 'd-none  form-control'
12283                 },
12284                 
12285                 {
12286                     tag: 'input',
12287                     multiple : 'multiple',
12288                     type : 'file',
12289                     cls : 'd-none  roo-card-upload-selector'
12290                 },
12291                 
12292                 {
12293                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12294                 },
12295                 {
12296                     cls : 'card-columns roo-card-uploader-container'
12297                 }
12298
12299             ]
12300         };
12301            
12302          
12303         return cfg;
12304     },
12305     
12306     getChildContainer : function() /// what children are added to.
12307     {
12308         return this.containerEl;
12309     },
12310    
12311     getButtonContainer : function() /// what children are added to.
12312     {
12313         return this.el.select(".roo-card-uploader-button-container").first();
12314     },
12315    
12316     initEvents : function()
12317     {
12318         
12319         Roo.bootstrap.Input.prototype.initEvents.call(this);
12320         
12321         var t = this;
12322         this.addxtype({
12323             xns: Roo.bootstrap,
12324
12325             xtype : 'Button',
12326             container_method : 'getButtonContainer' ,            
12327             html :  this.html, // fix changable?
12328             cls : 'w-100 ',
12329             listeners : {
12330                 'click' : function(btn, e) {
12331                     t.onClick(e);
12332                 }
12333             }
12334         });
12335         
12336         
12337         
12338         
12339         this.urlAPI = (window.createObjectURL && window) || 
12340                                 (window.URL && URL.revokeObjectURL && URL) || 
12341                                 (window.webkitURL && webkitURL);
12342                         
12343          
12344          
12345          
12346         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12347         
12348         this.selectorEl.on('change', this.onFileSelected, this);
12349         if (this.images) {
12350             var t = this;
12351             this.images.forEach(function(img) {
12352                 t.addCard(img)
12353             });
12354             this.images = false;
12355         }
12356         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12357          
12358        
12359     },
12360     
12361    
12362     onClick : function(e)
12363     {
12364         e.preventDefault();
12365          
12366         this.selectorEl.dom.click();
12367          
12368     },
12369     
12370     onFileSelected : function(e)
12371     {
12372         e.preventDefault();
12373         
12374         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12375             return;
12376         }
12377         
12378         Roo.each(this.selectorEl.dom.files, function(file){    
12379             this.addFile(file);
12380         }, this);
12381          
12382     },
12383     
12384       
12385     
12386       
12387     
12388     addFile : function(file)
12389     {
12390            
12391         if(typeof(file) === 'string'){
12392             throw "Add file by name?"; // should not happen
12393             return;
12394         }
12395         
12396         if(!file || !this.urlAPI){
12397             return;
12398         }
12399         
12400         // file;
12401         // file.type;
12402         
12403         var _this = this;
12404         
12405         
12406         var url = _this.urlAPI.createObjectURL( file);
12407            
12408         this.addCard({
12409             id : Roo.bootstrap.CardUploader.ID--,
12410             is_uploaded : false,
12411             src : url,
12412             title : file.name,
12413             mimetype : file.type,
12414             preview : false,
12415             is_deleted : 0
12416         })
12417         
12418     },
12419     
12420     addCard : function (data)
12421     {
12422         // hidden input element?
12423         // if the file is not an image...
12424         //then we need to use something other that and header_image
12425         var t = this;
12426         //   remove.....
12427         var footer = [
12428             {
12429                 xns : Roo.bootstrap,
12430                 xtype : 'CardFooter',
12431                 items: [
12432                     {
12433                         xns : Roo.bootstrap,
12434                         xtype : 'Element',
12435                         cls : 'd-flex',
12436                         items : [
12437                             
12438                             {
12439                                 xns : Roo.bootstrap,
12440                                 xtype : 'Button',
12441                                 html : String.format("<small>{0}</small>", data.title),
12442                                 cls : 'col-11 text-left',
12443                                 size: 'sm',
12444                                 weight: 'link',
12445                                 fa : 'download',
12446                                 listeners : {
12447                                     click : function() {
12448                                         this.downloadCard(data.id)
12449                                     }
12450                                 }
12451                             },
12452                           
12453                             {
12454                                 xns : Roo.bootstrap,
12455                                 xtype : 'Button',
12456                                 
12457                                 size : 'sm',
12458                                 weight: 'danger',
12459                                 cls : 'col-1',
12460                                 fa : 'times',
12461                                 listeners : {
12462                                     click : function() {
12463                                         t.removeCard(data.id)
12464                                     }
12465                                 }
12466                             }
12467                         ]
12468                     }
12469                     
12470                 ] 
12471             }
12472             
12473         ];
12474
12475         var cn = this.addxtype(
12476             {
12477                  
12478                 xns : Roo.bootstrap,
12479                 xtype : 'Card',
12480                 closeable : true,
12481                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12482                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
12483                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
12484                 data : data,
12485                 html : false,
12486                  
12487                 items : footer,
12488                 initEvents : function() {
12489                     Roo.bootstrap.Card.prototype.initEvents.call(this);
12490                     this.imgEl = this.el.select('.card-img-top').first();
12491                     if (this.imgEl) {
12492                         this.imgEl.on('click', function() { t.previewCard( data.id); }, this);
12493                         this.imgEl.set({ 'pointer' : 'cursor' });
12494                                   
12495                     }
12496                     
12497                   
12498                 }
12499                 
12500             }
12501         );
12502         // dont' really need ot update items.
12503         // this.items.push(cn);
12504         this.fileCollection.add(cn);
12505         this.updateInput();
12506         
12507     },
12508     removeCard : function(id)
12509     {
12510         
12511         var card  = this.fileCollection.get(id);
12512         card.data.is_deleted = 1;
12513         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12514         this.fileCollection.remove(card);
12515         //this.items = this.items.filter(function(e) { return e != card });
12516         // dont' really need ot update items.
12517         card.el.dom.parentNode.removeChild(card.el.dom);
12518         
12519     },
12520     reset: function()
12521     {
12522         this.fileCollection.each(function(card) {
12523             card.el.dom.parentNode.removeChild(card.el.dom);    
12524         });
12525         this.fileCollection.clear();
12526         this.updateInput();
12527     },
12528     
12529     updateInput : function()
12530     {
12531         var data = [];
12532         this.fileCollection.each(function(e) {
12533             data.push(e.data);
12534         });
12535         
12536         this.inputEl().dom.value = JSON.stringify(data);
12537     }
12538     
12539     
12540 });
12541
12542
12543 Roo.bootstrap.CardUploader.ID = -1;/*
12544  * Based on:
12545  * Ext JS Library 1.1.1
12546  * Copyright(c) 2006-2007, Ext JS, LLC.
12547  *
12548  * Originally Released Under LGPL - original licence link has changed is not relivant.
12549  *
12550  * Fork - LGPL
12551  * <script type="text/javascript">
12552  */
12553
12554
12555 /**
12556  * @class Roo.data.SortTypes
12557  * @singleton
12558  * Defines the default sorting (casting?) comparison functions used when sorting data.
12559  */
12560 Roo.data.SortTypes = {
12561     /**
12562      * Default sort that does nothing
12563      * @param {Mixed} s The value being converted
12564      * @return {Mixed} The comparison value
12565      */
12566     none : function(s){
12567         return s;
12568     },
12569     
12570     /**
12571      * The regular expression used to strip tags
12572      * @type {RegExp}
12573      * @property
12574      */
12575     stripTagsRE : /<\/?[^>]+>/gi,
12576     
12577     /**
12578      * Strips all HTML tags to sort on text only
12579      * @param {Mixed} s The value being converted
12580      * @return {String} The comparison value
12581      */
12582     asText : function(s){
12583         return String(s).replace(this.stripTagsRE, "");
12584     },
12585     
12586     /**
12587      * Strips all HTML tags to sort on text only - Case insensitive
12588      * @param {Mixed} s The value being converted
12589      * @return {String} The comparison value
12590      */
12591     asUCText : function(s){
12592         return String(s).toUpperCase().replace(this.stripTagsRE, "");
12593     },
12594     
12595     /**
12596      * Case insensitive string
12597      * @param {Mixed} s The value being converted
12598      * @return {String} The comparison value
12599      */
12600     asUCString : function(s) {
12601         return String(s).toUpperCase();
12602     },
12603     
12604     /**
12605      * Date sorting
12606      * @param {Mixed} s The value being converted
12607      * @return {Number} The comparison value
12608      */
12609     asDate : function(s) {
12610         if(!s){
12611             return 0;
12612         }
12613         if(s instanceof Date){
12614             return s.getTime();
12615         }
12616         return Date.parse(String(s));
12617     },
12618     
12619     /**
12620      * Float sorting
12621      * @param {Mixed} s The value being converted
12622      * @return {Float} The comparison value
12623      */
12624     asFloat : function(s) {
12625         var val = parseFloat(String(s).replace(/,/g, ""));
12626         if(isNaN(val)) {
12627             val = 0;
12628         }
12629         return val;
12630     },
12631     
12632     /**
12633      * Integer sorting
12634      * @param {Mixed} s The value being converted
12635      * @return {Number} The comparison value
12636      */
12637     asInt : function(s) {
12638         var val = parseInt(String(s).replace(/,/g, ""));
12639         if(isNaN(val)) {
12640             val = 0;
12641         }
12642         return val;
12643     }
12644 };/*
12645  * Based on:
12646  * Ext JS Library 1.1.1
12647  * Copyright(c) 2006-2007, Ext JS, LLC.
12648  *
12649  * Originally Released Under LGPL - original licence link has changed is not relivant.
12650  *
12651  * Fork - LGPL
12652  * <script type="text/javascript">
12653  */
12654
12655 /**
12656 * @class Roo.data.Record
12657  * Instances of this class encapsulate both record <em>definition</em> information, and record
12658  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12659  * to access Records cached in an {@link Roo.data.Store} object.<br>
12660  * <p>
12661  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12662  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12663  * objects.<br>
12664  * <p>
12665  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12666  * @constructor
12667  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12668  * {@link #create}. The parameters are the same.
12669  * @param {Array} data An associative Array of data values keyed by the field name.
12670  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12671  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12672  * not specified an integer id is generated.
12673  */
12674 Roo.data.Record = function(data, id){
12675     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12676     this.data = data;
12677 };
12678
12679 /**
12680  * Generate a constructor for a specific record layout.
12681  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12682  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12683  * Each field definition object may contain the following properties: <ul>
12684  * <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,
12685  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12686  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12687  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12688  * is being used, then this is a string containing the javascript expression to reference the data relative to 
12689  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12690  * to the data item relative to the record element. If the mapping expression is the same as the field name,
12691  * this may be omitted.</p></li>
12692  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12693  * <ul><li>auto (Default, implies no conversion)</li>
12694  * <li>string</li>
12695  * <li>int</li>
12696  * <li>float</li>
12697  * <li>boolean</li>
12698  * <li>date</li></ul></p></li>
12699  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12700  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12701  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12702  * by the Reader into an object that will be stored in the Record. It is passed the
12703  * following parameters:<ul>
12704  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12705  * </ul></p></li>
12706  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12707  * </ul>
12708  * <br>usage:<br><pre><code>
12709 var TopicRecord = Roo.data.Record.create(
12710     {name: 'title', mapping: 'topic_title'},
12711     {name: 'author', mapping: 'username'},
12712     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12713     {name: 'lastPost', mapping: 'post_time', type: 'date'},
12714     {name: 'lastPoster', mapping: 'user2'},
12715     {name: 'excerpt', mapping: 'post_text'}
12716 );
12717
12718 var myNewRecord = new TopicRecord({
12719     title: 'Do my job please',
12720     author: 'noobie',
12721     totalPosts: 1,
12722     lastPost: new Date(),
12723     lastPoster: 'Animal',
12724     excerpt: 'No way dude!'
12725 });
12726 myStore.add(myNewRecord);
12727 </code></pre>
12728  * @method create
12729  * @static
12730  */
12731 Roo.data.Record.create = function(o){
12732     var f = function(){
12733         f.superclass.constructor.apply(this, arguments);
12734     };
12735     Roo.extend(f, Roo.data.Record);
12736     var p = f.prototype;
12737     p.fields = new Roo.util.MixedCollection(false, function(field){
12738         return field.name;
12739     });
12740     for(var i = 0, len = o.length; i < len; i++){
12741         p.fields.add(new Roo.data.Field(o[i]));
12742     }
12743     f.getField = function(name){
12744         return p.fields.get(name);  
12745     };
12746     return f;
12747 };
12748
12749 Roo.data.Record.AUTO_ID = 1000;
12750 Roo.data.Record.EDIT = 'edit';
12751 Roo.data.Record.REJECT = 'reject';
12752 Roo.data.Record.COMMIT = 'commit';
12753
12754 Roo.data.Record.prototype = {
12755     /**
12756      * Readonly flag - true if this record has been modified.
12757      * @type Boolean
12758      */
12759     dirty : false,
12760     editing : false,
12761     error: null,
12762     modified: null,
12763
12764     // private
12765     join : function(store){
12766         this.store = store;
12767     },
12768
12769     /**
12770      * Set the named field to the specified value.
12771      * @param {String} name The name of the field to set.
12772      * @param {Object} value The value to set the field to.
12773      */
12774     set : function(name, value){
12775         if(this.data[name] == value){
12776             return;
12777         }
12778         this.dirty = true;
12779         if(!this.modified){
12780             this.modified = {};
12781         }
12782         if(typeof this.modified[name] == 'undefined'){
12783             this.modified[name] = this.data[name];
12784         }
12785         this.data[name] = value;
12786         if(!this.editing && this.store){
12787             this.store.afterEdit(this);
12788         }       
12789     },
12790
12791     /**
12792      * Get the value of the named field.
12793      * @param {String} name The name of the field to get the value of.
12794      * @return {Object} The value of the field.
12795      */
12796     get : function(name){
12797         return this.data[name]; 
12798     },
12799
12800     // private
12801     beginEdit : function(){
12802         this.editing = true;
12803         this.modified = {}; 
12804     },
12805
12806     // private
12807     cancelEdit : function(){
12808         this.editing = false;
12809         delete this.modified;
12810     },
12811
12812     // private
12813     endEdit : function(){
12814         this.editing = false;
12815         if(this.dirty && this.store){
12816             this.store.afterEdit(this);
12817         }
12818     },
12819
12820     /**
12821      * Usually called by the {@link Roo.data.Store} which owns the Record.
12822      * Rejects all changes made to the Record since either creation, or the last commit operation.
12823      * Modified fields are reverted to their original values.
12824      * <p>
12825      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
12826      * of reject operations.
12827      */
12828     reject : function(){
12829         var m = this.modified;
12830         for(var n in m){
12831             if(typeof m[n] != "function"){
12832                 this.data[n] = m[n];
12833             }
12834         }
12835         this.dirty = false;
12836         delete this.modified;
12837         this.editing = false;
12838         if(this.store){
12839             this.store.afterReject(this);
12840         }
12841     },
12842
12843     /**
12844      * Usually called by the {@link Roo.data.Store} which owns the Record.
12845      * Commits all changes made to the Record since either creation, or the last commit operation.
12846      * <p>
12847      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
12848      * of commit operations.
12849      */
12850     commit : function(){
12851         this.dirty = false;
12852         delete this.modified;
12853         this.editing = false;
12854         if(this.store){
12855             this.store.afterCommit(this);
12856         }
12857     },
12858
12859     // private
12860     hasError : function(){
12861         return this.error != null;
12862     },
12863
12864     // private
12865     clearError : function(){
12866         this.error = null;
12867     },
12868
12869     /**
12870      * Creates a copy of this record.
12871      * @param {String} id (optional) A new record id if you don't want to use this record's id
12872      * @return {Record}
12873      */
12874     copy : function(newId) {
12875         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
12876     }
12877 };/*
12878  * Based on:
12879  * Ext JS Library 1.1.1
12880  * Copyright(c) 2006-2007, Ext JS, LLC.
12881  *
12882  * Originally Released Under LGPL - original licence link has changed is not relivant.
12883  *
12884  * Fork - LGPL
12885  * <script type="text/javascript">
12886  */
12887
12888
12889
12890 /**
12891  * @class Roo.data.Store
12892  * @extends Roo.util.Observable
12893  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
12894  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
12895  * <p>
12896  * 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
12897  * has no knowledge of the format of the data returned by the Proxy.<br>
12898  * <p>
12899  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
12900  * instances from the data object. These records are cached and made available through accessor functions.
12901  * @constructor
12902  * Creates a new Store.
12903  * @param {Object} config A config object containing the objects needed for the Store to access data,
12904  * and read the data into Records.
12905  */
12906 Roo.data.Store = function(config){
12907     this.data = new Roo.util.MixedCollection(false);
12908     this.data.getKey = function(o){
12909         return o.id;
12910     };
12911     this.baseParams = {};
12912     // private
12913     this.paramNames = {
12914         "start" : "start",
12915         "limit" : "limit",
12916         "sort" : "sort",
12917         "dir" : "dir",
12918         "multisort" : "_multisort"
12919     };
12920
12921     if(config && config.data){
12922         this.inlineData = config.data;
12923         delete config.data;
12924     }
12925
12926     Roo.apply(this, config);
12927     
12928     if(this.reader){ // reader passed
12929         this.reader = Roo.factory(this.reader, Roo.data);
12930         this.reader.xmodule = this.xmodule || false;
12931         if(!this.recordType){
12932             this.recordType = this.reader.recordType;
12933         }
12934         if(this.reader.onMetaChange){
12935             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
12936         }
12937     }
12938
12939     if(this.recordType){
12940         this.fields = this.recordType.prototype.fields;
12941     }
12942     this.modified = [];
12943
12944     this.addEvents({
12945         /**
12946          * @event datachanged
12947          * Fires when the data cache has changed, and a widget which is using this Store
12948          * as a Record cache should refresh its view.
12949          * @param {Store} this
12950          */
12951         datachanged : true,
12952         /**
12953          * @event metachange
12954          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
12955          * @param {Store} this
12956          * @param {Object} meta The JSON metadata
12957          */
12958         metachange : true,
12959         /**
12960          * @event add
12961          * Fires when Records have been added to the Store
12962          * @param {Store} this
12963          * @param {Roo.data.Record[]} records The array of Records added
12964          * @param {Number} index The index at which the record(s) were added
12965          */
12966         add : true,
12967         /**
12968          * @event remove
12969          * Fires when a Record has been removed from the Store
12970          * @param {Store} this
12971          * @param {Roo.data.Record} record The Record that was removed
12972          * @param {Number} index The index at which the record was removed
12973          */
12974         remove : true,
12975         /**
12976          * @event update
12977          * Fires when a Record has been updated
12978          * @param {Store} this
12979          * @param {Roo.data.Record} record The Record that was updated
12980          * @param {String} operation The update operation being performed.  Value may be one of:
12981          * <pre><code>
12982  Roo.data.Record.EDIT
12983  Roo.data.Record.REJECT
12984  Roo.data.Record.COMMIT
12985          * </code></pre>
12986          */
12987         update : true,
12988         /**
12989          * @event clear
12990          * Fires when the data cache has been cleared.
12991          * @param {Store} this
12992          */
12993         clear : true,
12994         /**
12995          * @event beforeload
12996          * Fires before a request is made for a new data object.  If the beforeload handler returns false
12997          * the load action will be canceled.
12998          * @param {Store} this
12999          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13000          */
13001         beforeload : true,
13002         /**
13003          * @event beforeloadadd
13004          * Fires after a new set of Records has been loaded.
13005          * @param {Store} this
13006          * @param {Roo.data.Record[]} records The Records that were loaded
13007          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13008          */
13009         beforeloadadd : true,
13010         /**
13011          * @event load
13012          * Fires after a new set of Records has been loaded, before they are added to the store.
13013          * @param {Store} this
13014          * @param {Roo.data.Record[]} records The Records that were loaded
13015          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13016          * @params {Object} return from reader
13017          */
13018         load : true,
13019         /**
13020          * @event loadexception
13021          * Fires if an exception occurs in the Proxy during loading.
13022          * Called with the signature of the Proxy's "loadexception" event.
13023          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13024          * 
13025          * @param {Proxy} 
13026          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13027          * @param {Object} load options 
13028          * @param {Object} jsonData from your request (normally this contains the Exception)
13029          */
13030         loadexception : true
13031     });
13032     
13033     if(this.proxy){
13034         this.proxy = Roo.factory(this.proxy, Roo.data);
13035         this.proxy.xmodule = this.xmodule || false;
13036         this.relayEvents(this.proxy,  ["loadexception"]);
13037     }
13038     this.sortToggle = {};
13039     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13040
13041     Roo.data.Store.superclass.constructor.call(this);
13042
13043     if(this.inlineData){
13044         this.loadData(this.inlineData);
13045         delete this.inlineData;
13046     }
13047 };
13048
13049 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13050      /**
13051     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13052     * without a remote query - used by combo/forms at present.
13053     */
13054     
13055     /**
13056     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13057     */
13058     /**
13059     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13060     */
13061     /**
13062     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13063     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13064     */
13065     /**
13066     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13067     * on any HTTP request
13068     */
13069     /**
13070     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13071     */
13072     /**
13073     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13074     */
13075     multiSort: false,
13076     /**
13077     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13078     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13079     */
13080     remoteSort : false,
13081
13082     /**
13083     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13084      * loaded or when a record is removed. (defaults to false).
13085     */
13086     pruneModifiedRecords : false,
13087
13088     // private
13089     lastOptions : null,
13090
13091     /**
13092      * Add Records to the Store and fires the add event.
13093      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13094      */
13095     add : function(records){
13096         records = [].concat(records);
13097         for(var i = 0, len = records.length; i < len; i++){
13098             records[i].join(this);
13099         }
13100         var index = this.data.length;
13101         this.data.addAll(records);
13102         this.fireEvent("add", this, records, index);
13103     },
13104
13105     /**
13106      * Remove a Record from the Store and fires the remove event.
13107      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13108      */
13109     remove : function(record){
13110         var index = this.data.indexOf(record);
13111         this.data.removeAt(index);
13112  
13113         if(this.pruneModifiedRecords){
13114             this.modified.remove(record);
13115         }
13116         this.fireEvent("remove", this, record, index);
13117     },
13118
13119     /**
13120      * Remove all Records from the Store and fires the clear event.
13121      */
13122     removeAll : function(){
13123         this.data.clear();
13124         if(this.pruneModifiedRecords){
13125             this.modified = [];
13126         }
13127         this.fireEvent("clear", this);
13128     },
13129
13130     /**
13131      * Inserts Records to the Store at the given index and fires the add event.
13132      * @param {Number} index The start index at which to insert the passed Records.
13133      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13134      */
13135     insert : function(index, records){
13136         records = [].concat(records);
13137         for(var i = 0, len = records.length; i < len; i++){
13138             this.data.insert(index, records[i]);
13139             records[i].join(this);
13140         }
13141         this.fireEvent("add", this, records, index);
13142     },
13143
13144     /**
13145      * Get the index within the cache of the passed Record.
13146      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13147      * @return {Number} The index of the passed Record. Returns -1 if not found.
13148      */
13149     indexOf : function(record){
13150         return this.data.indexOf(record);
13151     },
13152
13153     /**
13154      * Get the index within the cache of the Record with the passed id.
13155      * @param {String} id The id of the Record to find.
13156      * @return {Number} The index of the Record. Returns -1 if not found.
13157      */
13158     indexOfId : function(id){
13159         return this.data.indexOfKey(id);
13160     },
13161
13162     /**
13163      * Get the Record with the specified id.
13164      * @param {String} id The id of the Record to find.
13165      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13166      */
13167     getById : function(id){
13168         return this.data.key(id);
13169     },
13170
13171     /**
13172      * Get the Record at the specified index.
13173      * @param {Number} index The index of the Record to find.
13174      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13175      */
13176     getAt : function(index){
13177         return this.data.itemAt(index);
13178     },
13179
13180     /**
13181      * Returns a range of Records between specified indices.
13182      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13183      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13184      * @return {Roo.data.Record[]} An array of Records
13185      */
13186     getRange : function(start, end){
13187         return this.data.getRange(start, end);
13188     },
13189
13190     // private
13191     storeOptions : function(o){
13192         o = Roo.apply({}, o);
13193         delete o.callback;
13194         delete o.scope;
13195         this.lastOptions = o;
13196     },
13197
13198     /**
13199      * Loads the Record cache from the configured Proxy using the configured Reader.
13200      * <p>
13201      * If using remote paging, then the first load call must specify the <em>start</em>
13202      * and <em>limit</em> properties in the options.params property to establish the initial
13203      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13204      * <p>
13205      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13206      * and this call will return before the new data has been loaded. Perform any post-processing
13207      * in a callback function, or in a "load" event handler.</strong>
13208      * <p>
13209      * @param {Object} options An object containing properties which control loading options:<ul>
13210      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13211      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13212      * passed the following arguments:<ul>
13213      * <li>r : Roo.data.Record[]</li>
13214      * <li>options: Options object from the load call</li>
13215      * <li>success: Boolean success indicator</li></ul></li>
13216      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13217      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13218      * </ul>
13219      */
13220     load : function(options){
13221         options = options || {};
13222         if(this.fireEvent("beforeload", this, options) !== false){
13223             this.storeOptions(options);
13224             var p = Roo.apply(options.params || {}, this.baseParams);
13225             // if meta was not loaded from remote source.. try requesting it.
13226             if (!this.reader.metaFromRemote) {
13227                 p._requestMeta = 1;
13228             }
13229             if(this.sortInfo && this.remoteSort){
13230                 var pn = this.paramNames;
13231                 p[pn["sort"]] = this.sortInfo.field;
13232                 p[pn["dir"]] = this.sortInfo.direction;
13233             }
13234             if (this.multiSort) {
13235                 var pn = this.paramNames;
13236                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13237             }
13238             
13239             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13240         }
13241     },
13242
13243     /**
13244      * Reloads the Record cache from the configured Proxy using the configured Reader and
13245      * the options from the last load operation performed.
13246      * @param {Object} options (optional) An object containing properties which may override the options
13247      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13248      * the most recently used options are reused).
13249      */
13250     reload : function(options){
13251         this.load(Roo.applyIf(options||{}, this.lastOptions));
13252     },
13253
13254     // private
13255     // Called as a callback by the Reader during a load operation.
13256     loadRecords : function(o, options, success){
13257         if(!o || success === false){
13258             if(success !== false){
13259                 this.fireEvent("load", this, [], options, o);
13260             }
13261             if(options.callback){
13262                 options.callback.call(options.scope || this, [], options, false);
13263             }
13264             return;
13265         }
13266         // if data returned failure - throw an exception.
13267         if (o.success === false) {
13268             // show a message if no listener is registered.
13269             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13270                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13271             }
13272             // loadmask wil be hooked into this..
13273             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13274             return;
13275         }
13276         var r = o.records, t = o.totalRecords || r.length;
13277         
13278         this.fireEvent("beforeloadadd", this, r, options, o);
13279         
13280         if(!options || options.add !== true){
13281             if(this.pruneModifiedRecords){
13282                 this.modified = [];
13283             }
13284             for(var i = 0, len = r.length; i < len; i++){
13285                 r[i].join(this);
13286             }
13287             if(this.snapshot){
13288                 this.data = this.snapshot;
13289                 delete this.snapshot;
13290             }
13291             this.data.clear();
13292             this.data.addAll(r);
13293             this.totalLength = t;
13294             this.applySort();
13295             this.fireEvent("datachanged", this);
13296         }else{
13297             this.totalLength = Math.max(t, this.data.length+r.length);
13298             this.add(r);
13299         }
13300         
13301         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13302                 
13303             var e = new Roo.data.Record({});
13304
13305             e.set(this.parent.displayField, this.parent.emptyTitle);
13306             e.set(this.parent.valueField, '');
13307
13308             this.insert(0, e);
13309         }
13310             
13311         this.fireEvent("load", this, r, options, o);
13312         if(options.callback){
13313             options.callback.call(options.scope || this, r, options, true);
13314         }
13315     },
13316
13317
13318     /**
13319      * Loads data from a passed data block. A Reader which understands the format of the data
13320      * must have been configured in the constructor.
13321      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13322      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13323      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13324      */
13325     loadData : function(o, append){
13326         var r = this.reader.readRecords(o);
13327         this.loadRecords(r, {add: append}, true);
13328     },
13329     
13330      /**
13331      * using 'cn' the nested child reader read the child array into it's child stores.
13332      * @param {Object} rec The record with a 'children array
13333      */
13334     loadDataFromChildren : function(rec)
13335     {
13336         this.loadData(this.reader.toLoadData(rec));
13337     },
13338     
13339
13340     /**
13341      * Gets the number of cached records.
13342      * <p>
13343      * <em>If using paging, this may not be the total size of the dataset. If the data object
13344      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13345      * the data set size</em>
13346      */
13347     getCount : function(){
13348         return this.data.length || 0;
13349     },
13350
13351     /**
13352      * Gets the total number of records in the dataset as returned by the server.
13353      * <p>
13354      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13355      * the dataset size</em>
13356      */
13357     getTotalCount : function(){
13358         return this.totalLength || 0;
13359     },
13360
13361     /**
13362      * Returns the sort state of the Store as an object with two properties:
13363      * <pre><code>
13364  field {String} The name of the field by which the Records are sorted
13365  direction {String} The sort order, "ASC" or "DESC"
13366      * </code></pre>
13367      */
13368     getSortState : function(){
13369         return this.sortInfo;
13370     },
13371
13372     // private
13373     applySort : function(){
13374         if(this.sortInfo && !this.remoteSort){
13375             var s = this.sortInfo, f = s.field;
13376             var st = this.fields.get(f).sortType;
13377             var fn = function(r1, r2){
13378                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13379                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13380             };
13381             this.data.sort(s.direction, fn);
13382             if(this.snapshot && this.snapshot != this.data){
13383                 this.snapshot.sort(s.direction, fn);
13384             }
13385         }
13386     },
13387
13388     /**
13389      * Sets the default sort column and order to be used by the next load operation.
13390      * @param {String} fieldName The name of the field to sort by.
13391      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13392      */
13393     setDefaultSort : function(field, dir){
13394         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13395     },
13396
13397     /**
13398      * Sort the Records.
13399      * If remote sorting is used, the sort is performed on the server, and the cache is
13400      * reloaded. If local sorting is used, the cache is sorted internally.
13401      * @param {String} fieldName The name of the field to sort by.
13402      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13403      */
13404     sort : function(fieldName, dir){
13405         var f = this.fields.get(fieldName);
13406         if(!dir){
13407             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13408             
13409             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13410                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13411             }else{
13412                 dir = f.sortDir;
13413             }
13414         }
13415         this.sortToggle[f.name] = dir;
13416         this.sortInfo = {field: f.name, direction: dir};
13417         if(!this.remoteSort){
13418             this.applySort();
13419             this.fireEvent("datachanged", this);
13420         }else{
13421             this.load(this.lastOptions);
13422         }
13423     },
13424
13425     /**
13426      * Calls the specified function for each of the Records in the cache.
13427      * @param {Function} fn The function to call. The Record is passed as the first parameter.
13428      * Returning <em>false</em> aborts and exits the iteration.
13429      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13430      */
13431     each : function(fn, scope){
13432         this.data.each(fn, scope);
13433     },
13434
13435     /**
13436      * Gets all records modified since the last commit.  Modified records are persisted across load operations
13437      * (e.g., during paging).
13438      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13439      */
13440     getModifiedRecords : function(){
13441         return this.modified;
13442     },
13443
13444     // private
13445     createFilterFn : function(property, value, anyMatch){
13446         if(!value.exec){ // not a regex
13447             value = String(value);
13448             if(value.length == 0){
13449                 return false;
13450             }
13451             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13452         }
13453         return function(r){
13454             return value.test(r.data[property]);
13455         };
13456     },
13457
13458     /**
13459      * Sums the value of <i>property</i> for each record between start and end and returns the result.
13460      * @param {String} property A field on your records
13461      * @param {Number} start The record index to start at (defaults to 0)
13462      * @param {Number} end The last record index to include (defaults to length - 1)
13463      * @return {Number} The sum
13464      */
13465     sum : function(property, start, end){
13466         var rs = this.data.items, v = 0;
13467         start = start || 0;
13468         end = (end || end === 0) ? end : rs.length-1;
13469
13470         for(var i = start; i <= end; i++){
13471             v += (rs[i].data[property] || 0);
13472         }
13473         return v;
13474     },
13475
13476     /**
13477      * Filter the records by a specified property.
13478      * @param {String} field A field on your records
13479      * @param {String/RegExp} value Either a string that the field
13480      * should start with or a RegExp to test against the field
13481      * @param {Boolean} anyMatch True to match any part not just the beginning
13482      */
13483     filter : function(property, value, anyMatch){
13484         var fn = this.createFilterFn(property, value, anyMatch);
13485         return fn ? this.filterBy(fn) : this.clearFilter();
13486     },
13487
13488     /**
13489      * Filter by a function. The specified function will be called with each
13490      * record in this data source. If the function returns true the record is included,
13491      * otherwise it is filtered.
13492      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13493      * @param {Object} scope (optional) The scope of the function (defaults to this)
13494      */
13495     filterBy : function(fn, scope){
13496         this.snapshot = this.snapshot || this.data;
13497         this.data = this.queryBy(fn, scope||this);
13498         this.fireEvent("datachanged", this);
13499     },
13500
13501     /**
13502      * Query the records by a specified property.
13503      * @param {String} field A field on your records
13504      * @param {String/RegExp} value Either a string that the field
13505      * should start with or a RegExp to test against the field
13506      * @param {Boolean} anyMatch True to match any part not just the beginning
13507      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13508      */
13509     query : function(property, value, anyMatch){
13510         var fn = this.createFilterFn(property, value, anyMatch);
13511         return fn ? this.queryBy(fn) : this.data.clone();
13512     },
13513
13514     /**
13515      * Query by a function. The specified function will be called with each
13516      * record in this data source. If the function returns true the record is included
13517      * in the results.
13518      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13519      * @param {Object} scope (optional) The scope of the function (defaults to this)
13520       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13521      **/
13522     queryBy : function(fn, scope){
13523         var data = this.snapshot || this.data;
13524         return data.filterBy(fn, scope||this);
13525     },
13526
13527     /**
13528      * Collects unique values for a particular dataIndex from this store.
13529      * @param {String} dataIndex The property to collect
13530      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13531      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13532      * @return {Array} An array of the unique values
13533      **/
13534     collect : function(dataIndex, allowNull, bypassFilter){
13535         var d = (bypassFilter === true && this.snapshot) ?
13536                 this.snapshot.items : this.data.items;
13537         var v, sv, r = [], l = {};
13538         for(var i = 0, len = d.length; i < len; i++){
13539             v = d[i].data[dataIndex];
13540             sv = String(v);
13541             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13542                 l[sv] = true;
13543                 r[r.length] = v;
13544             }
13545         }
13546         return r;
13547     },
13548
13549     /**
13550      * Revert to a view of the Record cache with no filtering applied.
13551      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13552      */
13553     clearFilter : function(suppressEvent){
13554         if(this.snapshot && this.snapshot != this.data){
13555             this.data = this.snapshot;
13556             delete this.snapshot;
13557             if(suppressEvent !== true){
13558                 this.fireEvent("datachanged", this);
13559             }
13560         }
13561     },
13562
13563     // private
13564     afterEdit : function(record){
13565         if(this.modified.indexOf(record) == -1){
13566             this.modified.push(record);
13567         }
13568         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13569     },
13570     
13571     // private
13572     afterReject : function(record){
13573         this.modified.remove(record);
13574         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13575     },
13576
13577     // private
13578     afterCommit : function(record){
13579         this.modified.remove(record);
13580         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13581     },
13582
13583     /**
13584      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13585      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13586      */
13587     commitChanges : function(){
13588         var m = this.modified.slice(0);
13589         this.modified = [];
13590         for(var i = 0, len = m.length; i < len; i++){
13591             m[i].commit();
13592         }
13593     },
13594
13595     /**
13596      * Cancel outstanding changes on all changed records.
13597      */
13598     rejectChanges : function(){
13599         var m = this.modified.slice(0);
13600         this.modified = [];
13601         for(var i = 0, len = m.length; i < len; i++){
13602             m[i].reject();
13603         }
13604     },
13605
13606     onMetaChange : function(meta, rtype, o){
13607         this.recordType = rtype;
13608         this.fields = rtype.prototype.fields;
13609         delete this.snapshot;
13610         this.sortInfo = meta.sortInfo || this.sortInfo;
13611         this.modified = [];
13612         this.fireEvent('metachange', this, this.reader.meta);
13613     },
13614     
13615     moveIndex : function(data, type)
13616     {
13617         var index = this.indexOf(data);
13618         
13619         var newIndex = index + type;
13620         
13621         this.remove(data);
13622         
13623         this.insert(newIndex, data);
13624         
13625     }
13626 });/*
13627  * Based on:
13628  * Ext JS Library 1.1.1
13629  * Copyright(c) 2006-2007, Ext JS, LLC.
13630  *
13631  * Originally Released Under LGPL - original licence link has changed is not relivant.
13632  *
13633  * Fork - LGPL
13634  * <script type="text/javascript">
13635  */
13636
13637 /**
13638  * @class Roo.data.SimpleStore
13639  * @extends Roo.data.Store
13640  * Small helper class to make creating Stores from Array data easier.
13641  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13642  * @cfg {Array} fields An array of field definition objects, or field name strings.
13643  * @cfg {Object} an existing reader (eg. copied from another store)
13644  * @cfg {Array} data The multi-dimensional array of data
13645  * @constructor
13646  * @param {Object} config
13647  */
13648 Roo.data.SimpleStore = function(config)
13649 {
13650     Roo.data.SimpleStore.superclass.constructor.call(this, {
13651         isLocal : true,
13652         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13653                 id: config.id
13654             },
13655             Roo.data.Record.create(config.fields)
13656         ),
13657         proxy : new Roo.data.MemoryProxy(config.data)
13658     });
13659     this.load();
13660 };
13661 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13662  * Based on:
13663  * Ext JS Library 1.1.1
13664  * Copyright(c) 2006-2007, Ext JS, LLC.
13665  *
13666  * Originally Released Under LGPL - original licence link has changed is not relivant.
13667  *
13668  * Fork - LGPL
13669  * <script type="text/javascript">
13670  */
13671
13672 /**
13673 /**
13674  * @extends Roo.data.Store
13675  * @class Roo.data.JsonStore
13676  * Small helper class to make creating Stores for JSON data easier. <br/>
13677 <pre><code>
13678 var store = new Roo.data.JsonStore({
13679     url: 'get-images.php',
13680     root: 'images',
13681     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13682 });
13683 </code></pre>
13684  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13685  * JsonReader and HttpProxy (unless inline data is provided).</b>
13686  * @cfg {Array} fields An array of field definition objects, or field name strings.
13687  * @constructor
13688  * @param {Object} config
13689  */
13690 Roo.data.JsonStore = function(c){
13691     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13692         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13693         reader: new Roo.data.JsonReader(c, c.fields)
13694     }));
13695 };
13696 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13697  * Based on:
13698  * Ext JS Library 1.1.1
13699  * Copyright(c) 2006-2007, Ext JS, LLC.
13700  *
13701  * Originally Released Under LGPL - original licence link has changed is not relivant.
13702  *
13703  * Fork - LGPL
13704  * <script type="text/javascript">
13705  */
13706
13707  
13708 Roo.data.Field = function(config){
13709     if(typeof config == "string"){
13710         config = {name: config};
13711     }
13712     Roo.apply(this, config);
13713     
13714     if(!this.type){
13715         this.type = "auto";
13716     }
13717     
13718     var st = Roo.data.SortTypes;
13719     // named sortTypes are supported, here we look them up
13720     if(typeof this.sortType == "string"){
13721         this.sortType = st[this.sortType];
13722     }
13723     
13724     // set default sortType for strings and dates
13725     if(!this.sortType){
13726         switch(this.type){
13727             case "string":
13728                 this.sortType = st.asUCString;
13729                 break;
13730             case "date":
13731                 this.sortType = st.asDate;
13732                 break;
13733             default:
13734                 this.sortType = st.none;
13735         }
13736     }
13737
13738     // define once
13739     var stripRe = /[\$,%]/g;
13740
13741     // prebuilt conversion function for this field, instead of
13742     // switching every time we're reading a value
13743     if(!this.convert){
13744         var cv, dateFormat = this.dateFormat;
13745         switch(this.type){
13746             case "":
13747             case "auto":
13748             case undefined:
13749                 cv = function(v){ return v; };
13750                 break;
13751             case "string":
13752                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
13753                 break;
13754             case "int":
13755                 cv = function(v){
13756                     return v !== undefined && v !== null && v !== '' ?
13757                            parseInt(String(v).replace(stripRe, ""), 10) : '';
13758                     };
13759                 break;
13760             case "float":
13761                 cv = function(v){
13762                     return v !== undefined && v !== null && v !== '' ?
13763                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
13764                     };
13765                 break;
13766             case "bool":
13767             case "boolean":
13768                 cv = function(v){ return v === true || v === "true" || v == 1; };
13769                 break;
13770             case "date":
13771                 cv = function(v){
13772                     if(!v){
13773                         return '';
13774                     }
13775                     if(v instanceof Date){
13776                         return v;
13777                     }
13778                     if(dateFormat){
13779                         if(dateFormat == "timestamp"){
13780                             return new Date(v*1000);
13781                         }
13782                         return Date.parseDate(v, dateFormat);
13783                     }
13784                     var parsed = Date.parse(v);
13785                     return parsed ? new Date(parsed) : null;
13786                 };
13787              break;
13788             
13789         }
13790         this.convert = cv;
13791     }
13792 };
13793
13794 Roo.data.Field.prototype = {
13795     dateFormat: null,
13796     defaultValue: "",
13797     mapping: null,
13798     sortType : null,
13799     sortDir : "ASC"
13800 };/*
13801  * Based on:
13802  * Ext JS Library 1.1.1
13803  * Copyright(c) 2006-2007, Ext JS, LLC.
13804  *
13805  * Originally Released Under LGPL - original licence link has changed is not relivant.
13806  *
13807  * Fork - LGPL
13808  * <script type="text/javascript">
13809  */
13810  
13811 // Base class for reading structured data from a data source.  This class is intended to be
13812 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
13813
13814 /**
13815  * @class Roo.data.DataReader
13816  * Base class for reading structured data from a data source.  This class is intended to be
13817  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
13818  */
13819
13820 Roo.data.DataReader = function(meta, recordType){
13821     
13822     this.meta = meta;
13823     
13824     this.recordType = recordType instanceof Array ? 
13825         Roo.data.Record.create(recordType) : recordType;
13826 };
13827
13828 Roo.data.DataReader.prototype = {
13829     
13830     
13831     readerType : 'Data',
13832      /**
13833      * Create an empty record
13834      * @param {Object} data (optional) - overlay some values
13835      * @return {Roo.data.Record} record created.
13836      */
13837     newRow :  function(d) {
13838         var da =  {};
13839         this.recordType.prototype.fields.each(function(c) {
13840             switch( c.type) {
13841                 case 'int' : da[c.name] = 0; break;
13842                 case 'date' : da[c.name] = new Date(); break;
13843                 case 'float' : da[c.name] = 0.0; break;
13844                 case 'boolean' : da[c.name] = false; break;
13845                 default : da[c.name] = ""; break;
13846             }
13847             
13848         });
13849         return new this.recordType(Roo.apply(da, d));
13850     }
13851     
13852     
13853 };/*
13854  * Based on:
13855  * Ext JS Library 1.1.1
13856  * Copyright(c) 2006-2007, Ext JS, LLC.
13857  *
13858  * Originally Released Under LGPL - original licence link has changed is not relivant.
13859  *
13860  * Fork - LGPL
13861  * <script type="text/javascript">
13862  */
13863
13864 /**
13865  * @class Roo.data.DataProxy
13866  * @extends Roo.data.Observable
13867  * This class is an abstract base class for implementations which provide retrieval of
13868  * unformatted data objects.<br>
13869  * <p>
13870  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
13871  * (of the appropriate type which knows how to parse the data object) to provide a block of
13872  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
13873  * <p>
13874  * Custom implementations must implement the load method as described in
13875  * {@link Roo.data.HttpProxy#load}.
13876  */
13877 Roo.data.DataProxy = function(){
13878     this.addEvents({
13879         /**
13880          * @event beforeload
13881          * Fires before a network request is made to retrieve a data object.
13882          * @param {Object} This DataProxy object.
13883          * @param {Object} params The params parameter to the load function.
13884          */
13885         beforeload : true,
13886         /**
13887          * @event load
13888          * Fires before the load method's callback is called.
13889          * @param {Object} This DataProxy object.
13890          * @param {Object} o The data object.
13891          * @param {Object} arg The callback argument object passed to the load function.
13892          */
13893         load : true,
13894         /**
13895          * @event loadexception
13896          * Fires if an Exception occurs during data retrieval.
13897          * @param {Object} This DataProxy object.
13898          * @param {Object} o The data object.
13899          * @param {Object} arg The callback argument object passed to the load function.
13900          * @param {Object} e The Exception.
13901          */
13902         loadexception : true
13903     });
13904     Roo.data.DataProxy.superclass.constructor.call(this);
13905 };
13906
13907 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
13908
13909     /**
13910      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
13911      */
13912 /*
13913  * Based on:
13914  * Ext JS Library 1.1.1
13915  * Copyright(c) 2006-2007, Ext JS, LLC.
13916  *
13917  * Originally Released Under LGPL - original licence link has changed is not relivant.
13918  *
13919  * Fork - LGPL
13920  * <script type="text/javascript">
13921  */
13922 /**
13923  * @class Roo.data.MemoryProxy
13924  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
13925  * to the Reader when its load method is called.
13926  * @constructor
13927  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
13928  */
13929 Roo.data.MemoryProxy = function(data){
13930     if (data.data) {
13931         data = data.data;
13932     }
13933     Roo.data.MemoryProxy.superclass.constructor.call(this);
13934     this.data = data;
13935 };
13936
13937 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
13938     
13939     /**
13940      * Load data from the requested source (in this case an in-memory
13941      * data object passed to the constructor), read the data object into
13942      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
13943      * process that block using the passed callback.
13944      * @param {Object} params This parameter is not used by the MemoryProxy class.
13945      * @param {Roo.data.DataReader} reader The Reader object which converts the data
13946      * object into a block of Roo.data.Records.
13947      * @param {Function} callback The function into which to pass the block of Roo.data.records.
13948      * The function must be passed <ul>
13949      * <li>The Record block object</li>
13950      * <li>The "arg" argument from the load function</li>
13951      * <li>A boolean success indicator</li>
13952      * </ul>
13953      * @param {Object} scope The scope in which to call the callback
13954      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
13955      */
13956     load : function(params, reader, callback, scope, arg){
13957         params = params || {};
13958         var result;
13959         try {
13960             result = reader.readRecords(params.data ? params.data :this.data);
13961         }catch(e){
13962             this.fireEvent("loadexception", this, arg, null, e);
13963             callback.call(scope, null, arg, false);
13964             return;
13965         }
13966         callback.call(scope, result, arg, true);
13967     },
13968     
13969     // private
13970     update : function(params, records){
13971         
13972     }
13973 });/*
13974  * Based on:
13975  * Ext JS Library 1.1.1
13976  * Copyright(c) 2006-2007, Ext JS, LLC.
13977  *
13978  * Originally Released Under LGPL - original licence link has changed is not relivant.
13979  *
13980  * Fork - LGPL
13981  * <script type="text/javascript">
13982  */
13983 /**
13984  * @class Roo.data.HttpProxy
13985  * @extends Roo.data.DataProxy
13986  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
13987  * configured to reference a certain URL.<br><br>
13988  * <p>
13989  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
13990  * from which the running page was served.<br><br>
13991  * <p>
13992  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
13993  * <p>
13994  * Be aware that to enable the browser to parse an XML document, the server must set
13995  * the Content-Type header in the HTTP response to "text/xml".
13996  * @constructor
13997  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
13998  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
13999  * will be used to make the request.
14000  */
14001 Roo.data.HttpProxy = function(conn){
14002     Roo.data.HttpProxy.superclass.constructor.call(this);
14003     // is conn a conn config or a real conn?
14004     this.conn = conn;
14005     this.useAjax = !conn || !conn.events;
14006   
14007 };
14008
14009 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14010     // thse are take from connection...
14011     
14012     /**
14013      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14014      */
14015     /**
14016      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14017      * extra parameters to each request made by this object. (defaults to undefined)
14018      */
14019     /**
14020      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14021      *  to each request made by this object. (defaults to undefined)
14022      */
14023     /**
14024      * @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)
14025      */
14026     /**
14027      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14028      */
14029      /**
14030      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14031      * @type Boolean
14032      */
14033   
14034
14035     /**
14036      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14037      * @type Boolean
14038      */
14039     /**
14040      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14041      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14042      * a finer-grained basis than the DataProxy events.
14043      */
14044     getConnection : function(){
14045         return this.useAjax ? Roo.Ajax : this.conn;
14046     },
14047
14048     /**
14049      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14050      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14051      * process that block using the passed callback.
14052      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14053      * for the request to the remote server.
14054      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14055      * object into a block of Roo.data.Records.
14056      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14057      * The function must be passed <ul>
14058      * <li>The Record block object</li>
14059      * <li>The "arg" argument from the load function</li>
14060      * <li>A boolean success indicator</li>
14061      * </ul>
14062      * @param {Object} scope The scope in which to call the callback
14063      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14064      */
14065     load : function(params, reader, callback, scope, arg){
14066         if(this.fireEvent("beforeload", this, params) !== false){
14067             var  o = {
14068                 params : params || {},
14069                 request: {
14070                     callback : callback,
14071                     scope : scope,
14072                     arg : arg
14073                 },
14074                 reader: reader,
14075                 callback : this.loadResponse,
14076                 scope: this
14077             };
14078             if(this.useAjax){
14079                 Roo.applyIf(o, this.conn);
14080                 if(this.activeRequest){
14081                     Roo.Ajax.abort(this.activeRequest);
14082                 }
14083                 this.activeRequest = Roo.Ajax.request(o);
14084             }else{
14085                 this.conn.request(o);
14086             }
14087         }else{
14088             callback.call(scope||this, null, arg, false);
14089         }
14090     },
14091
14092     // private
14093     loadResponse : function(o, success, response){
14094         delete this.activeRequest;
14095         if(!success){
14096             this.fireEvent("loadexception", this, o, response);
14097             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14098             return;
14099         }
14100         var result;
14101         try {
14102             result = o.reader.read(response);
14103         }catch(e){
14104             this.fireEvent("loadexception", this, o, response, e);
14105             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14106             return;
14107         }
14108         
14109         this.fireEvent("load", this, o, o.request.arg);
14110         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14111     },
14112
14113     // private
14114     update : function(dataSet){
14115
14116     },
14117
14118     // private
14119     updateResponse : function(dataSet){
14120
14121     }
14122 });/*
14123  * Based on:
14124  * Ext JS Library 1.1.1
14125  * Copyright(c) 2006-2007, Ext JS, LLC.
14126  *
14127  * Originally Released Under LGPL - original licence link has changed is not relivant.
14128  *
14129  * Fork - LGPL
14130  * <script type="text/javascript">
14131  */
14132
14133 /**
14134  * @class Roo.data.ScriptTagProxy
14135  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14136  * other than the originating domain of the running page.<br><br>
14137  * <p>
14138  * <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
14139  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14140  * <p>
14141  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14142  * source code that is used as the source inside a &lt;script> tag.<br><br>
14143  * <p>
14144  * In order for the browser to process the returned data, the server must wrap the data object
14145  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14146  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14147  * depending on whether the callback name was passed:
14148  * <p>
14149  * <pre><code>
14150 boolean scriptTag = false;
14151 String cb = request.getParameter("callback");
14152 if (cb != null) {
14153     scriptTag = true;
14154     response.setContentType("text/javascript");
14155 } else {
14156     response.setContentType("application/x-json");
14157 }
14158 Writer out = response.getWriter();
14159 if (scriptTag) {
14160     out.write(cb + "(");
14161 }
14162 out.print(dataBlock.toJsonString());
14163 if (scriptTag) {
14164     out.write(");");
14165 }
14166 </pre></code>
14167  *
14168  * @constructor
14169  * @param {Object} config A configuration object.
14170  */
14171 Roo.data.ScriptTagProxy = function(config){
14172     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14173     Roo.apply(this, config);
14174     this.head = document.getElementsByTagName("head")[0];
14175 };
14176
14177 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14178
14179 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14180     /**
14181      * @cfg {String} url The URL from which to request the data object.
14182      */
14183     /**
14184      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14185      */
14186     timeout : 30000,
14187     /**
14188      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14189      * the server the name of the callback function set up by the load call to process the returned data object.
14190      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14191      * javascript output which calls this named function passing the data object as its only parameter.
14192      */
14193     callbackParam : "callback",
14194     /**
14195      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14196      * name to the request.
14197      */
14198     nocache : true,
14199
14200     /**
14201      * Load data from the configured URL, read the data object into
14202      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14203      * process that block using the passed callback.
14204      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14205      * for the request to the remote server.
14206      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14207      * object into a block of Roo.data.Records.
14208      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14209      * The function must be passed <ul>
14210      * <li>The Record block object</li>
14211      * <li>The "arg" argument from the load function</li>
14212      * <li>A boolean success indicator</li>
14213      * </ul>
14214      * @param {Object} scope The scope in which to call the callback
14215      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14216      */
14217     load : function(params, reader, callback, scope, arg){
14218         if(this.fireEvent("beforeload", this, params) !== false){
14219
14220             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14221
14222             var url = this.url;
14223             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14224             if(this.nocache){
14225                 url += "&_dc=" + (new Date().getTime());
14226             }
14227             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14228             var trans = {
14229                 id : transId,
14230                 cb : "stcCallback"+transId,
14231                 scriptId : "stcScript"+transId,
14232                 params : params,
14233                 arg : arg,
14234                 url : url,
14235                 callback : callback,
14236                 scope : scope,
14237                 reader : reader
14238             };
14239             var conn = this;
14240
14241             window[trans.cb] = function(o){
14242                 conn.handleResponse(o, trans);
14243             };
14244
14245             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14246
14247             if(this.autoAbort !== false){
14248                 this.abort();
14249             }
14250
14251             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14252
14253             var script = document.createElement("script");
14254             script.setAttribute("src", url);
14255             script.setAttribute("type", "text/javascript");
14256             script.setAttribute("id", trans.scriptId);
14257             this.head.appendChild(script);
14258
14259             this.trans = trans;
14260         }else{
14261             callback.call(scope||this, null, arg, false);
14262         }
14263     },
14264
14265     // private
14266     isLoading : function(){
14267         return this.trans ? true : false;
14268     },
14269
14270     /**
14271      * Abort the current server request.
14272      */
14273     abort : function(){
14274         if(this.isLoading()){
14275             this.destroyTrans(this.trans);
14276         }
14277     },
14278
14279     // private
14280     destroyTrans : function(trans, isLoaded){
14281         this.head.removeChild(document.getElementById(trans.scriptId));
14282         clearTimeout(trans.timeoutId);
14283         if(isLoaded){
14284             window[trans.cb] = undefined;
14285             try{
14286                 delete window[trans.cb];
14287             }catch(e){}
14288         }else{
14289             // if hasn't been loaded, wait for load to remove it to prevent script error
14290             window[trans.cb] = function(){
14291                 window[trans.cb] = undefined;
14292                 try{
14293                     delete window[trans.cb];
14294                 }catch(e){}
14295             };
14296         }
14297     },
14298
14299     // private
14300     handleResponse : function(o, trans){
14301         this.trans = false;
14302         this.destroyTrans(trans, true);
14303         var result;
14304         try {
14305             result = trans.reader.readRecords(o);
14306         }catch(e){
14307             this.fireEvent("loadexception", this, o, trans.arg, e);
14308             trans.callback.call(trans.scope||window, null, trans.arg, false);
14309             return;
14310         }
14311         this.fireEvent("load", this, o, trans.arg);
14312         trans.callback.call(trans.scope||window, result, trans.arg, true);
14313     },
14314
14315     // private
14316     handleFailure : function(trans){
14317         this.trans = false;
14318         this.destroyTrans(trans, false);
14319         this.fireEvent("loadexception", this, null, trans.arg);
14320         trans.callback.call(trans.scope||window, null, trans.arg, false);
14321     }
14322 });/*
14323  * Based on:
14324  * Ext JS Library 1.1.1
14325  * Copyright(c) 2006-2007, Ext JS, LLC.
14326  *
14327  * Originally Released Under LGPL - original licence link has changed is not relivant.
14328  *
14329  * Fork - LGPL
14330  * <script type="text/javascript">
14331  */
14332
14333 /**
14334  * @class Roo.data.JsonReader
14335  * @extends Roo.data.DataReader
14336  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14337  * based on mappings in a provided Roo.data.Record constructor.
14338  * 
14339  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14340  * in the reply previously. 
14341  * 
14342  * <p>
14343  * Example code:
14344  * <pre><code>
14345 var RecordDef = Roo.data.Record.create([
14346     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14347     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14348 ]);
14349 var myReader = new Roo.data.JsonReader({
14350     totalProperty: "results",    // The property which contains the total dataset size (optional)
14351     root: "rows",                // The property which contains an Array of row objects
14352     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14353 }, RecordDef);
14354 </code></pre>
14355  * <p>
14356  * This would consume a JSON file like this:
14357  * <pre><code>
14358 { 'results': 2, 'rows': [
14359     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14360     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14361 }
14362 </code></pre>
14363  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14364  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14365  * paged from the remote server.
14366  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14367  * @cfg {String} root name of the property which contains the Array of row objects.
14368  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14369  * @cfg {Array} fields Array of field definition objects
14370  * @constructor
14371  * Create a new JsonReader
14372  * @param {Object} meta Metadata configuration options
14373  * @param {Object} recordType Either an Array of field definition objects,
14374  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14375  */
14376 Roo.data.JsonReader = function(meta, recordType){
14377     
14378     meta = meta || {};
14379     // set some defaults:
14380     Roo.applyIf(meta, {
14381         totalProperty: 'total',
14382         successProperty : 'success',
14383         root : 'data',
14384         id : 'id'
14385     });
14386     
14387     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14388 };
14389 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14390     
14391     readerType : 'Json',
14392     
14393     /**
14394      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
14395      * Used by Store query builder to append _requestMeta to params.
14396      * 
14397      */
14398     metaFromRemote : false,
14399     /**
14400      * This method is only used by a DataProxy which has retrieved data from a remote server.
14401      * @param {Object} response The XHR object which contains the JSON data in its responseText.
14402      * @return {Object} data A data block which is used by an Roo.data.Store object as
14403      * a cache of Roo.data.Records.
14404      */
14405     read : function(response){
14406         var json = response.responseText;
14407        
14408         var o = /* eval:var:o */ eval("("+json+")");
14409         if(!o) {
14410             throw {message: "JsonReader.read: Json object not found"};
14411         }
14412         
14413         if(o.metaData){
14414             
14415             delete this.ef;
14416             this.metaFromRemote = true;
14417             this.meta = o.metaData;
14418             this.recordType = Roo.data.Record.create(o.metaData.fields);
14419             this.onMetaChange(this.meta, this.recordType, o);
14420         }
14421         return this.readRecords(o);
14422     },
14423
14424     // private function a store will implement
14425     onMetaChange : function(meta, recordType, o){
14426
14427     },
14428
14429     /**
14430          * @ignore
14431          */
14432     simpleAccess: function(obj, subsc) {
14433         return obj[subsc];
14434     },
14435
14436         /**
14437          * @ignore
14438          */
14439     getJsonAccessor: function(){
14440         var re = /[\[\.]/;
14441         return function(expr) {
14442             try {
14443                 return(re.test(expr))
14444                     ? new Function("obj", "return obj." + expr)
14445                     : function(obj){
14446                         return obj[expr];
14447                     };
14448             } catch(e){}
14449             return Roo.emptyFn;
14450         };
14451     }(),
14452
14453     /**
14454      * Create a data block containing Roo.data.Records from an XML document.
14455      * @param {Object} o An object which contains an Array of row objects in the property specified
14456      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14457      * which contains the total size of the dataset.
14458      * @return {Object} data A data block which is used by an Roo.data.Store object as
14459      * a cache of Roo.data.Records.
14460      */
14461     readRecords : function(o){
14462         /**
14463          * After any data loads, the raw JSON data is available for further custom processing.
14464          * @type Object
14465          */
14466         this.o = o;
14467         var s = this.meta, Record = this.recordType,
14468             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14469
14470 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
14471         if (!this.ef) {
14472             if(s.totalProperty) {
14473                     this.getTotal = this.getJsonAccessor(s.totalProperty);
14474                 }
14475                 if(s.successProperty) {
14476                     this.getSuccess = this.getJsonAccessor(s.successProperty);
14477                 }
14478                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14479                 if (s.id) {
14480                         var g = this.getJsonAccessor(s.id);
14481                         this.getId = function(rec) {
14482                                 var r = g(rec);  
14483                                 return (r === undefined || r === "") ? null : r;
14484                         };
14485                 } else {
14486                         this.getId = function(){return null;};
14487                 }
14488             this.ef = [];
14489             for(var jj = 0; jj < fl; jj++){
14490                 f = fi[jj];
14491                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14492                 this.ef[jj] = this.getJsonAccessor(map);
14493             }
14494         }
14495
14496         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14497         if(s.totalProperty){
14498             var vt = parseInt(this.getTotal(o), 10);
14499             if(!isNaN(vt)){
14500                 totalRecords = vt;
14501             }
14502         }
14503         if(s.successProperty){
14504             var vs = this.getSuccess(o);
14505             if(vs === false || vs === 'false'){
14506                 success = false;
14507             }
14508         }
14509         var records = [];
14510         for(var i = 0; i < c; i++){
14511                 var n = root[i];
14512             var values = {};
14513             var id = this.getId(n);
14514             for(var j = 0; j < fl; j++){
14515                 f = fi[j];
14516             var v = this.ef[j](n);
14517             if (!f.convert) {
14518                 Roo.log('missing convert for ' + f.name);
14519                 Roo.log(f);
14520                 continue;
14521             }
14522             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14523             }
14524             var record = new Record(values, id);
14525             record.json = n;
14526             records[i] = record;
14527         }
14528         return {
14529             raw : o,
14530             success : success,
14531             records : records,
14532             totalRecords : totalRecords
14533         };
14534     },
14535     // used when loading children.. @see loadDataFromChildren
14536     toLoadData: function(rec)
14537     {
14538         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14539         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14540         return { data : data, total : data.length };
14541         
14542     }
14543 });/*
14544  * Based on:
14545  * Ext JS Library 1.1.1
14546  * Copyright(c) 2006-2007, Ext JS, LLC.
14547  *
14548  * Originally Released Under LGPL - original licence link has changed is not relivant.
14549  *
14550  * Fork - LGPL
14551  * <script type="text/javascript">
14552  */
14553
14554 /**
14555  * @class Roo.data.ArrayReader
14556  * @extends Roo.data.DataReader
14557  * Data reader class to create an Array of Roo.data.Record objects from an Array.
14558  * Each element of that Array represents a row of data fields. The
14559  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14560  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14561  * <p>
14562  * Example code:.
14563  * <pre><code>
14564 var RecordDef = Roo.data.Record.create([
14565     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
14566     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
14567 ]);
14568 var myReader = new Roo.data.ArrayReader({
14569     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
14570 }, RecordDef);
14571 </code></pre>
14572  * <p>
14573  * This would consume an Array like this:
14574  * <pre><code>
14575 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14576   </code></pre>
14577  
14578  * @constructor
14579  * Create a new JsonReader
14580  * @param {Object} meta Metadata configuration options.
14581  * @param {Object|Array} recordType Either an Array of field definition objects
14582  * 
14583  * @cfg {Array} fields Array of field definition objects
14584  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14585  * as specified to {@link Roo.data.Record#create},
14586  * or an {@link Roo.data.Record} object
14587  *
14588  * 
14589  * created using {@link Roo.data.Record#create}.
14590  */
14591 Roo.data.ArrayReader = function(meta, recordType)
14592 {    
14593     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14594 };
14595
14596 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14597     
14598       /**
14599      * Create a data block containing Roo.data.Records from an XML document.
14600      * @param {Object} o An Array of row objects which represents the dataset.
14601      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14602      * a cache of Roo.data.Records.
14603      */
14604     readRecords : function(o)
14605     {
14606         var sid = this.meta ? this.meta.id : null;
14607         var recordType = this.recordType, fields = recordType.prototype.fields;
14608         var records = [];
14609         var root = o;
14610         for(var i = 0; i < root.length; i++){
14611                 var n = root[i];
14612             var values = {};
14613             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14614             for(var j = 0, jlen = fields.length; j < jlen; j++){
14615                 var f = fields.items[j];
14616                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14617                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14618                 v = f.convert(v);
14619                 values[f.name] = v;
14620             }
14621             var record = new recordType(values, id);
14622             record.json = n;
14623             records[records.length] = record;
14624         }
14625         return {
14626             records : records,
14627             totalRecords : records.length
14628         };
14629     },
14630     // used when loading children.. @see loadDataFromChildren
14631     toLoadData: function(rec)
14632     {
14633         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14634         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14635         
14636     }
14637     
14638     
14639 });/*
14640  * - LGPL
14641  * * 
14642  */
14643
14644 /**
14645  * @class Roo.bootstrap.ComboBox
14646  * @extends Roo.bootstrap.TriggerField
14647  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14648  * @cfg {Boolean} append (true|false) default false
14649  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14650  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14651  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14652  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14653  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14654  * @cfg {Boolean} animate default true
14655  * @cfg {Boolean} emptyResultText only for touch device
14656  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14657  * @cfg {String} emptyTitle default ''
14658  * @constructor
14659  * Create a new ComboBox.
14660  * @param {Object} config Configuration options
14661  */
14662 Roo.bootstrap.ComboBox = function(config){
14663     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14664     this.addEvents({
14665         /**
14666          * @event expand
14667          * Fires when the dropdown list is expanded
14668         * @param {Roo.bootstrap.ComboBox} combo This combo box
14669         */
14670         'expand' : true,
14671         /**
14672          * @event collapse
14673          * Fires when the dropdown list is collapsed
14674         * @param {Roo.bootstrap.ComboBox} combo This combo box
14675         */
14676         'collapse' : true,
14677         /**
14678          * @event beforeselect
14679          * Fires before a list item is selected. Return false to cancel the selection.
14680         * @param {Roo.bootstrap.ComboBox} combo This combo box
14681         * @param {Roo.data.Record} record The data record returned from the underlying store
14682         * @param {Number} index The index of the selected item in the dropdown list
14683         */
14684         'beforeselect' : true,
14685         /**
14686          * @event select
14687          * Fires when a list item is selected
14688         * @param {Roo.bootstrap.ComboBox} combo This combo box
14689         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14690         * @param {Number} index The index of the selected item in the dropdown list
14691         */
14692         'select' : true,
14693         /**
14694          * @event beforequery
14695          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14696          * The event object passed has these properties:
14697         * @param {Roo.bootstrap.ComboBox} combo This combo box
14698         * @param {String} query The query
14699         * @param {Boolean} forceAll true to force "all" query
14700         * @param {Boolean} cancel true to cancel the query
14701         * @param {Object} e The query event object
14702         */
14703         'beforequery': true,
14704          /**
14705          * @event add
14706          * Fires when the 'add' icon is pressed (add a listener to enable add button)
14707         * @param {Roo.bootstrap.ComboBox} combo This combo box
14708         */
14709         'add' : true,
14710         /**
14711          * @event edit
14712          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14713         * @param {Roo.bootstrap.ComboBox} combo This combo box
14714         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14715         */
14716         'edit' : true,
14717         /**
14718          * @event remove
14719          * Fires when the remove value from the combobox array
14720         * @param {Roo.bootstrap.ComboBox} combo This combo box
14721         */
14722         'remove' : true,
14723         /**
14724          * @event afterremove
14725          * Fires when the remove value from the combobox array
14726         * @param {Roo.bootstrap.ComboBox} combo This combo box
14727         */
14728         'afterremove' : true,
14729         /**
14730          * @event specialfilter
14731          * Fires when specialfilter
14732             * @param {Roo.bootstrap.ComboBox} combo This combo box
14733             */
14734         'specialfilter' : true,
14735         /**
14736          * @event tick
14737          * Fires when tick the element
14738             * @param {Roo.bootstrap.ComboBox} combo This combo box
14739             */
14740         'tick' : true,
14741         /**
14742          * @event touchviewdisplay
14743          * Fires when touch view require special display (default is using displayField)
14744             * @param {Roo.bootstrap.ComboBox} combo This combo box
14745             * @param {Object} cfg set html .
14746             */
14747         'touchviewdisplay' : true
14748         
14749     });
14750     
14751     this.item = [];
14752     this.tickItems = [];
14753     
14754     this.selectedIndex = -1;
14755     if(this.mode == 'local'){
14756         if(config.queryDelay === undefined){
14757             this.queryDelay = 10;
14758         }
14759         if(config.minChars === undefined){
14760             this.minChars = 0;
14761         }
14762     }
14763 };
14764
14765 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
14766      
14767     /**
14768      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
14769      * rendering into an Roo.Editor, defaults to false)
14770      */
14771     /**
14772      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
14773      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
14774      */
14775     /**
14776      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
14777      */
14778     /**
14779      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
14780      * the dropdown list (defaults to undefined, with no header element)
14781      */
14782
14783      /**
14784      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
14785      */
14786      
14787      /**
14788      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
14789      */
14790     listWidth: undefined,
14791     /**
14792      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
14793      * mode = 'remote' or 'text' if mode = 'local')
14794      */
14795     displayField: undefined,
14796     
14797     /**
14798      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
14799      * mode = 'remote' or 'value' if mode = 'local'). 
14800      * Note: use of a valueField requires the user make a selection
14801      * in order for a value to be mapped.
14802      */
14803     valueField: undefined,
14804     /**
14805      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
14806      */
14807     modalTitle : '',
14808     
14809     /**
14810      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
14811      * field's data value (defaults to the underlying DOM element's name)
14812      */
14813     hiddenName: undefined,
14814     /**
14815      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
14816      */
14817     listClass: '',
14818     /**
14819      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
14820      */
14821     selectedClass: 'active',
14822     
14823     /**
14824      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14825      */
14826     shadow:'sides',
14827     /**
14828      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
14829      * anchor positions (defaults to 'tl-bl')
14830      */
14831     listAlign: 'tl-bl?',
14832     /**
14833      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
14834      */
14835     maxHeight: 300,
14836     /**
14837      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
14838      * query specified by the allQuery config option (defaults to 'query')
14839      */
14840     triggerAction: 'query',
14841     /**
14842      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
14843      * (defaults to 4, does not apply if editable = false)
14844      */
14845     minChars : 4,
14846     /**
14847      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
14848      * delay (typeAheadDelay) if it matches a known value (defaults to false)
14849      */
14850     typeAhead: false,
14851     /**
14852      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
14853      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
14854      */
14855     queryDelay: 500,
14856     /**
14857      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
14858      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
14859      */
14860     pageSize: 0,
14861     /**
14862      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
14863      * when editable = true (defaults to false)
14864      */
14865     selectOnFocus:false,
14866     /**
14867      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
14868      */
14869     queryParam: 'query',
14870     /**
14871      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
14872      * when mode = 'remote' (defaults to 'Loading...')
14873      */
14874     loadingText: 'Loading...',
14875     /**
14876      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
14877      */
14878     resizable: false,
14879     /**
14880      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
14881      */
14882     handleHeight : 8,
14883     /**
14884      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
14885      * traditional select (defaults to true)
14886      */
14887     editable: true,
14888     /**
14889      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
14890      */
14891     allQuery: '',
14892     /**
14893      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
14894      */
14895     mode: 'remote',
14896     /**
14897      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
14898      * listWidth has a higher value)
14899      */
14900     minListWidth : 70,
14901     /**
14902      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
14903      * allow the user to set arbitrary text into the field (defaults to false)
14904      */
14905     forceSelection:false,
14906     /**
14907      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
14908      * if typeAhead = true (defaults to 250)
14909      */
14910     typeAheadDelay : 250,
14911     /**
14912      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
14913      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
14914      */
14915     valueNotFoundText : undefined,
14916     /**
14917      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
14918      */
14919     blockFocus : false,
14920     
14921     /**
14922      * @cfg {Boolean} disableClear Disable showing of clear button.
14923      */
14924     disableClear : false,
14925     /**
14926      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
14927      */
14928     alwaysQuery : false,
14929     
14930     /**
14931      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
14932      */
14933     multiple : false,
14934     
14935     /**
14936      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
14937      */
14938     invalidClass : "has-warning",
14939     
14940     /**
14941      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
14942      */
14943     validClass : "has-success",
14944     
14945     /**
14946      * @cfg {Boolean} specialFilter (true|false) special filter default false
14947      */
14948     specialFilter : false,
14949     
14950     /**
14951      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
14952      */
14953     mobileTouchView : true,
14954     
14955     /**
14956      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
14957      */
14958     useNativeIOS : false,
14959     
14960     /**
14961      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
14962      */
14963     mobile_restrict_height : false,
14964     
14965     ios_options : false,
14966     
14967     //private
14968     addicon : false,
14969     editicon: false,
14970     
14971     page: 0,
14972     hasQuery: false,
14973     append: false,
14974     loadNext: false,
14975     autoFocus : true,
14976     tickable : false,
14977     btnPosition : 'right',
14978     triggerList : true,
14979     showToggleBtn : true,
14980     animate : true,
14981     emptyResultText: 'Empty',
14982     triggerText : 'Select',
14983     emptyTitle : '',
14984     
14985     // element that contains real text value.. (when hidden is used..)
14986     
14987     getAutoCreate : function()
14988     {   
14989         var cfg = false;
14990         //render
14991         /*
14992          * Render classic select for iso
14993          */
14994         
14995         if(Roo.isIOS && this.useNativeIOS){
14996             cfg = this.getAutoCreateNativeIOS();
14997             return cfg;
14998         }
14999         
15000         /*
15001          * Touch Devices
15002          */
15003         
15004         if(Roo.isTouch && this.mobileTouchView){
15005             cfg = this.getAutoCreateTouchView();
15006             return cfg;;
15007         }
15008         
15009         /*
15010          *  Normal ComboBox
15011          */
15012         if(!this.tickable){
15013             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15014             return cfg;
15015         }
15016         
15017         /*
15018          *  ComboBox with tickable selections
15019          */
15020              
15021         var align = this.labelAlign || this.parentLabelAlign();
15022         
15023         cfg = {
15024             cls : 'form-group roo-combobox-tickable' //input-group
15025         };
15026         
15027         var btn_text_select = '';
15028         var btn_text_done = '';
15029         var btn_text_cancel = '';
15030         
15031         if (this.btn_text_show) {
15032             btn_text_select = 'Select';
15033             btn_text_done = 'Done';
15034             btn_text_cancel = 'Cancel'; 
15035         }
15036         
15037         var buttons = {
15038             tag : 'div',
15039             cls : 'tickable-buttons',
15040             cn : [
15041                 {
15042                     tag : 'button',
15043                     type : 'button',
15044                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15045                     //html : this.triggerText
15046                     html: btn_text_select
15047                 },
15048                 {
15049                     tag : 'button',
15050                     type : 'button',
15051                     name : 'ok',
15052                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15053                     //html : 'Done'
15054                     html: btn_text_done
15055                 },
15056                 {
15057                     tag : 'button',
15058                     type : 'button',
15059                     name : 'cancel',
15060                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15061                     //html : 'Cancel'
15062                     html: btn_text_cancel
15063                 }
15064             ]
15065         };
15066         
15067         if(this.editable){
15068             buttons.cn.unshift({
15069                 tag: 'input',
15070                 cls: 'roo-select2-search-field-input'
15071             });
15072         }
15073         
15074         var _this = this;
15075         
15076         Roo.each(buttons.cn, function(c){
15077             if (_this.size) {
15078                 c.cls += ' btn-' + _this.size;
15079             }
15080
15081             if (_this.disabled) {
15082                 c.disabled = true;
15083             }
15084         });
15085         
15086         var box = {
15087             tag: 'div',
15088             style : 'display: contents',
15089             cn: [
15090                 {
15091                     tag: 'input',
15092                     type : 'hidden',
15093                     cls: 'form-hidden-field'
15094                 },
15095                 {
15096                     tag: 'ul',
15097                     cls: 'roo-select2-choices',
15098                     cn:[
15099                         {
15100                             tag: 'li',
15101                             cls: 'roo-select2-search-field',
15102                             cn: [
15103                                 buttons
15104                             ]
15105                         }
15106                     ]
15107                 }
15108             ]
15109         };
15110         
15111         var combobox = {
15112             cls: 'roo-select2-container input-group roo-select2-container-multi',
15113             cn: [
15114                 
15115                 box
15116 //                {
15117 //                    tag: 'ul',
15118 //                    cls: 'typeahead typeahead-long dropdown-menu',
15119 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15120 //                }
15121             ]
15122         };
15123         
15124         if(this.hasFeedback && !this.allowBlank){
15125             
15126             var feedback = {
15127                 tag: 'span',
15128                 cls: 'glyphicon form-control-feedback'
15129             };
15130
15131             combobox.cn.push(feedback);
15132         }
15133         
15134         
15135         
15136         var indicator = {
15137             tag : 'i',
15138             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15139             tooltip : 'This field is required'
15140         };
15141         if (Roo.bootstrap.version == 4) {
15142             indicator = {
15143                 tag : 'i',
15144                 style : 'display:none'
15145             };
15146         }
15147         if (align ==='left' && this.fieldLabel.length) {
15148             
15149             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15150             
15151             cfg.cn = [
15152                 indicator,
15153                 {
15154                     tag: 'label',
15155                     'for' :  id,
15156                     cls : 'control-label col-form-label',
15157                     html : this.fieldLabel
15158
15159                 },
15160                 {
15161                     cls : "", 
15162                     cn: [
15163                         combobox
15164                     ]
15165                 }
15166
15167             ];
15168             
15169             var labelCfg = cfg.cn[1];
15170             var contentCfg = cfg.cn[2];
15171             
15172
15173             if(this.indicatorpos == 'right'){
15174                 
15175                 cfg.cn = [
15176                     {
15177                         tag: 'label',
15178                         'for' :  id,
15179                         cls : 'control-label col-form-label',
15180                         cn : [
15181                             {
15182                                 tag : 'span',
15183                                 html : this.fieldLabel
15184                             },
15185                             indicator
15186                         ]
15187                     },
15188                     {
15189                         cls : "",
15190                         cn: [
15191                             combobox
15192                         ]
15193                     }
15194
15195                 ];
15196                 
15197                 
15198                 
15199                 labelCfg = cfg.cn[0];
15200                 contentCfg = cfg.cn[1];
15201             
15202             }
15203             
15204             if(this.labelWidth > 12){
15205                 labelCfg.style = "width: " + this.labelWidth + 'px';
15206             }
15207             
15208             if(this.labelWidth < 13 && this.labelmd == 0){
15209                 this.labelmd = this.labelWidth;
15210             }
15211             
15212             if(this.labellg > 0){
15213                 labelCfg.cls += ' col-lg-' + this.labellg;
15214                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15215             }
15216             
15217             if(this.labelmd > 0){
15218                 labelCfg.cls += ' col-md-' + this.labelmd;
15219                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15220             }
15221             
15222             if(this.labelsm > 0){
15223                 labelCfg.cls += ' col-sm-' + this.labelsm;
15224                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15225             }
15226             
15227             if(this.labelxs > 0){
15228                 labelCfg.cls += ' col-xs-' + this.labelxs;
15229                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15230             }
15231                 
15232                 
15233         } else if ( this.fieldLabel.length) {
15234 //                Roo.log(" label");
15235                  cfg.cn = [
15236                    indicator,
15237                     {
15238                         tag: 'label',
15239                         //cls : 'input-group-addon',
15240                         html : this.fieldLabel
15241                     },
15242                     combobox
15243                 ];
15244                 
15245                 if(this.indicatorpos == 'right'){
15246                     cfg.cn = [
15247                         {
15248                             tag: 'label',
15249                             //cls : 'input-group-addon',
15250                             html : this.fieldLabel
15251                         },
15252                         indicator,
15253                         combobox
15254                     ];
15255                     
15256                 }
15257
15258         } else {
15259             
15260 //                Roo.log(" no label && no align");
15261                 cfg = combobox
15262                      
15263                 
15264         }
15265          
15266         var settings=this;
15267         ['xs','sm','md','lg'].map(function(size){
15268             if (settings[size]) {
15269                 cfg.cls += ' col-' + size + '-' + settings[size];
15270             }
15271         });
15272         
15273         return cfg;
15274         
15275     },
15276     
15277     _initEventsCalled : false,
15278     
15279     // private
15280     initEvents: function()
15281     {   
15282         if (this._initEventsCalled) { // as we call render... prevent looping...
15283             return;
15284         }
15285         this._initEventsCalled = true;
15286         
15287         if (!this.store) {
15288             throw "can not find store for combo";
15289         }
15290         
15291         this.indicator = this.indicatorEl();
15292         
15293         this.store = Roo.factory(this.store, Roo.data);
15294         this.store.parent = this;
15295         
15296         // if we are building from html. then this element is so complex, that we can not really
15297         // use the rendered HTML.
15298         // so we have to trash and replace the previous code.
15299         if (Roo.XComponent.build_from_html) {
15300             // remove this element....
15301             var e = this.el.dom, k=0;
15302             while (e ) { e = e.previousSibling;  ++k;}
15303
15304             this.el.remove();
15305             
15306             this.el=false;
15307             this.rendered = false;
15308             
15309             this.render(this.parent().getChildContainer(true), k);
15310         }
15311         
15312         if(Roo.isIOS && this.useNativeIOS){
15313             this.initIOSView();
15314             return;
15315         }
15316         
15317         /*
15318          * Touch Devices
15319          */
15320         
15321         if(Roo.isTouch && this.mobileTouchView){
15322             this.initTouchView();
15323             return;
15324         }
15325         
15326         if(this.tickable){
15327             this.initTickableEvents();
15328             return;
15329         }
15330         
15331         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15332         
15333         if(this.hiddenName){
15334             
15335             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15336             
15337             this.hiddenField.dom.value =
15338                 this.hiddenValue !== undefined ? this.hiddenValue :
15339                 this.value !== undefined ? this.value : '';
15340
15341             // prevent input submission
15342             this.el.dom.removeAttribute('name');
15343             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15344              
15345              
15346         }
15347         //if(Roo.isGecko){
15348         //    this.el.dom.setAttribute('autocomplete', 'off');
15349         //}
15350         
15351         var cls = 'x-combo-list';
15352         
15353         //this.list = new Roo.Layer({
15354         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15355         //});
15356         
15357         var _this = this;
15358         
15359         (function(){
15360             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15361             _this.list.setWidth(lw);
15362         }).defer(100);
15363         
15364         this.list.on('mouseover', this.onViewOver, this);
15365         this.list.on('mousemove', this.onViewMove, this);
15366         this.list.on('scroll', this.onViewScroll, this);
15367         
15368         /*
15369         this.list.swallowEvent('mousewheel');
15370         this.assetHeight = 0;
15371
15372         if(this.title){
15373             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15374             this.assetHeight += this.header.getHeight();
15375         }
15376
15377         this.innerList = this.list.createChild({cls:cls+'-inner'});
15378         this.innerList.on('mouseover', this.onViewOver, this);
15379         this.innerList.on('mousemove', this.onViewMove, this);
15380         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15381         
15382         if(this.allowBlank && !this.pageSize && !this.disableClear){
15383             this.footer = this.list.createChild({cls:cls+'-ft'});
15384             this.pageTb = new Roo.Toolbar(this.footer);
15385            
15386         }
15387         if(this.pageSize){
15388             this.footer = this.list.createChild({cls:cls+'-ft'});
15389             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15390                     {pageSize: this.pageSize});
15391             
15392         }
15393         
15394         if (this.pageTb && this.allowBlank && !this.disableClear) {
15395             var _this = this;
15396             this.pageTb.add(new Roo.Toolbar.Fill(), {
15397                 cls: 'x-btn-icon x-btn-clear',
15398                 text: '&#160;',
15399                 handler: function()
15400                 {
15401                     _this.collapse();
15402                     _this.clearValue();
15403                     _this.onSelect(false, -1);
15404                 }
15405             });
15406         }
15407         if (this.footer) {
15408             this.assetHeight += this.footer.getHeight();
15409         }
15410         */
15411             
15412         if(!this.tpl){
15413             this.tpl = Roo.bootstrap.version == 4 ?
15414                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
15415                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15416         }
15417
15418         this.view = new Roo.View(this.list, this.tpl, {
15419             singleSelect:true, store: this.store, selectedClass: this.selectedClass
15420         });
15421         //this.view.wrapEl.setDisplayed(false);
15422         this.view.on('click', this.onViewClick, this);
15423         
15424         
15425         this.store.on('beforeload', this.onBeforeLoad, this);
15426         this.store.on('load', this.onLoad, this);
15427         this.store.on('loadexception', this.onLoadException, this);
15428         /*
15429         if(this.resizable){
15430             this.resizer = new Roo.Resizable(this.list,  {
15431                pinned:true, handles:'se'
15432             });
15433             this.resizer.on('resize', function(r, w, h){
15434                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15435                 this.listWidth = w;
15436                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15437                 this.restrictHeight();
15438             }, this);
15439             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15440         }
15441         */
15442         if(!this.editable){
15443             this.editable = true;
15444             this.setEditable(false);
15445         }
15446         
15447         /*
15448         
15449         if (typeof(this.events.add.listeners) != 'undefined') {
15450             
15451             this.addicon = this.wrap.createChild(
15452                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
15453        
15454             this.addicon.on('click', function(e) {
15455                 this.fireEvent('add', this);
15456             }, this);
15457         }
15458         if (typeof(this.events.edit.listeners) != 'undefined') {
15459             
15460             this.editicon = this.wrap.createChild(
15461                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
15462             if (this.addicon) {
15463                 this.editicon.setStyle('margin-left', '40px');
15464             }
15465             this.editicon.on('click', function(e) {
15466                 
15467                 // we fire even  if inothing is selected..
15468                 this.fireEvent('edit', this, this.lastData );
15469                 
15470             }, this);
15471         }
15472         */
15473         
15474         this.keyNav = new Roo.KeyNav(this.inputEl(), {
15475             "up" : function(e){
15476                 this.inKeyMode = true;
15477                 this.selectPrev();
15478             },
15479
15480             "down" : function(e){
15481                 if(!this.isExpanded()){
15482                     this.onTriggerClick();
15483                 }else{
15484                     this.inKeyMode = true;
15485                     this.selectNext();
15486                 }
15487             },
15488
15489             "enter" : function(e){
15490 //                this.onViewClick();
15491                 //return true;
15492                 this.collapse();
15493                 
15494                 if(this.fireEvent("specialkey", this, e)){
15495                     this.onViewClick(false);
15496                 }
15497                 
15498                 return true;
15499             },
15500
15501             "esc" : function(e){
15502                 this.collapse();
15503             },
15504
15505             "tab" : function(e){
15506                 this.collapse();
15507                 
15508                 if(this.fireEvent("specialkey", this, e)){
15509                     this.onViewClick(false);
15510                 }
15511                 
15512                 return true;
15513             },
15514
15515             scope : this,
15516
15517             doRelay : function(foo, bar, hname){
15518                 if(hname == 'down' || this.scope.isExpanded()){
15519                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15520                 }
15521                 return true;
15522             },
15523
15524             forceKeyDown: true
15525         });
15526         
15527         
15528         this.queryDelay = Math.max(this.queryDelay || 10,
15529                 this.mode == 'local' ? 10 : 250);
15530         
15531         
15532         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15533         
15534         if(this.typeAhead){
15535             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15536         }
15537         if(this.editable !== false){
15538             this.inputEl().on("keyup", this.onKeyUp, this);
15539         }
15540         if(this.forceSelection){
15541             this.inputEl().on('blur', this.doForce, this);
15542         }
15543         
15544         if(this.multiple){
15545             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15546             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15547         }
15548     },
15549     
15550     initTickableEvents: function()
15551     {   
15552         this.createList();
15553         
15554         if(this.hiddenName){
15555             
15556             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15557             
15558             this.hiddenField.dom.value =
15559                 this.hiddenValue !== undefined ? this.hiddenValue :
15560                 this.value !== undefined ? this.value : '';
15561
15562             // prevent input submission
15563             this.el.dom.removeAttribute('name');
15564             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15565              
15566              
15567         }
15568         
15569 //        this.list = this.el.select('ul.dropdown-menu',true).first();
15570         
15571         this.choices = this.el.select('ul.roo-select2-choices', true).first();
15572         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15573         if(this.triggerList){
15574             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15575         }
15576          
15577         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15578         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15579         
15580         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15581         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15582         
15583         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15584         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15585         
15586         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15587         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15588         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15589         
15590         this.okBtn.hide();
15591         this.cancelBtn.hide();
15592         
15593         var _this = this;
15594         
15595         (function(){
15596             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15597             _this.list.setWidth(lw);
15598         }).defer(100);
15599         
15600         this.list.on('mouseover', this.onViewOver, this);
15601         this.list.on('mousemove', this.onViewMove, this);
15602         
15603         this.list.on('scroll', this.onViewScroll, this);
15604         
15605         if(!this.tpl){
15606             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
15607                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15608         }
15609
15610         this.view = new Roo.View(this.list, this.tpl, {
15611             singleSelect:true,
15612             tickable:true,
15613             parent:this,
15614             store: this.store,
15615             selectedClass: this.selectedClass
15616         });
15617         
15618         //this.view.wrapEl.setDisplayed(false);
15619         this.view.on('click', this.onViewClick, this);
15620         
15621         
15622         
15623         this.store.on('beforeload', this.onBeforeLoad, this);
15624         this.store.on('load', this.onLoad, this);
15625         this.store.on('loadexception', this.onLoadException, this);
15626         
15627         if(this.editable){
15628             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15629                 "up" : function(e){
15630                     this.inKeyMode = true;
15631                     this.selectPrev();
15632                 },
15633
15634                 "down" : function(e){
15635                     this.inKeyMode = true;
15636                     this.selectNext();
15637                 },
15638
15639                 "enter" : function(e){
15640                     if(this.fireEvent("specialkey", this, e)){
15641                         this.onViewClick(false);
15642                     }
15643                     
15644                     return true;
15645                 },
15646
15647                 "esc" : function(e){
15648                     this.onTickableFooterButtonClick(e, false, false);
15649                 },
15650
15651                 "tab" : function(e){
15652                     this.fireEvent("specialkey", this, e);
15653                     
15654                     this.onTickableFooterButtonClick(e, false, false);
15655                     
15656                     return true;
15657                 },
15658
15659                 scope : this,
15660
15661                 doRelay : function(e, fn, key){
15662                     if(this.scope.isExpanded()){
15663                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15664                     }
15665                     return true;
15666                 },
15667
15668                 forceKeyDown: true
15669             });
15670         }
15671         
15672         this.queryDelay = Math.max(this.queryDelay || 10,
15673                 this.mode == 'local' ? 10 : 250);
15674         
15675         
15676         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15677         
15678         if(this.typeAhead){
15679             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15680         }
15681         
15682         if(this.editable !== false){
15683             this.tickableInputEl().on("keyup", this.onKeyUp, this);
15684         }
15685         
15686         this.indicator = this.indicatorEl();
15687         
15688         if(this.indicator){
15689             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15690             this.indicator.hide();
15691         }
15692         
15693     },
15694
15695     onDestroy : function(){
15696         if(this.view){
15697             this.view.setStore(null);
15698             this.view.el.removeAllListeners();
15699             this.view.el.remove();
15700             this.view.purgeListeners();
15701         }
15702         if(this.list){
15703             this.list.dom.innerHTML  = '';
15704         }
15705         
15706         if(this.store){
15707             this.store.un('beforeload', this.onBeforeLoad, this);
15708             this.store.un('load', this.onLoad, this);
15709             this.store.un('loadexception', this.onLoadException, this);
15710         }
15711         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15712     },
15713
15714     // private
15715     fireKey : function(e){
15716         if(e.isNavKeyPress() && !this.list.isVisible()){
15717             this.fireEvent("specialkey", this, e);
15718         }
15719     },
15720
15721     // private
15722     onResize: function(w, h){
15723 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
15724 //        
15725 //        if(typeof w != 'number'){
15726 //            // we do not handle it!?!?
15727 //            return;
15728 //        }
15729 //        var tw = this.trigger.getWidth();
15730 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
15731 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
15732 //        var x = w - tw;
15733 //        this.inputEl().setWidth( this.adjustWidth('input', x));
15734 //            
15735 //        //this.trigger.setStyle('left', x+'px');
15736 //        
15737 //        if(this.list && this.listWidth === undefined){
15738 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
15739 //            this.list.setWidth(lw);
15740 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15741 //        }
15742         
15743     
15744         
15745     },
15746
15747     /**
15748      * Allow or prevent the user from directly editing the field text.  If false is passed,
15749      * the user will only be able to select from the items defined in the dropdown list.  This method
15750      * is the runtime equivalent of setting the 'editable' config option at config time.
15751      * @param {Boolean} value True to allow the user to directly edit the field text
15752      */
15753     setEditable : function(value){
15754         if(value == this.editable){
15755             return;
15756         }
15757         this.editable = value;
15758         if(!value){
15759             this.inputEl().dom.setAttribute('readOnly', true);
15760             this.inputEl().on('mousedown', this.onTriggerClick,  this);
15761             this.inputEl().addClass('x-combo-noedit');
15762         }else{
15763             this.inputEl().dom.setAttribute('readOnly', false);
15764             this.inputEl().un('mousedown', this.onTriggerClick,  this);
15765             this.inputEl().removeClass('x-combo-noedit');
15766         }
15767     },
15768
15769     // private
15770     
15771     onBeforeLoad : function(combo,opts){
15772         if(!this.hasFocus){
15773             return;
15774         }
15775          if (!opts.add) {
15776             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
15777          }
15778         this.restrictHeight();
15779         this.selectedIndex = -1;
15780     },
15781
15782     // private
15783     onLoad : function(){
15784         
15785         this.hasQuery = false;
15786         
15787         if(!this.hasFocus){
15788             return;
15789         }
15790         
15791         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
15792             this.loading.hide();
15793         }
15794         
15795         if(this.store.getCount() > 0){
15796             
15797             this.expand();
15798             this.restrictHeight();
15799             if(this.lastQuery == this.allQuery){
15800                 if(this.editable && !this.tickable){
15801                     this.inputEl().dom.select();
15802                 }
15803                 
15804                 if(
15805                     !this.selectByValue(this.value, true) &&
15806                     this.autoFocus && 
15807                     (
15808                         !this.store.lastOptions ||
15809                         typeof(this.store.lastOptions.add) == 'undefined' || 
15810                         this.store.lastOptions.add != true
15811                     )
15812                 ){
15813                     this.select(0, true);
15814                 }
15815             }else{
15816                 if(this.autoFocus){
15817                     this.selectNext();
15818                 }
15819                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
15820                     this.taTask.delay(this.typeAheadDelay);
15821                 }
15822             }
15823         }else{
15824             this.onEmptyResults();
15825         }
15826         
15827         //this.el.focus();
15828     },
15829     // private
15830     onLoadException : function()
15831     {
15832         this.hasQuery = false;
15833         
15834         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
15835             this.loading.hide();
15836         }
15837         
15838         if(this.tickable && this.editable){
15839             return;
15840         }
15841         
15842         this.collapse();
15843         // only causes errors at present
15844         //Roo.log(this.store.reader.jsonData);
15845         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
15846             // fixme
15847             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
15848         //}
15849         
15850         
15851     },
15852     // private
15853     onTypeAhead : function(){
15854         if(this.store.getCount() > 0){
15855             var r = this.store.getAt(0);
15856             var newValue = r.data[this.displayField];
15857             var len = newValue.length;
15858             var selStart = this.getRawValue().length;
15859             
15860             if(selStart != len){
15861                 this.setRawValue(newValue);
15862                 this.selectText(selStart, newValue.length);
15863             }
15864         }
15865     },
15866
15867     // private
15868     onSelect : function(record, index){
15869         
15870         if(this.fireEvent('beforeselect', this, record, index) !== false){
15871         
15872             this.setFromData(index > -1 ? record.data : false);
15873             
15874             this.collapse();
15875             this.fireEvent('select', this, record, index);
15876         }
15877     },
15878
15879     /**
15880      * Returns the currently selected field value or empty string if no value is set.
15881      * @return {String} value The selected value
15882      */
15883     getValue : function()
15884     {
15885         if(Roo.isIOS && this.useNativeIOS){
15886             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
15887         }
15888         
15889         if(this.multiple){
15890             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
15891         }
15892         
15893         if(this.valueField){
15894             return typeof this.value != 'undefined' ? this.value : '';
15895         }else{
15896             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
15897         }
15898     },
15899     
15900     getRawValue : function()
15901     {
15902         if(Roo.isIOS && this.useNativeIOS){
15903             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
15904         }
15905         
15906         var v = this.inputEl().getValue();
15907         
15908         return v;
15909     },
15910
15911     /**
15912      * Clears any text/value currently set in the field
15913      */
15914     clearValue : function(){
15915         
15916         if(this.hiddenField){
15917             this.hiddenField.dom.value = '';
15918         }
15919         this.value = '';
15920         this.setRawValue('');
15921         this.lastSelectionText = '';
15922         this.lastData = false;
15923         
15924         var close = this.closeTriggerEl();
15925         
15926         if(close){
15927             close.hide();
15928         }
15929         
15930         this.validate();
15931         
15932     },
15933
15934     /**
15935      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
15936      * will be displayed in the field.  If the value does not match the data value of an existing item,
15937      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
15938      * Otherwise the field will be blank (although the value will still be set).
15939      * @param {String} value The value to match
15940      */
15941     setValue : function(v)
15942     {
15943         if(Roo.isIOS && this.useNativeIOS){
15944             this.setIOSValue(v);
15945             return;
15946         }
15947         
15948         if(this.multiple){
15949             this.syncValue();
15950             return;
15951         }
15952         
15953         var text = v;
15954         if(this.valueField){
15955             var r = this.findRecord(this.valueField, v);
15956             if(r){
15957                 text = r.data[this.displayField];
15958             }else if(this.valueNotFoundText !== undefined){
15959                 text = this.valueNotFoundText;
15960             }
15961         }
15962         this.lastSelectionText = text;
15963         if(this.hiddenField){
15964             this.hiddenField.dom.value = v;
15965         }
15966         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
15967         this.value = v;
15968         
15969         var close = this.closeTriggerEl();
15970         
15971         if(close){
15972             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
15973         }
15974         
15975         this.validate();
15976     },
15977     /**
15978      * @property {Object} the last set data for the element
15979      */
15980     
15981     lastData : false,
15982     /**
15983      * Sets the value of the field based on a object which is related to the record format for the store.
15984      * @param {Object} value the value to set as. or false on reset?
15985      */
15986     setFromData : function(o){
15987         
15988         if(this.multiple){
15989             this.addItem(o);
15990             return;
15991         }
15992             
15993         var dv = ''; // display value
15994         var vv = ''; // value value..
15995         this.lastData = o;
15996         if (this.displayField) {
15997             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
15998         } else {
15999             // this is an error condition!!!
16000             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16001         }
16002         
16003         if(this.valueField){
16004             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16005         }
16006         
16007         var close = this.closeTriggerEl();
16008         
16009         if(close){
16010             if(dv.length || vv * 1 > 0){
16011                 close.show() ;
16012                 this.blockFocus=true;
16013             } else {
16014                 close.hide();
16015             }             
16016         }
16017         
16018         if(this.hiddenField){
16019             this.hiddenField.dom.value = vv;
16020             
16021             this.lastSelectionText = dv;
16022             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16023             this.value = vv;
16024             return;
16025         }
16026         // no hidden field.. - we store the value in 'value', but still display
16027         // display field!!!!
16028         this.lastSelectionText = dv;
16029         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16030         this.value = vv;
16031         
16032         
16033         
16034     },
16035     // private
16036     reset : function(){
16037         // overridden so that last data is reset..
16038         
16039         if(this.multiple){
16040             this.clearItem();
16041             return;
16042         }
16043         
16044         this.setValue(this.originalValue);
16045         //this.clearInvalid();
16046         this.lastData = false;
16047         if (this.view) {
16048             this.view.clearSelections();
16049         }
16050         
16051         this.validate();
16052     },
16053     // private
16054     findRecord : function(prop, value){
16055         var record;
16056         if(this.store.getCount() > 0){
16057             this.store.each(function(r){
16058                 if(r.data[prop] == value){
16059                     record = r;
16060                     return false;
16061                 }
16062                 return true;
16063             });
16064         }
16065         return record;
16066     },
16067     
16068     getName: function()
16069     {
16070         // returns hidden if it's set..
16071         if (!this.rendered) {return ''};
16072         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16073         
16074     },
16075     // private
16076     onViewMove : function(e, t){
16077         this.inKeyMode = false;
16078     },
16079
16080     // private
16081     onViewOver : function(e, t){
16082         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16083             return;
16084         }
16085         var item = this.view.findItemFromChild(t);
16086         
16087         if(item){
16088             var index = this.view.indexOf(item);
16089             this.select(index, false);
16090         }
16091     },
16092
16093     // private
16094     onViewClick : function(view, doFocus, el, e)
16095     {
16096         var index = this.view.getSelectedIndexes()[0];
16097         
16098         var r = this.store.getAt(index);
16099         
16100         if(this.tickable){
16101             
16102             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16103                 return;
16104             }
16105             
16106             var rm = false;
16107             var _this = this;
16108             
16109             Roo.each(this.tickItems, function(v,k){
16110                 
16111                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16112                     Roo.log(v);
16113                     _this.tickItems.splice(k, 1);
16114                     
16115                     if(typeof(e) == 'undefined' && view == false){
16116                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16117                     }
16118                     
16119                     rm = true;
16120                     return;
16121                 }
16122             });
16123             
16124             if(rm){
16125                 return;
16126             }
16127             
16128             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16129                 this.tickItems.push(r.data);
16130             }
16131             
16132             if(typeof(e) == 'undefined' && view == false){
16133                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16134             }
16135                     
16136             return;
16137         }
16138         
16139         if(r){
16140             this.onSelect(r, index);
16141         }
16142         if(doFocus !== false && !this.blockFocus){
16143             this.inputEl().focus();
16144         }
16145     },
16146
16147     // private
16148     restrictHeight : function(){
16149         //this.innerList.dom.style.height = '';
16150         //var inner = this.innerList.dom;
16151         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16152         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16153         //this.list.beginUpdate();
16154         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16155         this.list.alignTo(this.inputEl(), this.listAlign);
16156         this.list.alignTo(this.inputEl(), this.listAlign);
16157         //this.list.endUpdate();
16158     },
16159
16160     // private
16161     onEmptyResults : function(){
16162         
16163         if(this.tickable && this.editable){
16164             this.hasFocus = false;
16165             this.restrictHeight();
16166             return;
16167         }
16168         
16169         this.collapse();
16170     },
16171
16172     /**
16173      * Returns true if the dropdown list is expanded, else false.
16174      */
16175     isExpanded : function(){
16176         return this.list.isVisible();
16177     },
16178
16179     /**
16180      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16181      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16182      * @param {String} value The data value of the item to select
16183      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16184      * selected item if it is not currently in view (defaults to true)
16185      * @return {Boolean} True if the value matched an item in the list, else false
16186      */
16187     selectByValue : function(v, scrollIntoView){
16188         if(v !== undefined && v !== null){
16189             var r = this.findRecord(this.valueField || this.displayField, v);
16190             if(r){
16191                 this.select(this.store.indexOf(r), scrollIntoView);
16192                 return true;
16193             }
16194         }
16195         return false;
16196     },
16197
16198     /**
16199      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16200      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16201      * @param {Number} index The zero-based index of the list item to select
16202      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16203      * selected item if it is not currently in view (defaults to true)
16204      */
16205     select : function(index, scrollIntoView){
16206         this.selectedIndex = index;
16207         this.view.select(index);
16208         if(scrollIntoView !== false){
16209             var el = this.view.getNode(index);
16210             /*
16211              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16212              */
16213             if(el){
16214                 this.list.scrollChildIntoView(el, false);
16215             }
16216         }
16217     },
16218
16219     // private
16220     selectNext : function(){
16221         var ct = this.store.getCount();
16222         if(ct > 0){
16223             if(this.selectedIndex == -1){
16224                 this.select(0);
16225             }else if(this.selectedIndex < ct-1){
16226                 this.select(this.selectedIndex+1);
16227             }
16228         }
16229     },
16230
16231     // private
16232     selectPrev : function(){
16233         var ct = this.store.getCount();
16234         if(ct > 0){
16235             if(this.selectedIndex == -1){
16236                 this.select(0);
16237             }else if(this.selectedIndex != 0){
16238                 this.select(this.selectedIndex-1);
16239             }
16240         }
16241     },
16242
16243     // private
16244     onKeyUp : function(e){
16245         if(this.editable !== false && !e.isSpecialKey()){
16246             this.lastKey = e.getKey();
16247             this.dqTask.delay(this.queryDelay);
16248         }
16249     },
16250
16251     // private
16252     validateBlur : function(){
16253         return !this.list || !this.list.isVisible();   
16254     },
16255
16256     // private
16257     initQuery : function(){
16258         
16259         var v = this.getRawValue();
16260         
16261         if(this.tickable && this.editable){
16262             v = this.tickableInputEl().getValue();
16263         }
16264         
16265         this.doQuery(v);
16266     },
16267
16268     // private
16269     doForce : function(){
16270         if(this.inputEl().dom.value.length > 0){
16271             this.inputEl().dom.value =
16272                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16273              
16274         }
16275     },
16276
16277     /**
16278      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16279      * query allowing the query action to be canceled if needed.
16280      * @param {String} query The SQL query to execute
16281      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16282      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16283      * saved in the current store (defaults to false)
16284      */
16285     doQuery : function(q, forceAll){
16286         
16287         if(q === undefined || q === null){
16288             q = '';
16289         }
16290         var qe = {
16291             query: q,
16292             forceAll: forceAll,
16293             combo: this,
16294             cancel:false
16295         };
16296         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16297             return false;
16298         }
16299         q = qe.query;
16300         
16301         forceAll = qe.forceAll;
16302         if(forceAll === true || (q.length >= this.minChars)){
16303             
16304             this.hasQuery = true;
16305             
16306             if(this.lastQuery != q || this.alwaysQuery){
16307                 this.lastQuery = q;
16308                 if(this.mode == 'local'){
16309                     this.selectedIndex = -1;
16310                     if(forceAll){
16311                         this.store.clearFilter();
16312                     }else{
16313                         
16314                         if(this.specialFilter){
16315                             this.fireEvent('specialfilter', this);
16316                             this.onLoad();
16317                             return;
16318                         }
16319                         
16320                         this.store.filter(this.displayField, q);
16321                     }
16322                     
16323                     this.store.fireEvent("datachanged", this.store);
16324                     
16325                     this.onLoad();
16326                     
16327                     
16328                 }else{
16329                     
16330                     this.store.baseParams[this.queryParam] = q;
16331                     
16332                     var options = {params : this.getParams(q)};
16333                     
16334                     if(this.loadNext){
16335                         options.add = true;
16336                         options.params.start = this.page * this.pageSize;
16337                     }
16338                     
16339                     this.store.load(options);
16340                     
16341                     /*
16342                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16343                      *  we should expand the list on onLoad
16344                      *  so command out it
16345                      */
16346 //                    this.expand();
16347                 }
16348             }else{
16349                 this.selectedIndex = -1;
16350                 this.onLoad();   
16351             }
16352         }
16353         
16354         this.loadNext = false;
16355     },
16356     
16357     // private
16358     getParams : function(q){
16359         var p = {};
16360         //p[this.queryParam] = q;
16361         
16362         if(this.pageSize){
16363             p.start = 0;
16364             p.limit = this.pageSize;
16365         }
16366         return p;
16367     },
16368
16369     /**
16370      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16371      */
16372     collapse : function(){
16373         if(!this.isExpanded()){
16374             return;
16375         }
16376         
16377         this.list.hide();
16378         
16379         this.hasFocus = false;
16380         
16381         if(this.tickable){
16382             this.okBtn.hide();
16383             this.cancelBtn.hide();
16384             this.trigger.show();
16385             
16386             if(this.editable){
16387                 this.tickableInputEl().dom.value = '';
16388                 this.tickableInputEl().blur();
16389             }
16390             
16391         }
16392         
16393         Roo.get(document).un('mousedown', this.collapseIf, this);
16394         Roo.get(document).un('mousewheel', this.collapseIf, this);
16395         if (!this.editable) {
16396             Roo.get(document).un('keydown', this.listKeyPress, this);
16397         }
16398         this.fireEvent('collapse', this);
16399         
16400         this.validate();
16401     },
16402
16403     // private
16404     collapseIf : function(e){
16405         var in_combo  = e.within(this.el);
16406         var in_list =  e.within(this.list);
16407         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16408         
16409         if (in_combo || in_list || is_list) {
16410             //e.stopPropagation();
16411             return;
16412         }
16413         
16414         if(this.tickable){
16415             this.onTickableFooterButtonClick(e, false, false);
16416         }
16417
16418         this.collapse();
16419         
16420     },
16421
16422     /**
16423      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16424      */
16425     expand : function(){
16426        
16427         if(this.isExpanded() || !this.hasFocus){
16428             return;
16429         }
16430         
16431         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16432         this.list.setWidth(lw);
16433         
16434         Roo.log('expand');
16435         
16436         this.list.show();
16437         
16438         this.restrictHeight();
16439         
16440         if(this.tickable){
16441             
16442             this.tickItems = Roo.apply([], this.item);
16443             
16444             this.okBtn.show();
16445             this.cancelBtn.show();
16446             this.trigger.hide();
16447             
16448             if(this.editable){
16449                 this.tickableInputEl().focus();
16450             }
16451             
16452         }
16453         
16454         Roo.get(document).on('mousedown', this.collapseIf, this);
16455         Roo.get(document).on('mousewheel', this.collapseIf, this);
16456         if (!this.editable) {
16457             Roo.get(document).on('keydown', this.listKeyPress, this);
16458         }
16459         
16460         this.fireEvent('expand', this);
16461     },
16462
16463     // private
16464     // Implements the default empty TriggerField.onTriggerClick function
16465     onTriggerClick : function(e)
16466     {
16467         Roo.log('trigger click');
16468         
16469         if(this.disabled || !this.triggerList){
16470             return;
16471         }
16472         
16473         this.page = 0;
16474         this.loadNext = false;
16475         
16476         if(this.isExpanded()){
16477             this.collapse();
16478             if (!this.blockFocus) {
16479                 this.inputEl().focus();
16480             }
16481             
16482         }else {
16483             this.hasFocus = true;
16484             if(this.triggerAction == 'all') {
16485                 this.doQuery(this.allQuery, true);
16486             } else {
16487                 this.doQuery(this.getRawValue());
16488             }
16489             if (!this.blockFocus) {
16490                 this.inputEl().focus();
16491             }
16492         }
16493     },
16494     
16495     onTickableTriggerClick : function(e)
16496     {
16497         if(this.disabled){
16498             return;
16499         }
16500         
16501         this.page = 0;
16502         this.loadNext = false;
16503         this.hasFocus = true;
16504         
16505         if(this.triggerAction == 'all') {
16506             this.doQuery(this.allQuery, true);
16507         } else {
16508             this.doQuery(this.getRawValue());
16509         }
16510     },
16511     
16512     onSearchFieldClick : function(e)
16513     {
16514         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16515             this.onTickableFooterButtonClick(e, false, false);
16516             return;
16517         }
16518         
16519         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16520             return;
16521         }
16522         
16523         this.page = 0;
16524         this.loadNext = false;
16525         this.hasFocus = true;
16526         
16527         if(this.triggerAction == 'all') {
16528             this.doQuery(this.allQuery, true);
16529         } else {
16530             this.doQuery(this.getRawValue());
16531         }
16532     },
16533     
16534     listKeyPress : function(e)
16535     {
16536         //Roo.log('listkeypress');
16537         // scroll to first matching element based on key pres..
16538         if (e.isSpecialKey()) {
16539             return false;
16540         }
16541         var k = String.fromCharCode(e.getKey()).toUpperCase();
16542         //Roo.log(k);
16543         var match  = false;
16544         var csel = this.view.getSelectedNodes();
16545         var cselitem = false;
16546         if (csel.length) {
16547             var ix = this.view.indexOf(csel[0]);
16548             cselitem  = this.store.getAt(ix);
16549             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16550                 cselitem = false;
16551             }
16552             
16553         }
16554         
16555         this.store.each(function(v) { 
16556             if (cselitem) {
16557                 // start at existing selection.
16558                 if (cselitem.id == v.id) {
16559                     cselitem = false;
16560                 }
16561                 return true;
16562             }
16563                 
16564             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16565                 match = this.store.indexOf(v);
16566                 return false;
16567             }
16568             return true;
16569         }, this);
16570         
16571         if (match === false) {
16572             return true; // no more action?
16573         }
16574         // scroll to?
16575         this.view.select(match);
16576         var sn = Roo.get(this.view.getSelectedNodes()[0]);
16577         sn.scrollIntoView(sn.dom.parentNode, false);
16578     },
16579     
16580     onViewScroll : function(e, t){
16581         
16582         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){
16583             return;
16584         }
16585         
16586         this.hasQuery = true;
16587         
16588         this.loading = this.list.select('.loading', true).first();
16589         
16590         if(this.loading === null){
16591             this.list.createChild({
16592                 tag: 'div',
16593                 cls: 'loading roo-select2-more-results roo-select2-active',
16594                 html: 'Loading more results...'
16595             });
16596             
16597             this.loading = this.list.select('.loading', true).first();
16598             
16599             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16600             
16601             this.loading.hide();
16602         }
16603         
16604         this.loading.show();
16605         
16606         var _combo = this;
16607         
16608         this.page++;
16609         this.loadNext = true;
16610         
16611         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16612         
16613         return;
16614     },
16615     
16616     addItem : function(o)
16617     {   
16618         var dv = ''; // display value
16619         
16620         if (this.displayField) {
16621             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16622         } else {
16623             // this is an error condition!!!
16624             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16625         }
16626         
16627         if(!dv.length){
16628             return;
16629         }
16630         
16631         var choice = this.choices.createChild({
16632             tag: 'li',
16633             cls: 'roo-select2-search-choice',
16634             cn: [
16635                 {
16636                     tag: 'div',
16637                     html: dv
16638                 },
16639                 {
16640                     tag: 'a',
16641                     href: '#',
16642                     cls: 'roo-select2-search-choice-close fa fa-times',
16643                     tabindex: '-1'
16644                 }
16645             ]
16646             
16647         }, this.searchField);
16648         
16649         var close = choice.select('a.roo-select2-search-choice-close', true).first();
16650         
16651         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16652         
16653         this.item.push(o);
16654         
16655         this.lastData = o;
16656         
16657         this.syncValue();
16658         
16659         this.inputEl().dom.value = '';
16660         
16661         this.validate();
16662     },
16663     
16664     onRemoveItem : function(e, _self, o)
16665     {
16666         e.preventDefault();
16667         
16668         this.lastItem = Roo.apply([], this.item);
16669         
16670         var index = this.item.indexOf(o.data) * 1;
16671         
16672         if( index < 0){
16673             Roo.log('not this item?!');
16674             return;
16675         }
16676         
16677         this.item.splice(index, 1);
16678         o.item.remove();
16679         
16680         this.syncValue();
16681         
16682         this.fireEvent('remove', this, e);
16683         
16684         this.validate();
16685         
16686     },
16687     
16688     syncValue : function()
16689     {
16690         if(!this.item.length){
16691             this.clearValue();
16692             return;
16693         }
16694             
16695         var value = [];
16696         var _this = this;
16697         Roo.each(this.item, function(i){
16698             if(_this.valueField){
16699                 value.push(i[_this.valueField]);
16700                 return;
16701             }
16702
16703             value.push(i);
16704         });
16705
16706         this.value = value.join(',');
16707
16708         if(this.hiddenField){
16709             this.hiddenField.dom.value = this.value;
16710         }
16711         
16712         this.store.fireEvent("datachanged", this.store);
16713         
16714         this.validate();
16715     },
16716     
16717     clearItem : function()
16718     {
16719         if(!this.multiple){
16720             return;
16721         }
16722         
16723         this.item = [];
16724         
16725         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
16726            c.remove();
16727         });
16728         
16729         this.syncValue();
16730         
16731         this.validate();
16732         
16733         if(this.tickable && !Roo.isTouch){
16734             this.view.refresh();
16735         }
16736     },
16737     
16738     inputEl: function ()
16739     {
16740         if(Roo.isIOS && this.useNativeIOS){
16741             return this.el.select('select.roo-ios-select', true).first();
16742         }
16743         
16744         if(Roo.isTouch && this.mobileTouchView){
16745             return this.el.select('input.form-control',true).first();
16746         }
16747         
16748         if(this.tickable){
16749             return this.searchField;
16750         }
16751         
16752         return this.el.select('input.form-control',true).first();
16753     },
16754     
16755     onTickableFooterButtonClick : function(e, btn, el)
16756     {
16757         e.preventDefault();
16758         
16759         this.lastItem = Roo.apply([], this.item);
16760         
16761         if(btn && btn.name == 'cancel'){
16762             this.tickItems = Roo.apply([], this.item);
16763             this.collapse();
16764             return;
16765         }
16766         
16767         this.clearItem();
16768         
16769         var _this = this;
16770         
16771         Roo.each(this.tickItems, function(o){
16772             _this.addItem(o);
16773         });
16774         
16775         this.collapse();
16776         
16777     },
16778     
16779     validate : function()
16780     {
16781         if(this.getVisibilityEl().hasClass('hidden')){
16782             return true;
16783         }
16784         
16785         var v = this.getRawValue();
16786         
16787         if(this.multiple){
16788             v = this.getValue();
16789         }
16790         
16791         if(this.disabled || this.allowBlank || v.length){
16792             this.markValid();
16793             return true;
16794         }
16795         
16796         this.markInvalid();
16797         return false;
16798     },
16799     
16800     tickableInputEl : function()
16801     {
16802         if(!this.tickable || !this.editable){
16803             return this.inputEl();
16804         }
16805         
16806         return this.inputEl().select('.roo-select2-search-field-input', true).first();
16807     },
16808     
16809     
16810     getAutoCreateTouchView : function()
16811     {
16812         var id = Roo.id();
16813         
16814         var cfg = {
16815             cls: 'form-group' //input-group
16816         };
16817         
16818         var input =  {
16819             tag: 'input',
16820             id : id,
16821             type : this.inputType,
16822             cls : 'form-control x-combo-noedit',
16823             autocomplete: 'new-password',
16824             placeholder : this.placeholder || '',
16825             readonly : true
16826         };
16827         
16828         if (this.name) {
16829             input.name = this.name;
16830         }
16831         
16832         if (this.size) {
16833             input.cls += ' input-' + this.size;
16834         }
16835         
16836         if (this.disabled) {
16837             input.disabled = true;
16838         }
16839         
16840         var inputblock = {
16841             cls : '',
16842             cn : [
16843                 input
16844             ]
16845         };
16846         
16847         if(this.before){
16848             inputblock.cls += ' input-group';
16849             
16850             inputblock.cn.unshift({
16851                 tag :'span',
16852                 cls : 'input-group-addon input-group-prepend input-group-text',
16853                 html : this.before
16854             });
16855         }
16856         
16857         if(this.removable && !this.multiple){
16858             inputblock.cls += ' roo-removable';
16859             
16860             inputblock.cn.push({
16861                 tag: 'button',
16862                 html : 'x',
16863                 cls : 'roo-combo-removable-btn close'
16864             });
16865         }
16866
16867         if(this.hasFeedback && !this.allowBlank){
16868             
16869             inputblock.cls += ' has-feedback';
16870             
16871             inputblock.cn.push({
16872                 tag: 'span',
16873                 cls: 'glyphicon form-control-feedback'
16874             });
16875             
16876         }
16877         
16878         if (this.after) {
16879             
16880             inputblock.cls += (this.before) ? '' : ' input-group';
16881             
16882             inputblock.cn.push({
16883                 tag :'span',
16884                 cls : 'input-group-addon input-group-append input-group-text',
16885                 html : this.after
16886             });
16887         }
16888
16889         
16890         var ibwrap = inputblock;
16891         
16892         if(this.multiple){
16893             ibwrap = {
16894                 tag: 'ul',
16895                 cls: 'roo-select2-choices',
16896                 cn:[
16897                     {
16898                         tag: 'li',
16899                         cls: 'roo-select2-search-field',
16900                         cn: [
16901
16902                             inputblock
16903                         ]
16904                     }
16905                 ]
16906             };
16907         
16908             
16909         }
16910         
16911         var combobox = {
16912             cls: 'roo-select2-container input-group roo-touchview-combobox ',
16913             cn: [
16914                 {
16915                     tag: 'input',
16916                     type : 'hidden',
16917                     cls: 'form-hidden-field'
16918                 },
16919                 ibwrap
16920             ]
16921         };
16922         
16923         if(!this.multiple && this.showToggleBtn){
16924             
16925             var caret = {
16926                 cls: 'caret'
16927             };
16928             
16929             if (this.caret != false) {
16930                 caret = {
16931                      tag: 'i',
16932                      cls: 'fa fa-' + this.caret
16933                 };
16934                 
16935             }
16936             
16937             combobox.cn.push({
16938                 tag :'span',
16939                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
16940                 cn : [
16941                     Roo.bootstrap.version == 3 ? caret : '',
16942                     {
16943                         tag: 'span',
16944                         cls: 'combobox-clear',
16945                         cn  : [
16946                             {
16947                                 tag : 'i',
16948                                 cls: 'icon-remove'
16949                             }
16950                         ]
16951                     }
16952                 ]
16953
16954             })
16955         }
16956         
16957         if(this.multiple){
16958             combobox.cls += ' roo-select2-container-multi';
16959         }
16960         
16961         var align = this.labelAlign || this.parentLabelAlign();
16962         
16963         if (align ==='left' && this.fieldLabel.length) {
16964
16965             cfg.cn = [
16966                 {
16967                    tag : 'i',
16968                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
16969                    tooltip : 'This field is required'
16970                 },
16971                 {
16972                     tag: 'label',
16973                     cls : 'control-label col-form-label',
16974                     html : this.fieldLabel
16975
16976                 },
16977                 {
16978                     cls : '', 
16979                     cn: [
16980                         combobox
16981                     ]
16982                 }
16983             ];
16984             
16985             var labelCfg = cfg.cn[1];
16986             var contentCfg = cfg.cn[2];
16987             
16988
16989             if(this.indicatorpos == 'right'){
16990                 cfg.cn = [
16991                     {
16992                         tag: 'label',
16993                         'for' :  id,
16994                         cls : 'control-label col-form-label',
16995                         cn : [
16996                             {
16997                                 tag : 'span',
16998                                 html : this.fieldLabel
16999                             },
17000                             {
17001                                 tag : 'i',
17002                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17003                                 tooltip : 'This field is required'
17004                             }
17005                         ]
17006                     },
17007                     {
17008                         cls : "",
17009                         cn: [
17010                             combobox
17011                         ]
17012                     }
17013
17014                 ];
17015                 
17016                 labelCfg = cfg.cn[0];
17017                 contentCfg = cfg.cn[1];
17018             }
17019             
17020            
17021             
17022             if(this.labelWidth > 12){
17023                 labelCfg.style = "width: " + this.labelWidth + 'px';
17024             }
17025             
17026             if(this.labelWidth < 13 && this.labelmd == 0){
17027                 this.labelmd = this.labelWidth;
17028             }
17029             
17030             if(this.labellg > 0){
17031                 labelCfg.cls += ' col-lg-' + this.labellg;
17032                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17033             }
17034             
17035             if(this.labelmd > 0){
17036                 labelCfg.cls += ' col-md-' + this.labelmd;
17037                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17038             }
17039             
17040             if(this.labelsm > 0){
17041                 labelCfg.cls += ' col-sm-' + this.labelsm;
17042                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17043             }
17044             
17045             if(this.labelxs > 0){
17046                 labelCfg.cls += ' col-xs-' + this.labelxs;
17047                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17048             }
17049                 
17050                 
17051         } else if ( this.fieldLabel.length) {
17052             cfg.cn = [
17053                 {
17054                    tag : 'i',
17055                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17056                    tooltip : 'This field is required'
17057                 },
17058                 {
17059                     tag: 'label',
17060                     cls : 'control-label',
17061                     html : this.fieldLabel
17062
17063                 },
17064                 {
17065                     cls : '', 
17066                     cn: [
17067                         combobox
17068                     ]
17069                 }
17070             ];
17071             
17072             if(this.indicatorpos == 'right'){
17073                 cfg.cn = [
17074                     {
17075                         tag: 'label',
17076                         cls : 'control-label',
17077                         html : this.fieldLabel,
17078                         cn : [
17079                             {
17080                                tag : 'i',
17081                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17082                                tooltip : 'This field is required'
17083                             }
17084                         ]
17085                     },
17086                     {
17087                         cls : '', 
17088                         cn: [
17089                             combobox
17090                         ]
17091                     }
17092                 ];
17093             }
17094         } else {
17095             cfg.cn = combobox;    
17096         }
17097         
17098         
17099         var settings = this;
17100         
17101         ['xs','sm','md','lg'].map(function(size){
17102             if (settings[size]) {
17103                 cfg.cls += ' col-' + size + '-' + settings[size];
17104             }
17105         });
17106         
17107         return cfg;
17108     },
17109     
17110     initTouchView : function()
17111     {
17112         this.renderTouchView();
17113         
17114         this.touchViewEl.on('scroll', function(){
17115             this.el.dom.scrollTop = 0;
17116         }, this);
17117         
17118         this.originalValue = this.getValue();
17119         
17120         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17121         
17122         this.inputEl().on("click", this.showTouchView, this);
17123         if (this.triggerEl) {
17124             this.triggerEl.on("click", this.showTouchView, this);
17125         }
17126         
17127         
17128         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17129         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17130         
17131         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17132         
17133         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17134         this.store.on('load', this.onTouchViewLoad, this);
17135         this.store.on('loadexception', this.onTouchViewLoadException, this);
17136         
17137         if(this.hiddenName){
17138             
17139             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17140             
17141             this.hiddenField.dom.value =
17142                 this.hiddenValue !== undefined ? this.hiddenValue :
17143                 this.value !== undefined ? this.value : '';
17144         
17145             this.el.dom.removeAttribute('name');
17146             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17147         }
17148         
17149         if(this.multiple){
17150             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17151             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17152         }
17153         
17154         if(this.removable && !this.multiple){
17155             var close = this.closeTriggerEl();
17156             if(close){
17157                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17158                 close.on('click', this.removeBtnClick, this, close);
17159             }
17160         }
17161         /*
17162          * fix the bug in Safari iOS8
17163          */
17164         this.inputEl().on("focus", function(e){
17165             document.activeElement.blur();
17166         }, this);
17167         
17168         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17169         
17170         return;
17171         
17172         
17173     },
17174     
17175     renderTouchView : function()
17176     {
17177         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17178         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17179         
17180         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17181         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17182         
17183         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17184         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17185         this.touchViewBodyEl.setStyle('overflow', 'auto');
17186         
17187         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17188         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17189         
17190         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17191         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17192         
17193     },
17194     
17195     showTouchView : function()
17196     {
17197         if(this.disabled){
17198             return;
17199         }
17200         
17201         this.touchViewHeaderEl.hide();
17202
17203         if(this.modalTitle.length){
17204             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17205             this.touchViewHeaderEl.show();
17206         }
17207
17208         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17209         this.touchViewEl.show();
17210
17211         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17212         
17213         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17214         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17215
17216         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17217
17218         if(this.modalTitle.length){
17219             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17220         }
17221         
17222         this.touchViewBodyEl.setHeight(bodyHeight);
17223
17224         if(this.animate){
17225             var _this = this;
17226             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
17227         }else{
17228             this.touchViewEl.addClass('in');
17229         }
17230         
17231         if(this._touchViewMask){
17232             Roo.get(document.body).addClass("x-body-masked");
17233             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17234             this._touchViewMask.setStyle('z-index', 10000);
17235             this._touchViewMask.addClass('show');
17236         }
17237         
17238         this.doTouchViewQuery();
17239         
17240     },
17241     
17242     hideTouchView : function()
17243     {
17244         this.touchViewEl.removeClass('in');
17245
17246         if(this.animate){
17247             var _this = this;
17248             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17249         }else{
17250             this.touchViewEl.setStyle('display', 'none');
17251         }
17252         
17253         if(this._touchViewMask){
17254             this._touchViewMask.removeClass('show');
17255             Roo.get(document.body).removeClass("x-body-masked");
17256         }
17257     },
17258     
17259     setTouchViewValue : function()
17260     {
17261         if(this.multiple){
17262             this.clearItem();
17263         
17264             var _this = this;
17265
17266             Roo.each(this.tickItems, function(o){
17267                 this.addItem(o);
17268             }, this);
17269         }
17270         
17271         this.hideTouchView();
17272     },
17273     
17274     doTouchViewQuery : function()
17275     {
17276         var qe = {
17277             query: '',
17278             forceAll: true,
17279             combo: this,
17280             cancel:false
17281         };
17282         
17283         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17284             return false;
17285         }
17286         
17287         if(!this.alwaysQuery || this.mode == 'local'){
17288             this.onTouchViewLoad();
17289             return;
17290         }
17291         
17292         this.store.load();
17293     },
17294     
17295     onTouchViewBeforeLoad : function(combo,opts)
17296     {
17297         return;
17298     },
17299
17300     // private
17301     onTouchViewLoad : function()
17302     {
17303         if(this.store.getCount() < 1){
17304             this.onTouchViewEmptyResults();
17305             return;
17306         }
17307         
17308         this.clearTouchView();
17309         
17310         var rawValue = this.getRawValue();
17311         
17312         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17313         
17314         this.tickItems = [];
17315         
17316         this.store.data.each(function(d, rowIndex){
17317             var row = this.touchViewListGroup.createChild(template);
17318             
17319             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17320                 row.addClass(d.data.cls);
17321             }
17322             
17323             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17324                 var cfg = {
17325                     data : d.data,
17326                     html : d.data[this.displayField]
17327                 };
17328                 
17329                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17330                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17331                 }
17332             }
17333             row.removeClass('selected');
17334             if(!this.multiple && this.valueField &&
17335                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17336             {
17337                 // radio buttons..
17338                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17339                 row.addClass('selected');
17340             }
17341             
17342             if(this.multiple && this.valueField &&
17343                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17344             {
17345                 
17346                 // checkboxes...
17347                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17348                 this.tickItems.push(d.data);
17349             }
17350             
17351             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17352             
17353         }, this);
17354         
17355         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17356         
17357         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17358
17359         if(this.modalTitle.length){
17360             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17361         }
17362
17363         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17364         
17365         if(this.mobile_restrict_height && listHeight < bodyHeight){
17366             this.touchViewBodyEl.setHeight(listHeight);
17367         }
17368         
17369         var _this = this;
17370         
17371         if(firstChecked && listHeight > bodyHeight){
17372             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17373         }
17374         
17375     },
17376     
17377     onTouchViewLoadException : function()
17378     {
17379         this.hideTouchView();
17380     },
17381     
17382     onTouchViewEmptyResults : function()
17383     {
17384         this.clearTouchView();
17385         
17386         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17387         
17388         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17389         
17390     },
17391     
17392     clearTouchView : function()
17393     {
17394         this.touchViewListGroup.dom.innerHTML = '';
17395     },
17396     
17397     onTouchViewClick : function(e, el, o)
17398     {
17399         e.preventDefault();
17400         
17401         var row = o.row;
17402         var rowIndex = o.rowIndex;
17403         
17404         var r = this.store.getAt(rowIndex);
17405         
17406         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17407             
17408             if(!this.multiple){
17409                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17410                     c.dom.removeAttribute('checked');
17411                 }, this);
17412
17413                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17414
17415                 this.setFromData(r.data);
17416
17417                 var close = this.closeTriggerEl();
17418
17419                 if(close){
17420                     close.show();
17421                 }
17422
17423                 this.hideTouchView();
17424
17425                 this.fireEvent('select', this, r, rowIndex);
17426
17427                 return;
17428             }
17429
17430             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17431                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17432                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17433                 return;
17434             }
17435
17436             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17437             this.addItem(r.data);
17438             this.tickItems.push(r.data);
17439         }
17440     },
17441     
17442     getAutoCreateNativeIOS : function()
17443     {
17444         var cfg = {
17445             cls: 'form-group' //input-group,
17446         };
17447         
17448         var combobox =  {
17449             tag: 'select',
17450             cls : 'roo-ios-select'
17451         };
17452         
17453         if (this.name) {
17454             combobox.name = this.name;
17455         }
17456         
17457         if (this.disabled) {
17458             combobox.disabled = true;
17459         }
17460         
17461         var settings = this;
17462         
17463         ['xs','sm','md','lg'].map(function(size){
17464             if (settings[size]) {
17465                 cfg.cls += ' col-' + size + '-' + settings[size];
17466             }
17467         });
17468         
17469         cfg.cn = combobox;
17470         
17471         return cfg;
17472         
17473     },
17474     
17475     initIOSView : function()
17476     {
17477         this.store.on('load', this.onIOSViewLoad, this);
17478         
17479         return;
17480     },
17481     
17482     onIOSViewLoad : function()
17483     {
17484         if(this.store.getCount() < 1){
17485             return;
17486         }
17487         
17488         this.clearIOSView();
17489         
17490         if(this.allowBlank) {
17491             
17492             var default_text = '-- SELECT --';
17493             
17494             if(this.placeholder.length){
17495                 default_text = this.placeholder;
17496             }
17497             
17498             if(this.emptyTitle.length){
17499                 default_text += ' - ' + this.emptyTitle + ' -';
17500             }
17501             
17502             var opt = this.inputEl().createChild({
17503                 tag: 'option',
17504                 value : 0,
17505                 html : default_text
17506             });
17507             
17508             var o = {};
17509             o[this.valueField] = 0;
17510             o[this.displayField] = default_text;
17511             
17512             this.ios_options.push({
17513                 data : o,
17514                 el : opt
17515             });
17516             
17517         }
17518         
17519         this.store.data.each(function(d, rowIndex){
17520             
17521             var html = '';
17522             
17523             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17524                 html = d.data[this.displayField];
17525             }
17526             
17527             var value = '';
17528             
17529             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17530                 value = d.data[this.valueField];
17531             }
17532             
17533             var option = {
17534                 tag: 'option',
17535                 value : value,
17536                 html : html
17537             };
17538             
17539             if(this.value == d.data[this.valueField]){
17540                 option['selected'] = true;
17541             }
17542             
17543             var opt = this.inputEl().createChild(option);
17544             
17545             this.ios_options.push({
17546                 data : d.data,
17547                 el : opt
17548             });
17549             
17550         }, this);
17551         
17552         this.inputEl().on('change', function(){
17553            this.fireEvent('select', this);
17554         }, this);
17555         
17556     },
17557     
17558     clearIOSView: function()
17559     {
17560         this.inputEl().dom.innerHTML = '';
17561         
17562         this.ios_options = [];
17563     },
17564     
17565     setIOSValue: function(v)
17566     {
17567         this.value = v;
17568         
17569         if(!this.ios_options){
17570             return;
17571         }
17572         
17573         Roo.each(this.ios_options, function(opts){
17574            
17575            opts.el.dom.removeAttribute('selected');
17576            
17577            if(opts.data[this.valueField] != v){
17578                return;
17579            }
17580            
17581            opts.el.dom.setAttribute('selected', true);
17582            
17583         }, this);
17584     }
17585
17586     /** 
17587     * @cfg {Boolean} grow 
17588     * @hide 
17589     */
17590     /** 
17591     * @cfg {Number} growMin 
17592     * @hide 
17593     */
17594     /** 
17595     * @cfg {Number} growMax 
17596     * @hide 
17597     */
17598     /**
17599      * @hide
17600      * @method autoSize
17601      */
17602 });
17603
17604 Roo.apply(Roo.bootstrap.ComboBox,  {
17605     
17606     header : {
17607         tag: 'div',
17608         cls: 'modal-header',
17609         cn: [
17610             {
17611                 tag: 'h4',
17612                 cls: 'modal-title'
17613             }
17614         ]
17615     },
17616     
17617     body : {
17618         tag: 'div',
17619         cls: 'modal-body',
17620         cn: [
17621             {
17622                 tag: 'ul',
17623                 cls: 'list-group'
17624             }
17625         ]
17626     },
17627     
17628     listItemRadio : {
17629         tag: 'li',
17630         cls: 'list-group-item',
17631         cn: [
17632             {
17633                 tag: 'span',
17634                 cls: 'roo-combobox-list-group-item-value'
17635             },
17636             {
17637                 tag: 'div',
17638                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17639                 cn: [
17640                     {
17641                         tag: 'input',
17642                         type: 'radio'
17643                     },
17644                     {
17645                         tag: 'label'
17646                     }
17647                 ]
17648             }
17649         ]
17650     },
17651     
17652     listItemCheckbox : {
17653         tag: 'li',
17654         cls: 'list-group-item',
17655         cn: [
17656             {
17657                 tag: 'span',
17658                 cls: 'roo-combobox-list-group-item-value'
17659             },
17660             {
17661                 tag: 'div',
17662                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17663                 cn: [
17664                     {
17665                         tag: 'input',
17666                         type: 'checkbox'
17667                     },
17668                     {
17669                         tag: 'label'
17670                     }
17671                 ]
17672             }
17673         ]
17674     },
17675     
17676     emptyResult : {
17677         tag: 'div',
17678         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17679     },
17680     
17681     footer : {
17682         tag: 'div',
17683         cls: 'modal-footer',
17684         cn: [
17685             {
17686                 tag: 'div',
17687                 cls: 'row',
17688                 cn: [
17689                     {
17690                         tag: 'div',
17691                         cls: 'col-xs-6 text-left',
17692                         cn: {
17693                             tag: 'button',
17694                             cls: 'btn btn-danger roo-touch-view-cancel',
17695                             html: 'Cancel'
17696                         }
17697                     },
17698                     {
17699                         tag: 'div',
17700                         cls: 'col-xs-6 text-right',
17701                         cn: {
17702                             tag: 'button',
17703                             cls: 'btn btn-success roo-touch-view-ok',
17704                             html: 'OK'
17705                         }
17706                     }
17707                 ]
17708             }
17709         ]
17710         
17711     }
17712 });
17713
17714 Roo.apply(Roo.bootstrap.ComboBox,  {
17715     
17716     touchViewTemplate : {
17717         tag: 'div',
17718         cls: 'modal fade roo-combobox-touch-view',
17719         cn: [
17720             {
17721                 tag: 'div',
17722                 cls: 'modal-dialog',
17723                 style : 'position:fixed', // we have to fix position....
17724                 cn: [
17725                     {
17726                         tag: 'div',
17727                         cls: 'modal-content',
17728                         cn: [
17729                             Roo.bootstrap.ComboBox.header,
17730                             Roo.bootstrap.ComboBox.body,
17731                             Roo.bootstrap.ComboBox.footer
17732                         ]
17733                     }
17734                 ]
17735             }
17736         ]
17737     }
17738 });/*
17739  * Based on:
17740  * Ext JS Library 1.1.1
17741  * Copyright(c) 2006-2007, Ext JS, LLC.
17742  *
17743  * Originally Released Under LGPL - original licence link has changed is not relivant.
17744  *
17745  * Fork - LGPL
17746  * <script type="text/javascript">
17747  */
17748
17749 /**
17750  * @class Roo.View
17751  * @extends Roo.util.Observable
17752  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
17753  * This class also supports single and multi selection modes. <br>
17754  * Create a data model bound view:
17755  <pre><code>
17756  var store = new Roo.data.Store(...);
17757
17758  var view = new Roo.View({
17759     el : "my-element",
17760     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
17761  
17762     singleSelect: true,
17763     selectedClass: "ydataview-selected",
17764     store: store
17765  });
17766
17767  // listen for node click?
17768  view.on("click", function(vw, index, node, e){
17769  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
17770  });
17771
17772  // load XML data
17773  dataModel.load("foobar.xml");
17774  </code></pre>
17775  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
17776  * <br><br>
17777  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
17778  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
17779  * 
17780  * Note: old style constructor is still suported (container, template, config)
17781  * 
17782  * @constructor
17783  * Create a new View
17784  * @param {Object} config The config object
17785  * 
17786  */
17787 Roo.View = function(config, depreciated_tpl, depreciated_config){
17788     
17789     this.parent = false;
17790     
17791     if (typeof(depreciated_tpl) == 'undefined') {
17792         // new way.. - universal constructor.
17793         Roo.apply(this, config);
17794         this.el  = Roo.get(this.el);
17795     } else {
17796         // old format..
17797         this.el  = Roo.get(config);
17798         this.tpl = depreciated_tpl;
17799         Roo.apply(this, depreciated_config);
17800     }
17801     this.wrapEl  = this.el.wrap().wrap();
17802     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
17803     
17804     
17805     if(typeof(this.tpl) == "string"){
17806         this.tpl = new Roo.Template(this.tpl);
17807     } else {
17808         // support xtype ctors..
17809         this.tpl = new Roo.factory(this.tpl, Roo);
17810     }
17811     
17812     
17813     this.tpl.compile();
17814     
17815     /** @private */
17816     this.addEvents({
17817         /**
17818          * @event beforeclick
17819          * Fires before a click is processed. Returns false to cancel the default action.
17820          * @param {Roo.View} this
17821          * @param {Number} index The index of the target node
17822          * @param {HTMLElement} node The target node
17823          * @param {Roo.EventObject} e The raw event object
17824          */
17825             "beforeclick" : true,
17826         /**
17827          * @event click
17828          * Fires when a template node is clicked.
17829          * @param {Roo.View} this
17830          * @param {Number} index The index of the target node
17831          * @param {HTMLElement} node The target node
17832          * @param {Roo.EventObject} e The raw event object
17833          */
17834             "click" : true,
17835         /**
17836          * @event dblclick
17837          * Fires when a template node is double clicked.
17838          * @param {Roo.View} this
17839          * @param {Number} index The index of the target node
17840          * @param {HTMLElement} node The target node
17841          * @param {Roo.EventObject} e The raw event object
17842          */
17843             "dblclick" : true,
17844         /**
17845          * @event contextmenu
17846          * Fires when a template node is right clicked.
17847          * @param {Roo.View} this
17848          * @param {Number} index The index of the target node
17849          * @param {HTMLElement} node The target node
17850          * @param {Roo.EventObject} e The raw event object
17851          */
17852             "contextmenu" : true,
17853         /**
17854          * @event selectionchange
17855          * Fires when the selected nodes change.
17856          * @param {Roo.View} this
17857          * @param {Array} selections Array of the selected nodes
17858          */
17859             "selectionchange" : true,
17860     
17861         /**
17862          * @event beforeselect
17863          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
17864          * @param {Roo.View} this
17865          * @param {HTMLElement} node The node to be selected
17866          * @param {Array} selections Array of currently selected nodes
17867          */
17868             "beforeselect" : true,
17869         /**
17870          * @event preparedata
17871          * Fires on every row to render, to allow you to change the data.
17872          * @param {Roo.View} this
17873          * @param {Object} data to be rendered (change this)
17874          */
17875           "preparedata" : true
17876           
17877           
17878         });
17879
17880
17881
17882     this.el.on({
17883         "click": this.onClick,
17884         "dblclick": this.onDblClick,
17885         "contextmenu": this.onContextMenu,
17886         scope:this
17887     });
17888
17889     this.selections = [];
17890     this.nodes = [];
17891     this.cmp = new Roo.CompositeElementLite([]);
17892     if(this.store){
17893         this.store = Roo.factory(this.store, Roo.data);
17894         this.setStore(this.store, true);
17895     }
17896     
17897     if ( this.footer && this.footer.xtype) {
17898            
17899          var fctr = this.wrapEl.appendChild(document.createElement("div"));
17900         
17901         this.footer.dataSource = this.store;
17902         this.footer.container = fctr;
17903         this.footer = Roo.factory(this.footer, Roo);
17904         fctr.insertFirst(this.el);
17905         
17906         // this is a bit insane - as the paging toolbar seems to detach the el..
17907 //        dom.parentNode.parentNode.parentNode
17908          // they get detached?
17909     }
17910     
17911     
17912     Roo.View.superclass.constructor.call(this);
17913     
17914     
17915 };
17916
17917 Roo.extend(Roo.View, Roo.util.Observable, {
17918     
17919      /**
17920      * @cfg {Roo.data.Store} store Data store to load data from.
17921      */
17922     store : false,
17923     
17924     /**
17925      * @cfg {String|Roo.Element} el The container element.
17926      */
17927     el : '',
17928     
17929     /**
17930      * @cfg {String|Roo.Template} tpl The template used by this View 
17931      */
17932     tpl : false,
17933     /**
17934      * @cfg {String} dataName the named area of the template to use as the data area
17935      *                          Works with domtemplates roo-name="name"
17936      */
17937     dataName: false,
17938     /**
17939      * @cfg {String} selectedClass The css class to add to selected nodes
17940      */
17941     selectedClass : "x-view-selected",
17942      /**
17943      * @cfg {String} emptyText The empty text to show when nothing is loaded.
17944      */
17945     emptyText : "",
17946     
17947     /**
17948      * @cfg {String} text to display on mask (default Loading)
17949      */
17950     mask : false,
17951     /**
17952      * @cfg {Boolean} multiSelect Allow multiple selection
17953      */
17954     multiSelect : false,
17955     /**
17956      * @cfg {Boolean} singleSelect Allow single selection
17957      */
17958     singleSelect:  false,
17959     
17960     /**
17961      * @cfg {Boolean} toggleSelect - selecting 
17962      */
17963     toggleSelect : false,
17964     
17965     /**
17966      * @cfg {Boolean} tickable - selecting 
17967      */
17968     tickable : false,
17969     
17970     /**
17971      * Returns the element this view is bound to.
17972      * @return {Roo.Element}
17973      */
17974     getEl : function(){
17975         return this.wrapEl;
17976     },
17977     
17978     
17979
17980     /**
17981      * Refreshes the view. - called by datachanged on the store. - do not call directly.
17982      */
17983     refresh : function(){
17984         //Roo.log('refresh');
17985         var t = this.tpl;
17986         
17987         // if we are using something like 'domtemplate', then
17988         // the what gets used is:
17989         // t.applySubtemplate(NAME, data, wrapping data..)
17990         // the outer template then get' applied with
17991         //     the store 'extra data'
17992         // and the body get's added to the
17993         //      roo-name="data" node?
17994         //      <span class='roo-tpl-{name}'></span> ?????
17995         
17996         
17997         
17998         this.clearSelections();
17999         this.el.update("");
18000         var html = [];
18001         var records = this.store.getRange();
18002         if(records.length < 1) {
18003             
18004             // is this valid??  = should it render a template??
18005             
18006             this.el.update(this.emptyText);
18007             return;
18008         }
18009         var el = this.el;
18010         if (this.dataName) {
18011             this.el.update(t.apply(this.store.meta)); //????
18012             el = this.el.child('.roo-tpl-' + this.dataName);
18013         }
18014         
18015         for(var i = 0, len = records.length; i < len; i++){
18016             var data = this.prepareData(records[i].data, i, records[i]);
18017             this.fireEvent("preparedata", this, data, i, records[i]);
18018             
18019             var d = Roo.apply({}, data);
18020             
18021             if(this.tickable){
18022                 Roo.apply(d, {'roo-id' : Roo.id()});
18023                 
18024                 var _this = this;
18025             
18026                 Roo.each(this.parent.item, function(item){
18027                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18028                         return;
18029                     }
18030                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18031                 });
18032             }
18033             
18034             html[html.length] = Roo.util.Format.trim(
18035                 this.dataName ?
18036                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18037                     t.apply(d)
18038             );
18039         }
18040         
18041         
18042         
18043         el.update(html.join(""));
18044         this.nodes = el.dom.childNodes;
18045         this.updateIndexes(0);
18046     },
18047     
18048
18049     /**
18050      * Function to override to reformat the data that is sent to
18051      * the template for each node.
18052      * DEPRICATED - use the preparedata event handler.
18053      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18054      * a JSON object for an UpdateManager bound view).
18055      */
18056     prepareData : function(data, index, record)
18057     {
18058         this.fireEvent("preparedata", this, data, index, record);
18059         return data;
18060     },
18061
18062     onUpdate : function(ds, record){
18063         // Roo.log('on update');   
18064         this.clearSelections();
18065         var index = this.store.indexOf(record);
18066         var n = this.nodes[index];
18067         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18068         n.parentNode.removeChild(n);
18069         this.updateIndexes(index, index);
18070     },
18071
18072     
18073     
18074 // --------- FIXME     
18075     onAdd : function(ds, records, index)
18076     {
18077         //Roo.log(['on Add', ds, records, index] );        
18078         this.clearSelections();
18079         if(this.nodes.length == 0){
18080             this.refresh();
18081             return;
18082         }
18083         var n = this.nodes[index];
18084         for(var i = 0, len = records.length; i < len; i++){
18085             var d = this.prepareData(records[i].data, i, records[i]);
18086             if(n){
18087                 this.tpl.insertBefore(n, d);
18088             }else{
18089                 
18090                 this.tpl.append(this.el, d);
18091             }
18092         }
18093         this.updateIndexes(index);
18094     },
18095
18096     onRemove : function(ds, record, index){
18097        // Roo.log('onRemove');
18098         this.clearSelections();
18099         var el = this.dataName  ?
18100             this.el.child('.roo-tpl-' + this.dataName) :
18101             this.el; 
18102         
18103         el.dom.removeChild(this.nodes[index]);
18104         this.updateIndexes(index);
18105     },
18106
18107     /**
18108      * Refresh an individual node.
18109      * @param {Number} index
18110      */
18111     refreshNode : function(index){
18112         this.onUpdate(this.store, this.store.getAt(index));
18113     },
18114
18115     updateIndexes : function(startIndex, endIndex){
18116         var ns = this.nodes;
18117         startIndex = startIndex || 0;
18118         endIndex = endIndex || ns.length - 1;
18119         for(var i = startIndex; i <= endIndex; i++){
18120             ns[i].nodeIndex = i;
18121         }
18122     },
18123
18124     /**
18125      * Changes the data store this view uses and refresh the view.
18126      * @param {Store} store
18127      */
18128     setStore : function(store, initial){
18129         if(!initial && this.store){
18130             this.store.un("datachanged", this.refresh);
18131             this.store.un("add", this.onAdd);
18132             this.store.un("remove", this.onRemove);
18133             this.store.un("update", this.onUpdate);
18134             this.store.un("clear", this.refresh);
18135             this.store.un("beforeload", this.onBeforeLoad);
18136             this.store.un("load", this.onLoad);
18137             this.store.un("loadexception", this.onLoad);
18138         }
18139         if(store){
18140           
18141             store.on("datachanged", this.refresh, this);
18142             store.on("add", this.onAdd, this);
18143             store.on("remove", this.onRemove, this);
18144             store.on("update", this.onUpdate, this);
18145             store.on("clear", this.refresh, this);
18146             store.on("beforeload", this.onBeforeLoad, this);
18147             store.on("load", this.onLoad, this);
18148             store.on("loadexception", this.onLoad, this);
18149         }
18150         
18151         if(store){
18152             this.refresh();
18153         }
18154     },
18155     /**
18156      * onbeforeLoad - masks the loading area.
18157      *
18158      */
18159     onBeforeLoad : function(store,opts)
18160     {
18161          //Roo.log('onBeforeLoad');   
18162         if (!opts.add) {
18163             this.el.update("");
18164         }
18165         this.el.mask(this.mask ? this.mask : "Loading" ); 
18166     },
18167     onLoad : function ()
18168     {
18169         this.el.unmask();
18170     },
18171     
18172
18173     /**
18174      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18175      * @param {HTMLElement} node
18176      * @return {HTMLElement} The template node
18177      */
18178     findItemFromChild : function(node){
18179         var el = this.dataName  ?
18180             this.el.child('.roo-tpl-' + this.dataName,true) :
18181             this.el.dom; 
18182         
18183         if(!node || node.parentNode == el){
18184                     return node;
18185             }
18186             var p = node.parentNode;
18187             while(p && p != el){
18188             if(p.parentNode == el){
18189                 return p;
18190             }
18191             p = p.parentNode;
18192         }
18193             return null;
18194     },
18195
18196     /** @ignore */
18197     onClick : function(e){
18198         var item = this.findItemFromChild(e.getTarget());
18199         if(item){
18200             var index = this.indexOf(item);
18201             if(this.onItemClick(item, index, e) !== false){
18202                 this.fireEvent("click", this, index, item, e);
18203             }
18204         }else{
18205             this.clearSelections();
18206         }
18207     },
18208
18209     /** @ignore */
18210     onContextMenu : function(e){
18211         var item = this.findItemFromChild(e.getTarget());
18212         if(item){
18213             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18214         }
18215     },
18216
18217     /** @ignore */
18218     onDblClick : function(e){
18219         var item = this.findItemFromChild(e.getTarget());
18220         if(item){
18221             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18222         }
18223     },
18224
18225     onItemClick : function(item, index, e)
18226     {
18227         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18228             return false;
18229         }
18230         if (this.toggleSelect) {
18231             var m = this.isSelected(item) ? 'unselect' : 'select';
18232             //Roo.log(m);
18233             var _t = this;
18234             _t[m](item, true, false);
18235             return true;
18236         }
18237         if(this.multiSelect || this.singleSelect){
18238             if(this.multiSelect && e.shiftKey && this.lastSelection){
18239                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18240             }else{
18241                 this.select(item, this.multiSelect && e.ctrlKey);
18242                 this.lastSelection = item;
18243             }
18244             
18245             if(!this.tickable){
18246                 e.preventDefault();
18247             }
18248             
18249         }
18250         return true;
18251     },
18252
18253     /**
18254      * Get the number of selected nodes.
18255      * @return {Number}
18256      */
18257     getSelectionCount : function(){
18258         return this.selections.length;
18259     },
18260
18261     /**
18262      * Get the currently selected nodes.
18263      * @return {Array} An array of HTMLElements
18264      */
18265     getSelectedNodes : function(){
18266         return this.selections;
18267     },
18268
18269     /**
18270      * Get the indexes of the selected nodes.
18271      * @return {Array}
18272      */
18273     getSelectedIndexes : function(){
18274         var indexes = [], s = this.selections;
18275         for(var i = 0, len = s.length; i < len; i++){
18276             indexes.push(s[i].nodeIndex);
18277         }
18278         return indexes;
18279     },
18280
18281     /**
18282      * Clear all selections
18283      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18284      */
18285     clearSelections : function(suppressEvent){
18286         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18287             this.cmp.elements = this.selections;
18288             this.cmp.removeClass(this.selectedClass);
18289             this.selections = [];
18290             if(!suppressEvent){
18291                 this.fireEvent("selectionchange", this, this.selections);
18292             }
18293         }
18294     },
18295
18296     /**
18297      * Returns true if the passed node is selected
18298      * @param {HTMLElement/Number} node The node or node index
18299      * @return {Boolean}
18300      */
18301     isSelected : function(node){
18302         var s = this.selections;
18303         if(s.length < 1){
18304             return false;
18305         }
18306         node = this.getNode(node);
18307         return s.indexOf(node) !== -1;
18308     },
18309
18310     /**
18311      * Selects nodes.
18312      * @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
18313      * @param {Boolean} keepExisting (optional) true to keep existing selections
18314      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18315      */
18316     select : function(nodeInfo, keepExisting, suppressEvent){
18317         if(nodeInfo instanceof Array){
18318             if(!keepExisting){
18319                 this.clearSelections(true);
18320             }
18321             for(var i = 0, len = nodeInfo.length; i < len; i++){
18322                 this.select(nodeInfo[i], true, true);
18323             }
18324             return;
18325         } 
18326         var node = this.getNode(nodeInfo);
18327         if(!node || this.isSelected(node)){
18328             return; // already selected.
18329         }
18330         if(!keepExisting){
18331             this.clearSelections(true);
18332         }
18333         
18334         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18335             Roo.fly(node).addClass(this.selectedClass);
18336             this.selections.push(node);
18337             if(!suppressEvent){
18338                 this.fireEvent("selectionchange", this, this.selections);
18339             }
18340         }
18341         
18342         
18343     },
18344       /**
18345      * Unselects nodes.
18346      * @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
18347      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18348      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18349      */
18350     unselect : function(nodeInfo, keepExisting, suppressEvent)
18351     {
18352         if(nodeInfo instanceof Array){
18353             Roo.each(this.selections, function(s) {
18354                 this.unselect(s, nodeInfo);
18355             }, this);
18356             return;
18357         }
18358         var node = this.getNode(nodeInfo);
18359         if(!node || !this.isSelected(node)){
18360             //Roo.log("not selected");
18361             return; // not selected.
18362         }
18363         // fireevent???
18364         var ns = [];
18365         Roo.each(this.selections, function(s) {
18366             if (s == node ) {
18367                 Roo.fly(node).removeClass(this.selectedClass);
18368
18369                 return;
18370             }
18371             ns.push(s);
18372         },this);
18373         
18374         this.selections= ns;
18375         this.fireEvent("selectionchange", this, this.selections);
18376     },
18377
18378     /**
18379      * Gets a template node.
18380      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18381      * @return {HTMLElement} The node or null if it wasn't found
18382      */
18383     getNode : function(nodeInfo){
18384         if(typeof nodeInfo == "string"){
18385             return document.getElementById(nodeInfo);
18386         }else if(typeof nodeInfo == "number"){
18387             return this.nodes[nodeInfo];
18388         }
18389         return nodeInfo;
18390     },
18391
18392     /**
18393      * Gets a range template nodes.
18394      * @param {Number} startIndex
18395      * @param {Number} endIndex
18396      * @return {Array} An array of nodes
18397      */
18398     getNodes : function(start, end){
18399         var ns = this.nodes;
18400         start = start || 0;
18401         end = typeof end == "undefined" ? ns.length - 1 : end;
18402         var nodes = [];
18403         if(start <= end){
18404             for(var i = start; i <= end; i++){
18405                 nodes.push(ns[i]);
18406             }
18407         } else{
18408             for(var i = start; i >= end; i--){
18409                 nodes.push(ns[i]);
18410             }
18411         }
18412         return nodes;
18413     },
18414
18415     /**
18416      * Finds the index of the passed node
18417      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18418      * @return {Number} The index of the node or -1
18419      */
18420     indexOf : function(node){
18421         node = this.getNode(node);
18422         if(typeof node.nodeIndex == "number"){
18423             return node.nodeIndex;
18424         }
18425         var ns = this.nodes;
18426         for(var i = 0, len = ns.length; i < len; i++){
18427             if(ns[i] == node){
18428                 return i;
18429             }
18430         }
18431         return -1;
18432     }
18433 });
18434 /*
18435  * - LGPL
18436  *
18437  * based on jquery fullcalendar
18438  * 
18439  */
18440
18441 Roo.bootstrap = Roo.bootstrap || {};
18442 /**
18443  * @class Roo.bootstrap.Calendar
18444  * @extends Roo.bootstrap.Component
18445  * Bootstrap Calendar class
18446  * @cfg {Boolean} loadMask (true|false) default false
18447  * @cfg {Object} header generate the user specific header of the calendar, default false
18448
18449  * @constructor
18450  * Create a new Container
18451  * @param {Object} config The config object
18452  */
18453
18454
18455
18456 Roo.bootstrap.Calendar = function(config){
18457     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18458      this.addEvents({
18459         /**
18460              * @event select
18461              * Fires when a date is selected
18462              * @param {DatePicker} this
18463              * @param {Date} date The selected date
18464              */
18465         'select': true,
18466         /**
18467              * @event monthchange
18468              * Fires when the displayed month changes 
18469              * @param {DatePicker} this
18470              * @param {Date} date The selected month
18471              */
18472         'monthchange': true,
18473         /**
18474              * @event evententer
18475              * Fires when mouse over an event
18476              * @param {Calendar} this
18477              * @param {event} Event
18478              */
18479         'evententer': true,
18480         /**
18481              * @event eventleave
18482              * Fires when the mouse leaves an
18483              * @param {Calendar} this
18484              * @param {event}
18485              */
18486         'eventleave': true,
18487         /**
18488              * @event eventclick
18489              * Fires when the mouse click an
18490              * @param {Calendar} this
18491              * @param {event}
18492              */
18493         'eventclick': true
18494         
18495     });
18496
18497 };
18498
18499 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
18500     
18501      /**
18502      * @cfg {Number} startDay
18503      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18504      */
18505     startDay : 0,
18506     
18507     loadMask : false,
18508     
18509     header : false,
18510       
18511     getAutoCreate : function(){
18512         
18513         
18514         var fc_button = function(name, corner, style, content ) {
18515             return Roo.apply({},{
18516                 tag : 'span',
18517                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
18518                          (corner.length ?
18519                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18520                             ''
18521                         ),
18522                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18523                 unselectable: 'on'
18524             });
18525         };
18526         
18527         var header = {};
18528         
18529         if(!this.header){
18530             header = {
18531                 tag : 'table',
18532                 cls : 'fc-header',
18533                 style : 'width:100%',
18534                 cn : [
18535                     {
18536                         tag: 'tr',
18537                         cn : [
18538                             {
18539                                 tag : 'td',
18540                                 cls : 'fc-header-left',
18541                                 cn : [
18542                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
18543                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
18544                                     { tag: 'span', cls: 'fc-header-space' },
18545                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
18546
18547
18548                                 ]
18549                             },
18550
18551                             {
18552                                 tag : 'td',
18553                                 cls : 'fc-header-center',
18554                                 cn : [
18555                                     {
18556                                         tag: 'span',
18557                                         cls: 'fc-header-title',
18558                                         cn : {
18559                                             tag: 'H2',
18560                                             html : 'month / year'
18561                                         }
18562                                     }
18563
18564                                 ]
18565                             },
18566                             {
18567                                 tag : 'td',
18568                                 cls : 'fc-header-right',
18569                                 cn : [
18570                               /*      fc_button('month', 'left', '', 'month' ),
18571                                     fc_button('week', '', '', 'week' ),
18572                                     fc_button('day', 'right', '', 'day' )
18573                                 */    
18574
18575                                 ]
18576                             }
18577
18578                         ]
18579                     }
18580                 ]
18581             };
18582         }
18583         
18584         header = this.header;
18585         
18586        
18587         var cal_heads = function() {
18588             var ret = [];
18589             // fixme - handle this.
18590             
18591             for (var i =0; i < Date.dayNames.length; i++) {
18592                 var d = Date.dayNames[i];
18593                 ret.push({
18594                     tag: 'th',
18595                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18596                     html : d.substring(0,3)
18597                 });
18598                 
18599             }
18600             ret[0].cls += ' fc-first';
18601             ret[6].cls += ' fc-last';
18602             return ret;
18603         };
18604         var cal_cell = function(n) {
18605             return  {
18606                 tag: 'td',
18607                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18608                 cn : [
18609                     {
18610                         cn : [
18611                             {
18612                                 cls: 'fc-day-number',
18613                                 html: 'D'
18614                             },
18615                             {
18616                                 cls: 'fc-day-content',
18617                              
18618                                 cn : [
18619                                      {
18620                                         style: 'position: relative;' // height: 17px;
18621                                     }
18622                                 ]
18623                             }
18624                             
18625                             
18626                         ]
18627                     }
18628                 ]
18629                 
18630             }
18631         };
18632         var cal_rows = function() {
18633             
18634             var ret = [];
18635             for (var r = 0; r < 6; r++) {
18636                 var row= {
18637                     tag : 'tr',
18638                     cls : 'fc-week',
18639                     cn : []
18640                 };
18641                 
18642                 for (var i =0; i < Date.dayNames.length; i++) {
18643                     var d = Date.dayNames[i];
18644                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18645
18646                 }
18647                 row.cn[0].cls+=' fc-first';
18648                 row.cn[0].cn[0].style = 'min-height:90px';
18649                 row.cn[6].cls+=' fc-last';
18650                 ret.push(row);
18651                 
18652             }
18653             ret[0].cls += ' fc-first';
18654             ret[4].cls += ' fc-prev-last';
18655             ret[5].cls += ' fc-last';
18656             return ret;
18657             
18658         };
18659         
18660         var cal_table = {
18661             tag: 'table',
18662             cls: 'fc-border-separate',
18663             style : 'width:100%',
18664             cellspacing  : 0,
18665             cn : [
18666                 { 
18667                     tag: 'thead',
18668                     cn : [
18669                         { 
18670                             tag: 'tr',
18671                             cls : 'fc-first fc-last',
18672                             cn : cal_heads()
18673                         }
18674                     ]
18675                 },
18676                 { 
18677                     tag: 'tbody',
18678                     cn : cal_rows()
18679                 }
18680                   
18681             ]
18682         };
18683          
18684          var cfg = {
18685             cls : 'fc fc-ltr',
18686             cn : [
18687                 header,
18688                 {
18689                     cls : 'fc-content',
18690                     style : "position: relative;",
18691                     cn : [
18692                         {
18693                             cls : 'fc-view fc-view-month fc-grid',
18694                             style : 'position: relative',
18695                             unselectable : 'on',
18696                             cn : [
18697                                 {
18698                                     cls : 'fc-event-container',
18699                                     style : 'position:absolute;z-index:8;top:0;left:0;'
18700                                 },
18701                                 cal_table
18702                             ]
18703                         }
18704                     ]
18705     
18706                 }
18707            ] 
18708             
18709         };
18710         
18711          
18712         
18713         return cfg;
18714     },
18715     
18716     
18717     initEvents : function()
18718     {
18719         if(!this.store){
18720             throw "can not find store for calendar";
18721         }
18722         
18723         var mark = {
18724             tag: "div",
18725             cls:"x-dlg-mask",
18726             style: "text-align:center",
18727             cn: [
18728                 {
18729                     tag: "div",
18730                     style: "background-color:white;width:50%;margin:250 auto",
18731                     cn: [
18732                         {
18733                             tag: "img",
18734                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
18735                         },
18736                         {
18737                             tag: "span",
18738                             html: "Loading"
18739                         }
18740                         
18741                     ]
18742                 }
18743             ]
18744         };
18745         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
18746         
18747         var size = this.el.select('.fc-content', true).first().getSize();
18748         this.maskEl.setSize(size.width, size.height);
18749         this.maskEl.enableDisplayMode("block");
18750         if(!this.loadMask){
18751             this.maskEl.hide();
18752         }
18753         
18754         this.store = Roo.factory(this.store, Roo.data);
18755         this.store.on('load', this.onLoad, this);
18756         this.store.on('beforeload', this.onBeforeLoad, this);
18757         
18758         this.resize();
18759         
18760         this.cells = this.el.select('.fc-day',true);
18761         //Roo.log(this.cells);
18762         this.textNodes = this.el.query('.fc-day-number');
18763         this.cells.addClassOnOver('fc-state-hover');
18764         
18765         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
18766         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
18767         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
18768         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
18769         
18770         this.on('monthchange', this.onMonthChange, this);
18771         
18772         this.update(new Date().clearTime());
18773     },
18774     
18775     resize : function() {
18776         var sz  = this.el.getSize();
18777         
18778         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
18779         this.el.select('.fc-day-content div',true).setHeight(34);
18780     },
18781     
18782     
18783     // private
18784     showPrevMonth : function(e){
18785         this.update(this.activeDate.add("mo", -1));
18786     },
18787     showToday : function(e){
18788         this.update(new Date().clearTime());
18789     },
18790     // private
18791     showNextMonth : function(e){
18792         this.update(this.activeDate.add("mo", 1));
18793     },
18794
18795     // private
18796     showPrevYear : function(){
18797         this.update(this.activeDate.add("y", -1));
18798     },
18799
18800     // private
18801     showNextYear : function(){
18802         this.update(this.activeDate.add("y", 1));
18803     },
18804
18805     
18806    // private
18807     update : function(date)
18808     {
18809         var vd = this.activeDate;
18810         this.activeDate = date;
18811 //        if(vd && this.el){
18812 //            var t = date.getTime();
18813 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
18814 //                Roo.log('using add remove');
18815 //                
18816 //                this.fireEvent('monthchange', this, date);
18817 //                
18818 //                this.cells.removeClass("fc-state-highlight");
18819 //                this.cells.each(function(c){
18820 //                   if(c.dateValue == t){
18821 //                       c.addClass("fc-state-highlight");
18822 //                       setTimeout(function(){
18823 //                            try{c.dom.firstChild.focus();}catch(e){}
18824 //                       }, 50);
18825 //                       return false;
18826 //                   }
18827 //                   return true;
18828 //                });
18829 //                return;
18830 //            }
18831 //        }
18832         
18833         var days = date.getDaysInMonth();
18834         
18835         var firstOfMonth = date.getFirstDateOfMonth();
18836         var startingPos = firstOfMonth.getDay()-this.startDay;
18837         
18838         if(startingPos < this.startDay){
18839             startingPos += 7;
18840         }
18841         
18842         var pm = date.add(Date.MONTH, -1);
18843         var prevStart = pm.getDaysInMonth()-startingPos;
18844 //        
18845         this.cells = this.el.select('.fc-day',true);
18846         this.textNodes = this.el.query('.fc-day-number');
18847         this.cells.addClassOnOver('fc-state-hover');
18848         
18849         var cells = this.cells.elements;
18850         var textEls = this.textNodes;
18851         
18852         Roo.each(cells, function(cell){
18853             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
18854         });
18855         
18856         days += startingPos;
18857
18858         // convert everything to numbers so it's fast
18859         var day = 86400000;
18860         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
18861         //Roo.log(d);
18862         //Roo.log(pm);
18863         //Roo.log(prevStart);
18864         
18865         var today = new Date().clearTime().getTime();
18866         var sel = date.clearTime().getTime();
18867         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
18868         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
18869         var ddMatch = this.disabledDatesRE;
18870         var ddText = this.disabledDatesText;
18871         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
18872         var ddaysText = this.disabledDaysText;
18873         var format = this.format;
18874         
18875         var setCellClass = function(cal, cell){
18876             cell.row = 0;
18877             cell.events = [];
18878             cell.more = [];
18879             //Roo.log('set Cell Class');
18880             cell.title = "";
18881             var t = d.getTime();
18882             
18883             //Roo.log(d);
18884             
18885             cell.dateValue = t;
18886             if(t == today){
18887                 cell.className += " fc-today";
18888                 cell.className += " fc-state-highlight";
18889                 cell.title = cal.todayText;
18890             }
18891             if(t == sel){
18892                 // disable highlight in other month..
18893                 //cell.className += " fc-state-highlight";
18894                 
18895             }
18896             // disabling
18897             if(t < min) {
18898                 cell.className = " fc-state-disabled";
18899                 cell.title = cal.minText;
18900                 return;
18901             }
18902             if(t > max) {
18903                 cell.className = " fc-state-disabled";
18904                 cell.title = cal.maxText;
18905                 return;
18906             }
18907             if(ddays){
18908                 if(ddays.indexOf(d.getDay()) != -1){
18909                     cell.title = ddaysText;
18910                     cell.className = " fc-state-disabled";
18911                 }
18912             }
18913             if(ddMatch && format){
18914                 var fvalue = d.dateFormat(format);
18915                 if(ddMatch.test(fvalue)){
18916                     cell.title = ddText.replace("%0", fvalue);
18917                     cell.className = " fc-state-disabled";
18918                 }
18919             }
18920             
18921             if (!cell.initialClassName) {
18922                 cell.initialClassName = cell.dom.className;
18923             }
18924             
18925             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
18926         };
18927
18928         var i = 0;
18929         
18930         for(; i < startingPos; i++) {
18931             textEls[i].innerHTML = (++prevStart);
18932             d.setDate(d.getDate()+1);
18933             
18934             cells[i].className = "fc-past fc-other-month";
18935             setCellClass(this, cells[i]);
18936         }
18937         
18938         var intDay = 0;
18939         
18940         for(; i < days; i++){
18941             intDay = i - startingPos + 1;
18942             textEls[i].innerHTML = (intDay);
18943             d.setDate(d.getDate()+1);
18944             
18945             cells[i].className = ''; // "x-date-active";
18946             setCellClass(this, cells[i]);
18947         }
18948         var extraDays = 0;
18949         
18950         for(; i < 42; i++) {
18951             textEls[i].innerHTML = (++extraDays);
18952             d.setDate(d.getDate()+1);
18953             
18954             cells[i].className = "fc-future fc-other-month";
18955             setCellClass(this, cells[i]);
18956         }
18957         
18958         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
18959         
18960         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
18961         
18962         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
18963         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
18964         
18965         if(totalRows != 6){
18966             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
18967             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
18968         }
18969         
18970         this.fireEvent('monthchange', this, date);
18971         
18972         
18973         /*
18974         if(!this.internalRender){
18975             var main = this.el.dom.firstChild;
18976             var w = main.offsetWidth;
18977             this.el.setWidth(w + this.el.getBorderWidth("lr"));
18978             Roo.fly(main).setWidth(w);
18979             this.internalRender = true;
18980             // opera does not respect the auto grow header center column
18981             // then, after it gets a width opera refuses to recalculate
18982             // without a second pass
18983             if(Roo.isOpera && !this.secondPass){
18984                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
18985                 this.secondPass = true;
18986                 this.update.defer(10, this, [date]);
18987             }
18988         }
18989         */
18990         
18991     },
18992     
18993     findCell : function(dt) {
18994         dt = dt.clearTime().getTime();
18995         var ret = false;
18996         this.cells.each(function(c){
18997             //Roo.log("check " +c.dateValue + '?=' + dt);
18998             if(c.dateValue == dt){
18999                 ret = c;
19000                 return false;
19001             }
19002             return true;
19003         });
19004         
19005         return ret;
19006     },
19007     
19008     findCells : function(ev) {
19009         var s = ev.start.clone().clearTime().getTime();
19010        // Roo.log(s);
19011         var e= ev.end.clone().clearTime().getTime();
19012        // Roo.log(e);
19013         var ret = [];
19014         this.cells.each(function(c){
19015              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19016             
19017             if(c.dateValue > e){
19018                 return ;
19019             }
19020             if(c.dateValue < s){
19021                 return ;
19022             }
19023             ret.push(c);
19024         });
19025         
19026         return ret;    
19027     },
19028     
19029 //    findBestRow: function(cells)
19030 //    {
19031 //        var ret = 0;
19032 //        
19033 //        for (var i =0 ; i < cells.length;i++) {
19034 //            ret  = Math.max(cells[i].rows || 0,ret);
19035 //        }
19036 //        return ret;
19037 //        
19038 //    },
19039     
19040     
19041     addItem : function(ev)
19042     {
19043         // look for vertical location slot in
19044         var cells = this.findCells(ev);
19045         
19046 //        ev.row = this.findBestRow(cells);
19047         
19048         // work out the location.
19049         
19050         var crow = false;
19051         var rows = [];
19052         for(var i =0; i < cells.length; i++) {
19053             
19054             cells[i].row = cells[0].row;
19055             
19056             if(i == 0){
19057                 cells[i].row = cells[i].row + 1;
19058             }
19059             
19060             if (!crow) {
19061                 crow = {
19062                     start : cells[i],
19063                     end :  cells[i]
19064                 };
19065                 continue;
19066             }
19067             if (crow.start.getY() == cells[i].getY()) {
19068                 // on same row.
19069                 crow.end = cells[i];
19070                 continue;
19071             }
19072             // different row.
19073             rows.push(crow);
19074             crow = {
19075                 start: cells[i],
19076                 end : cells[i]
19077             };
19078             
19079         }
19080         
19081         rows.push(crow);
19082         ev.els = [];
19083         ev.rows = rows;
19084         ev.cells = cells;
19085         
19086         cells[0].events.push(ev);
19087         
19088         this.calevents.push(ev);
19089     },
19090     
19091     clearEvents: function() {
19092         
19093         if(!this.calevents){
19094             return;
19095         }
19096         
19097         Roo.each(this.cells.elements, function(c){
19098             c.row = 0;
19099             c.events = [];
19100             c.more = [];
19101         });
19102         
19103         Roo.each(this.calevents, function(e) {
19104             Roo.each(e.els, function(el) {
19105                 el.un('mouseenter' ,this.onEventEnter, this);
19106                 el.un('mouseleave' ,this.onEventLeave, this);
19107                 el.remove();
19108             },this);
19109         },this);
19110         
19111         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19112             e.remove();
19113         });
19114         
19115     },
19116     
19117     renderEvents: function()
19118     {   
19119         var _this = this;
19120         
19121         this.cells.each(function(c) {
19122             
19123             if(c.row < 5){
19124                 return;
19125             }
19126             
19127             var ev = c.events;
19128             
19129             var r = 4;
19130             if(c.row != c.events.length){
19131                 r = 4 - (4 - (c.row - c.events.length));
19132             }
19133             
19134             c.events = ev.slice(0, r);
19135             c.more = ev.slice(r);
19136             
19137             if(c.more.length && c.more.length == 1){
19138                 c.events.push(c.more.pop());
19139             }
19140             
19141             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19142             
19143         });
19144             
19145         this.cells.each(function(c) {
19146             
19147             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19148             
19149             
19150             for (var e = 0; e < c.events.length; e++){
19151                 var ev = c.events[e];
19152                 var rows = ev.rows;
19153                 
19154                 for(var i = 0; i < rows.length; i++) {
19155                 
19156                     // how many rows should it span..
19157
19158                     var  cfg = {
19159                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19160                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19161
19162                         unselectable : "on",
19163                         cn : [
19164                             {
19165                                 cls: 'fc-event-inner',
19166                                 cn : [
19167     //                                {
19168     //                                  tag:'span',
19169     //                                  cls: 'fc-event-time',
19170     //                                  html : cells.length > 1 ? '' : ev.time
19171     //                                },
19172                                     {
19173                                       tag:'span',
19174                                       cls: 'fc-event-title',
19175                                       html : String.format('{0}', ev.title)
19176                                     }
19177
19178
19179                                 ]
19180                             },
19181                             {
19182                                 cls: 'ui-resizable-handle ui-resizable-e',
19183                                 html : '&nbsp;&nbsp;&nbsp'
19184                             }
19185
19186                         ]
19187                     };
19188
19189                     if (i == 0) {
19190                         cfg.cls += ' fc-event-start';
19191                     }
19192                     if ((i+1) == rows.length) {
19193                         cfg.cls += ' fc-event-end';
19194                     }
19195
19196                     var ctr = _this.el.select('.fc-event-container',true).first();
19197                     var cg = ctr.createChild(cfg);
19198
19199                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19200                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19201
19202                     var r = (c.more.length) ? 1 : 0;
19203                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19204                     cg.setWidth(ebox.right - sbox.x -2);
19205
19206                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19207                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19208                     cg.on('click', _this.onEventClick, _this, ev);
19209
19210                     ev.els.push(cg);
19211                     
19212                 }
19213                 
19214             }
19215             
19216             
19217             if(c.more.length){
19218                 var  cfg = {
19219                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19220                     style : 'position: absolute',
19221                     unselectable : "on",
19222                     cn : [
19223                         {
19224                             cls: 'fc-event-inner',
19225                             cn : [
19226                                 {
19227                                   tag:'span',
19228                                   cls: 'fc-event-title',
19229                                   html : 'More'
19230                                 }
19231
19232
19233                             ]
19234                         },
19235                         {
19236                             cls: 'ui-resizable-handle ui-resizable-e',
19237                             html : '&nbsp;&nbsp;&nbsp'
19238                         }
19239
19240                     ]
19241                 };
19242
19243                 var ctr = _this.el.select('.fc-event-container',true).first();
19244                 var cg = ctr.createChild(cfg);
19245
19246                 var sbox = c.select('.fc-day-content',true).first().getBox();
19247                 var ebox = c.select('.fc-day-content',true).first().getBox();
19248                 //Roo.log(cg);
19249                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19250                 cg.setWidth(ebox.right - sbox.x -2);
19251
19252                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19253                 
19254             }
19255             
19256         });
19257         
19258         
19259         
19260     },
19261     
19262     onEventEnter: function (e, el,event,d) {
19263         this.fireEvent('evententer', this, el, event);
19264     },
19265     
19266     onEventLeave: function (e, el,event,d) {
19267         this.fireEvent('eventleave', this, el, event);
19268     },
19269     
19270     onEventClick: function (e, el,event,d) {
19271         this.fireEvent('eventclick', this, el, event);
19272     },
19273     
19274     onMonthChange: function () {
19275         this.store.load();
19276     },
19277     
19278     onMoreEventClick: function(e, el, more)
19279     {
19280         var _this = this;
19281         
19282         this.calpopover.placement = 'right';
19283         this.calpopover.setTitle('More');
19284         
19285         this.calpopover.setContent('');
19286         
19287         var ctr = this.calpopover.el.select('.popover-content', true).first();
19288         
19289         Roo.each(more, function(m){
19290             var cfg = {
19291                 cls : 'fc-event-hori fc-event-draggable',
19292                 html : m.title
19293             };
19294             var cg = ctr.createChild(cfg);
19295             
19296             cg.on('click', _this.onEventClick, _this, m);
19297         });
19298         
19299         this.calpopover.show(el);
19300         
19301         
19302     },
19303     
19304     onLoad: function () 
19305     {   
19306         this.calevents = [];
19307         var cal = this;
19308         
19309         if(this.store.getCount() > 0){
19310             this.store.data.each(function(d){
19311                cal.addItem({
19312                     id : d.data.id,
19313                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19314                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19315                     time : d.data.start_time,
19316                     title : d.data.title,
19317                     description : d.data.description,
19318                     venue : d.data.venue
19319                 });
19320             });
19321         }
19322         
19323         this.renderEvents();
19324         
19325         if(this.calevents.length && this.loadMask){
19326             this.maskEl.hide();
19327         }
19328     },
19329     
19330     onBeforeLoad: function()
19331     {
19332         this.clearEvents();
19333         if(this.loadMask){
19334             this.maskEl.show();
19335         }
19336     }
19337 });
19338
19339  
19340  /*
19341  * - LGPL
19342  *
19343  * element
19344  * 
19345  */
19346
19347 /**
19348  * @class Roo.bootstrap.Popover
19349  * @extends Roo.bootstrap.Component
19350  * Bootstrap Popover class
19351  * @cfg {String} html contents of the popover   (or false to use children..)
19352  * @cfg {String} title of popover (or false to hide)
19353  * @cfg {String} placement how it is placed
19354  * @cfg {String} trigger click || hover (or false to trigger manually)
19355  * @cfg {String} over what (parent or false to trigger manually.)
19356  * @cfg {Number} delay - delay before showing
19357  
19358  * @constructor
19359  * Create a new Popover
19360  * @param {Object} config The config object
19361  */
19362
19363 Roo.bootstrap.Popover = function(config){
19364     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19365     
19366     this.addEvents({
19367         // raw events
19368          /**
19369          * @event show
19370          * After the popover show
19371          * 
19372          * @param {Roo.bootstrap.Popover} this
19373          */
19374         "show" : true,
19375         /**
19376          * @event hide
19377          * After the popover hide
19378          * 
19379          * @param {Roo.bootstrap.Popover} this
19380          */
19381         "hide" : true
19382     });
19383 };
19384
19385 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
19386     
19387     title: 'Fill in a title',
19388     html: false,
19389     
19390     placement : 'right',
19391     trigger : 'hover', // hover
19392     
19393     delay : 0,
19394     
19395     over: 'parent',
19396     
19397     can_build_overlaid : false,
19398     
19399     getChildContainer : function()
19400     {
19401         return this.el.select('.popover-content',true).first();
19402     },
19403     
19404     getAutoCreate : function(){
19405          
19406         var cfg = {
19407            cls : 'popover roo-dynamic',
19408            style: 'display:block',
19409            cn : [
19410                 {
19411                     cls : 'arrow'
19412                 },
19413                 {
19414                     cls : 'popover-inner',
19415                     cn : [
19416                         {
19417                             tag: 'h3',
19418                             cls: 'popover-title popover-header',
19419                             html : this.title
19420                         },
19421                         {
19422                             cls : 'popover-content popover-body',
19423                             html : this.html
19424                         }
19425                     ]
19426                     
19427                 }
19428            ]
19429         };
19430         
19431         return cfg;
19432     },
19433     setTitle: function(str)
19434     {
19435         this.title = str;
19436         this.el.select('.popover-title',true).first().dom.innerHTML = str;
19437     },
19438     setContent: function(str)
19439     {
19440         this.html = str;
19441         this.el.select('.popover-content',true).first().dom.innerHTML = str;
19442     },
19443     // as it get's added to the bottom of the page.
19444     onRender : function(ct, position)
19445     {
19446         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19447         if(!this.el){
19448             var cfg = Roo.apply({},  this.getAutoCreate());
19449             cfg.id = Roo.id();
19450             
19451             if (this.cls) {
19452                 cfg.cls += ' ' + this.cls;
19453             }
19454             if (this.style) {
19455                 cfg.style = this.style;
19456             }
19457             //Roo.log("adding to ");
19458             this.el = Roo.get(document.body).createChild(cfg, position);
19459 //            Roo.log(this.el);
19460         }
19461         this.initEvents();
19462     },
19463     
19464     initEvents : function()
19465     {
19466         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
19467         this.el.enableDisplayMode('block');
19468         this.el.hide();
19469         if (this.over === false) {
19470             return; 
19471         }
19472         if (this.triggers === false) {
19473             return;
19474         }
19475         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
19476         var triggers = this.trigger ? this.trigger.split(' ') : [];
19477         Roo.each(triggers, function(trigger) {
19478         
19479             if (trigger == 'click') {
19480                 on_el.on('click', this.toggle, this);
19481             } else if (trigger != 'manual') {
19482                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
19483                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19484       
19485                 on_el.on(eventIn  ,this.enter, this);
19486                 on_el.on(eventOut, this.leave, this);
19487             }
19488         }, this);
19489         
19490     },
19491     
19492     
19493     // private
19494     timeout : null,
19495     hoverState : null,
19496     
19497     toggle : function () {
19498         this.hoverState == 'in' ? this.leave() : this.enter();
19499     },
19500     
19501     enter : function () {
19502         
19503         clearTimeout(this.timeout);
19504     
19505         this.hoverState = 'in';
19506     
19507         if (!this.delay || !this.delay.show) {
19508             this.show();
19509             return;
19510         }
19511         var _t = this;
19512         this.timeout = setTimeout(function () {
19513             if (_t.hoverState == 'in') {
19514                 _t.show();
19515             }
19516         }, this.delay.show)
19517     },
19518     
19519     leave : function() {
19520         clearTimeout(this.timeout);
19521     
19522         this.hoverState = 'out';
19523     
19524         if (!this.delay || !this.delay.hide) {
19525             this.hide();
19526             return;
19527         }
19528         var _t = this;
19529         this.timeout = setTimeout(function () {
19530             if (_t.hoverState == 'out') {
19531                 _t.hide();
19532             }
19533         }, this.delay.hide)
19534     },
19535     
19536     show : function (on_el)
19537     {
19538         if (!on_el) {
19539             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
19540         }
19541         
19542         // set content.
19543         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
19544         if (this.html !== false) {
19545             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
19546         }
19547         this.el.removeClass([
19548             'fade','top','bottom', 'left', 'right','in',
19549             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19550         ]);
19551         if (!this.title.length) {
19552             this.el.select('.popover-title',true).hide();
19553         }
19554         
19555         var placement = typeof this.placement == 'function' ?
19556             this.placement.call(this, this.el, on_el) :
19557             this.placement;
19558             
19559         var autoToken = /\s?auto?\s?/i;
19560         var autoPlace = autoToken.test(placement);
19561         if (autoPlace) {
19562             placement = placement.replace(autoToken, '') || 'top';
19563         }
19564         
19565         //this.el.detach()
19566         //this.el.setXY([0,0]);
19567         this.el.show();
19568         this.el.dom.style.display='block';
19569         this.el.addClass(placement);
19570         
19571         //this.el.appendTo(on_el);
19572         
19573         var p = this.getPosition();
19574         var box = this.el.getBox();
19575         
19576         if (autoPlace) {
19577             // fixme..
19578         }
19579         var align = Roo.bootstrap.Popover.alignment[placement];
19580         
19581 //        Roo.log(align);
19582         this.el.alignTo(on_el, align[0],align[1]);
19583         //var arrow = this.el.select('.arrow',true).first();
19584         //arrow.set(align[2], 
19585         
19586         this.el.addClass('in');
19587         
19588         
19589         if (this.el.hasClass('fade')) {
19590             // fade it?
19591         }
19592         
19593         this.hoverState = 'in';
19594         
19595         this.fireEvent('show', this);
19596         
19597     },
19598     hide : function()
19599     {
19600         this.el.setXY([0,0]);
19601         this.el.removeClass('in');
19602         this.el.hide();
19603         this.hoverState = null;
19604         
19605         this.fireEvent('hide', this);
19606     }
19607     
19608 });
19609
19610 Roo.bootstrap.Popover.alignment = {
19611     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
19612     'right' : ['l-r', [10,0], 'left bs-popover-left'],
19613     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
19614     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
19615 };
19616
19617  /*
19618  * - LGPL
19619  *
19620  * Progress
19621  * 
19622  */
19623
19624 /**
19625  * @class Roo.bootstrap.Progress
19626  * @extends Roo.bootstrap.Component
19627  * Bootstrap Progress class
19628  * @cfg {Boolean} striped striped of the progress bar
19629  * @cfg {Boolean} active animated of the progress bar
19630  * 
19631  * 
19632  * @constructor
19633  * Create a new Progress
19634  * @param {Object} config The config object
19635  */
19636
19637 Roo.bootstrap.Progress = function(config){
19638     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
19639 };
19640
19641 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
19642     
19643     striped : false,
19644     active: false,
19645     
19646     getAutoCreate : function(){
19647         var cfg = {
19648             tag: 'div',
19649             cls: 'progress'
19650         };
19651         
19652         
19653         if(this.striped){
19654             cfg.cls += ' progress-striped';
19655         }
19656       
19657         if(this.active){
19658             cfg.cls += ' active';
19659         }
19660         
19661         
19662         return cfg;
19663     }
19664    
19665 });
19666
19667  
19668
19669  /*
19670  * - LGPL
19671  *
19672  * ProgressBar
19673  * 
19674  */
19675
19676 /**
19677  * @class Roo.bootstrap.ProgressBar
19678  * @extends Roo.bootstrap.Component
19679  * Bootstrap ProgressBar class
19680  * @cfg {Number} aria_valuenow aria-value now
19681  * @cfg {Number} aria_valuemin aria-value min
19682  * @cfg {Number} aria_valuemax aria-value max
19683  * @cfg {String} label label for the progress bar
19684  * @cfg {String} panel (success | info | warning | danger )
19685  * @cfg {String} role role of the progress bar
19686  * @cfg {String} sr_only text
19687  * 
19688  * 
19689  * @constructor
19690  * Create a new ProgressBar
19691  * @param {Object} config The config object
19692  */
19693
19694 Roo.bootstrap.ProgressBar = function(config){
19695     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
19696 };
19697
19698 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
19699     
19700     aria_valuenow : 0,
19701     aria_valuemin : 0,
19702     aria_valuemax : 100,
19703     label : false,
19704     panel : false,
19705     role : false,
19706     sr_only: false,
19707     
19708     getAutoCreate : function()
19709     {
19710         
19711         var cfg = {
19712             tag: 'div',
19713             cls: 'progress-bar',
19714             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
19715         };
19716         
19717         if(this.sr_only){
19718             cfg.cn = {
19719                 tag: 'span',
19720                 cls: 'sr-only',
19721                 html: this.sr_only
19722             }
19723         }
19724         
19725         if(this.role){
19726             cfg.role = this.role;
19727         }
19728         
19729         if(this.aria_valuenow){
19730             cfg['aria-valuenow'] = this.aria_valuenow;
19731         }
19732         
19733         if(this.aria_valuemin){
19734             cfg['aria-valuemin'] = this.aria_valuemin;
19735         }
19736         
19737         if(this.aria_valuemax){
19738             cfg['aria-valuemax'] = this.aria_valuemax;
19739         }
19740         
19741         if(this.label && !this.sr_only){
19742             cfg.html = this.label;
19743         }
19744         
19745         if(this.panel){
19746             cfg.cls += ' progress-bar-' + this.panel;
19747         }
19748         
19749         return cfg;
19750     },
19751     
19752     update : function(aria_valuenow)
19753     {
19754         this.aria_valuenow = aria_valuenow;
19755         
19756         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
19757     }
19758    
19759 });
19760
19761  
19762
19763  /*
19764  * - LGPL
19765  *
19766  * column
19767  * 
19768  */
19769
19770 /**
19771  * @class Roo.bootstrap.TabGroup
19772  * @extends Roo.bootstrap.Column
19773  * Bootstrap Column class
19774  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
19775  * @cfg {Boolean} carousel true to make the group behave like a carousel
19776  * @cfg {Boolean} bullets show bullets for the panels
19777  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
19778  * @cfg {Number} timer auto slide timer .. default 0 millisecond
19779  * @cfg {Boolean} showarrow (true|false) show arrow default true
19780  * 
19781  * @constructor
19782  * Create a new TabGroup
19783  * @param {Object} config The config object
19784  */
19785
19786 Roo.bootstrap.TabGroup = function(config){
19787     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
19788     if (!this.navId) {
19789         this.navId = Roo.id();
19790     }
19791     this.tabs = [];
19792     Roo.bootstrap.TabGroup.register(this);
19793     
19794 };
19795
19796 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
19797     
19798     carousel : false,
19799     transition : false,
19800     bullets : 0,
19801     timer : 0,
19802     autoslide : false,
19803     slideFn : false,
19804     slideOnTouch : false,
19805     showarrow : true,
19806     
19807     getAutoCreate : function()
19808     {
19809         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
19810         
19811         cfg.cls += ' tab-content';
19812         
19813         if (this.carousel) {
19814             cfg.cls += ' carousel slide';
19815             
19816             cfg.cn = [{
19817                cls : 'carousel-inner',
19818                cn : []
19819             }];
19820         
19821             if(this.bullets  && !Roo.isTouch){
19822                 
19823                 var bullets = {
19824                     cls : 'carousel-bullets',
19825                     cn : []
19826                 };
19827                
19828                 if(this.bullets_cls){
19829                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
19830                 }
19831                 
19832                 bullets.cn.push({
19833                     cls : 'clear'
19834                 });
19835                 
19836                 cfg.cn[0].cn.push(bullets);
19837             }
19838             
19839             if(this.showarrow){
19840                 cfg.cn[0].cn.push({
19841                     tag : 'div',
19842                     class : 'carousel-arrow',
19843                     cn : [
19844                         {
19845                             tag : 'div',
19846                             class : 'carousel-prev',
19847                             cn : [
19848                                 {
19849                                     tag : 'i',
19850                                     class : 'fa fa-chevron-left'
19851                                 }
19852                             ]
19853                         },
19854                         {
19855                             tag : 'div',
19856                             class : 'carousel-next',
19857                             cn : [
19858                                 {
19859                                     tag : 'i',
19860                                     class : 'fa fa-chevron-right'
19861                                 }
19862                             ]
19863                         }
19864                     ]
19865                 });
19866             }
19867             
19868         }
19869         
19870         return cfg;
19871     },
19872     
19873     initEvents:  function()
19874     {
19875 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
19876 //            this.el.on("touchstart", this.onTouchStart, this);
19877 //        }
19878         
19879         if(this.autoslide){
19880             var _this = this;
19881             
19882             this.slideFn = window.setInterval(function() {
19883                 _this.showPanelNext();
19884             }, this.timer);
19885         }
19886         
19887         if(this.showarrow){
19888             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
19889             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
19890         }
19891         
19892         
19893     },
19894     
19895 //    onTouchStart : function(e, el, o)
19896 //    {
19897 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
19898 //            return;
19899 //        }
19900 //        
19901 //        this.showPanelNext();
19902 //    },
19903     
19904     
19905     getChildContainer : function()
19906     {
19907         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
19908     },
19909     
19910     /**
19911     * register a Navigation item
19912     * @param {Roo.bootstrap.NavItem} the navitem to add
19913     */
19914     register : function(item)
19915     {
19916         this.tabs.push( item);
19917         item.navId = this.navId; // not really needed..
19918         this.addBullet();
19919     
19920     },
19921     
19922     getActivePanel : function()
19923     {
19924         var r = false;
19925         Roo.each(this.tabs, function(t) {
19926             if (t.active) {
19927                 r = t;
19928                 return false;
19929             }
19930             return null;
19931         });
19932         return r;
19933         
19934     },
19935     getPanelByName : function(n)
19936     {
19937         var r = false;
19938         Roo.each(this.tabs, function(t) {
19939             if (t.tabId == n) {
19940                 r = t;
19941                 return false;
19942             }
19943             return null;
19944         });
19945         return r;
19946     },
19947     indexOfPanel : function(p)
19948     {
19949         var r = false;
19950         Roo.each(this.tabs, function(t,i) {
19951             if (t.tabId == p.tabId) {
19952                 r = i;
19953                 return false;
19954             }
19955             return null;
19956         });
19957         return r;
19958     },
19959     /**
19960      * show a specific panel
19961      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
19962      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
19963      */
19964     showPanel : function (pan)
19965     {
19966         if(this.transition || typeof(pan) == 'undefined'){
19967             Roo.log("waiting for the transitionend");
19968             return false;
19969         }
19970         
19971         if (typeof(pan) == 'number') {
19972             pan = this.tabs[pan];
19973         }
19974         
19975         if (typeof(pan) == 'string') {
19976             pan = this.getPanelByName(pan);
19977         }
19978         
19979         var cur = this.getActivePanel();
19980         
19981         if(!pan || !cur){
19982             Roo.log('pan or acitve pan is undefined');
19983             return false;
19984         }
19985         
19986         if (pan.tabId == this.getActivePanel().tabId) {
19987             return true;
19988         }
19989         
19990         if (false === cur.fireEvent('beforedeactivate')) {
19991             return false;
19992         }
19993         
19994         if(this.bullets > 0 && !Roo.isTouch){
19995             this.setActiveBullet(this.indexOfPanel(pan));
19996         }
19997         
19998         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
19999             
20000             //class="carousel-item carousel-item-next carousel-item-left"
20001             
20002             this.transition = true;
20003             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20004             var lr = dir == 'next' ? 'left' : 'right';
20005             pan.el.addClass(dir); // or prev
20006             pan.el.addClass('carousel-item-' + dir); // or prev
20007             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20008             cur.el.addClass(lr); // or right
20009             pan.el.addClass(lr);
20010             cur.el.addClass('carousel-item-' +lr); // or right
20011             pan.el.addClass('carousel-item-' +lr);
20012             
20013             
20014             var _this = this;
20015             cur.el.on('transitionend', function() {
20016                 Roo.log("trans end?");
20017                 
20018                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20019                 pan.setActive(true);
20020                 
20021                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20022                 cur.setActive(false);
20023                 
20024                 _this.transition = false;
20025                 
20026             }, this, { single:  true } );
20027             
20028             return true;
20029         }
20030         
20031         cur.setActive(false);
20032         pan.setActive(true);
20033         
20034         return true;
20035         
20036     },
20037     showPanelNext : function()
20038     {
20039         var i = this.indexOfPanel(this.getActivePanel());
20040         
20041         if (i >= this.tabs.length - 1 && !this.autoslide) {
20042             return;
20043         }
20044         
20045         if (i >= this.tabs.length - 1 && this.autoslide) {
20046             i = -1;
20047         }
20048         
20049         this.showPanel(this.tabs[i+1]);
20050     },
20051     
20052     showPanelPrev : function()
20053     {
20054         var i = this.indexOfPanel(this.getActivePanel());
20055         
20056         if (i  < 1 && !this.autoslide) {
20057             return;
20058         }
20059         
20060         if (i < 1 && this.autoslide) {
20061             i = this.tabs.length;
20062         }
20063         
20064         this.showPanel(this.tabs[i-1]);
20065     },
20066     
20067     
20068     addBullet: function()
20069     {
20070         if(!this.bullets || Roo.isTouch){
20071             return;
20072         }
20073         var ctr = this.el.select('.carousel-bullets',true).first();
20074         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20075         var bullet = ctr.createChild({
20076             cls : 'bullet bullet-' + i
20077         },ctr.dom.lastChild);
20078         
20079         
20080         var _this = this;
20081         
20082         bullet.on('click', (function(e, el, o, ii, t){
20083
20084             e.preventDefault();
20085
20086             this.showPanel(ii);
20087
20088             if(this.autoslide && this.slideFn){
20089                 clearInterval(this.slideFn);
20090                 this.slideFn = window.setInterval(function() {
20091                     _this.showPanelNext();
20092                 }, this.timer);
20093             }
20094
20095         }).createDelegate(this, [i, bullet], true));
20096                 
20097         
20098     },
20099      
20100     setActiveBullet : function(i)
20101     {
20102         if(Roo.isTouch){
20103             return;
20104         }
20105         
20106         Roo.each(this.el.select('.bullet', true).elements, function(el){
20107             el.removeClass('selected');
20108         });
20109
20110         var bullet = this.el.select('.bullet-' + i, true).first();
20111         
20112         if(!bullet){
20113             return;
20114         }
20115         
20116         bullet.addClass('selected');
20117     }
20118     
20119     
20120   
20121 });
20122
20123  
20124
20125  
20126  
20127 Roo.apply(Roo.bootstrap.TabGroup, {
20128     
20129     groups: {},
20130      /**
20131     * register a Navigation Group
20132     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20133     */
20134     register : function(navgrp)
20135     {
20136         this.groups[navgrp.navId] = navgrp;
20137         
20138     },
20139     /**
20140     * fetch a Navigation Group based on the navigation ID
20141     * if one does not exist , it will get created.
20142     * @param {string} the navgroup to add
20143     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20144     */
20145     get: function(navId) {
20146         if (typeof(this.groups[navId]) == 'undefined') {
20147             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20148         }
20149         return this.groups[navId] ;
20150     }
20151     
20152     
20153     
20154 });
20155
20156  /*
20157  * - LGPL
20158  *
20159  * TabPanel
20160  * 
20161  */
20162
20163 /**
20164  * @class Roo.bootstrap.TabPanel
20165  * @extends Roo.bootstrap.Component
20166  * Bootstrap TabPanel class
20167  * @cfg {Boolean} active panel active
20168  * @cfg {String} html panel content
20169  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20170  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20171  * @cfg {String} href click to link..
20172  * 
20173  * 
20174  * @constructor
20175  * Create a new TabPanel
20176  * @param {Object} config The config object
20177  */
20178
20179 Roo.bootstrap.TabPanel = function(config){
20180     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20181     this.addEvents({
20182         /**
20183              * @event changed
20184              * Fires when the active status changes
20185              * @param {Roo.bootstrap.TabPanel} this
20186              * @param {Boolean} state the new state
20187             
20188          */
20189         'changed': true,
20190         /**
20191              * @event beforedeactivate
20192              * Fires before a tab is de-activated - can be used to do validation on a form.
20193              * @param {Roo.bootstrap.TabPanel} this
20194              * @return {Boolean} false if there is an error
20195             
20196          */
20197         'beforedeactivate': true
20198      });
20199     
20200     this.tabId = this.tabId || Roo.id();
20201   
20202 };
20203
20204 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
20205     
20206     active: false,
20207     html: false,
20208     tabId: false,
20209     navId : false,
20210     href : '',
20211     
20212     getAutoCreate : function(){
20213         
20214         
20215         var cfg = {
20216             tag: 'div',
20217             // item is needed for carousel - not sure if it has any effect otherwise
20218             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20219             html: this.html || ''
20220         };
20221         
20222         if(this.active){
20223             cfg.cls += ' active';
20224         }
20225         
20226         if(this.tabId){
20227             cfg.tabId = this.tabId;
20228         }
20229         
20230         
20231         
20232         return cfg;
20233     },
20234     
20235     initEvents:  function()
20236     {
20237         var p = this.parent();
20238         
20239         this.navId = this.navId || p.navId;
20240         
20241         if (typeof(this.navId) != 'undefined') {
20242             // not really needed.. but just in case.. parent should be a NavGroup.
20243             var tg = Roo.bootstrap.TabGroup.get(this.navId);
20244             
20245             tg.register(this);
20246             
20247             var i = tg.tabs.length - 1;
20248             
20249             if(this.active && tg.bullets > 0 && i < tg.bullets){
20250                 tg.setActiveBullet(i);
20251             }
20252         }
20253         
20254         this.el.on('click', this.onClick, this);
20255         
20256         if(Roo.isTouch){
20257             this.el.on("touchstart", this.onTouchStart, this);
20258             this.el.on("touchmove", this.onTouchMove, this);
20259             this.el.on("touchend", this.onTouchEnd, this);
20260         }
20261         
20262     },
20263     
20264     onRender : function(ct, position)
20265     {
20266         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20267     },
20268     
20269     setActive : function(state)
20270     {
20271         Roo.log("panel - set active " + this.tabId + "=" + state);
20272         
20273         this.active = state;
20274         if (!state) {
20275             this.el.removeClass('active');
20276             
20277         } else  if (!this.el.hasClass('active')) {
20278             this.el.addClass('active');
20279         }
20280         
20281         this.fireEvent('changed', this, state);
20282     },
20283     
20284     onClick : function(e)
20285     {
20286         e.preventDefault();
20287         
20288         if(!this.href.length){
20289             return;
20290         }
20291         
20292         window.location.href = this.href;
20293     },
20294     
20295     startX : 0,
20296     startY : 0,
20297     endX : 0,
20298     endY : 0,
20299     swiping : false,
20300     
20301     onTouchStart : function(e)
20302     {
20303         this.swiping = false;
20304         
20305         this.startX = e.browserEvent.touches[0].clientX;
20306         this.startY = e.browserEvent.touches[0].clientY;
20307     },
20308     
20309     onTouchMove : function(e)
20310     {
20311         this.swiping = true;
20312         
20313         this.endX = e.browserEvent.touches[0].clientX;
20314         this.endY = e.browserEvent.touches[0].clientY;
20315     },
20316     
20317     onTouchEnd : function(e)
20318     {
20319         if(!this.swiping){
20320             this.onClick(e);
20321             return;
20322         }
20323         
20324         var tabGroup = this.parent();
20325         
20326         if(this.endX > this.startX){ // swiping right
20327             tabGroup.showPanelPrev();
20328             return;
20329         }
20330         
20331         if(this.startX > this.endX){ // swiping left
20332             tabGroup.showPanelNext();
20333             return;
20334         }
20335     }
20336     
20337     
20338 });
20339  
20340
20341  
20342
20343  /*
20344  * - LGPL
20345  *
20346  * DateField
20347  * 
20348  */
20349
20350 /**
20351  * @class Roo.bootstrap.DateField
20352  * @extends Roo.bootstrap.Input
20353  * Bootstrap DateField class
20354  * @cfg {Number} weekStart default 0
20355  * @cfg {String} viewMode default empty, (months|years)
20356  * @cfg {String} minViewMode default empty, (months|years)
20357  * @cfg {Number} startDate default -Infinity
20358  * @cfg {Number} endDate default Infinity
20359  * @cfg {Boolean} todayHighlight default false
20360  * @cfg {Boolean} todayBtn default false
20361  * @cfg {Boolean} calendarWeeks default false
20362  * @cfg {Object} daysOfWeekDisabled default empty
20363  * @cfg {Boolean} singleMode default false (true | false)
20364  * 
20365  * @cfg {Boolean} keyboardNavigation default true
20366  * @cfg {String} language default en
20367  * 
20368  * @constructor
20369  * Create a new DateField
20370  * @param {Object} config The config object
20371  */
20372
20373 Roo.bootstrap.DateField = function(config){
20374     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20375      this.addEvents({
20376             /**
20377              * @event show
20378              * Fires when this field show.
20379              * @param {Roo.bootstrap.DateField} this
20380              * @param {Mixed} date The date value
20381              */
20382             show : true,
20383             /**
20384              * @event show
20385              * Fires when this field hide.
20386              * @param {Roo.bootstrap.DateField} this
20387              * @param {Mixed} date The date value
20388              */
20389             hide : true,
20390             /**
20391              * @event select
20392              * Fires when select a date.
20393              * @param {Roo.bootstrap.DateField} this
20394              * @param {Mixed} date The date value
20395              */
20396             select : true,
20397             /**
20398              * @event beforeselect
20399              * Fires when before select a date.
20400              * @param {Roo.bootstrap.DateField} this
20401              * @param {Mixed} date The date value
20402              */
20403             beforeselect : true
20404         });
20405 };
20406
20407 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
20408     
20409     /**
20410      * @cfg {String} format
20411      * The default date format string which can be overriden for localization support.  The format must be
20412      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20413      */
20414     format : "m/d/y",
20415     /**
20416      * @cfg {String} altFormats
20417      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20418      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20419      */
20420     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20421     
20422     weekStart : 0,
20423     
20424     viewMode : '',
20425     
20426     minViewMode : '',
20427     
20428     todayHighlight : false,
20429     
20430     todayBtn: false,
20431     
20432     language: 'en',
20433     
20434     keyboardNavigation: true,
20435     
20436     calendarWeeks: false,
20437     
20438     startDate: -Infinity,
20439     
20440     endDate: Infinity,
20441     
20442     daysOfWeekDisabled: [],
20443     
20444     _events: [],
20445     
20446     singleMode : false,
20447     
20448     UTCDate: function()
20449     {
20450         return new Date(Date.UTC.apply(Date, arguments));
20451     },
20452     
20453     UTCToday: function()
20454     {
20455         var today = new Date();
20456         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20457     },
20458     
20459     getDate: function() {
20460             var d = this.getUTCDate();
20461             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20462     },
20463     
20464     getUTCDate: function() {
20465             return this.date;
20466     },
20467     
20468     setDate: function(d) {
20469             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20470     },
20471     
20472     setUTCDate: function(d) {
20473             this.date = d;
20474             this.setValue(this.formatDate(this.date));
20475     },
20476         
20477     onRender: function(ct, position)
20478     {
20479         
20480         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20481         
20482         this.language = this.language || 'en';
20483         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20484         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20485         
20486         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
20487         this.format = this.format || 'm/d/y';
20488         this.isInline = false;
20489         this.isInput = true;
20490         this.component = this.el.select('.add-on', true).first() || false;
20491         this.component = (this.component && this.component.length === 0) ? false : this.component;
20492         this.hasInput = this.component && this.inputEl().length;
20493         
20494         if (typeof(this.minViewMode === 'string')) {
20495             switch (this.minViewMode) {
20496                 case 'months':
20497                     this.minViewMode = 1;
20498                     break;
20499                 case 'years':
20500                     this.minViewMode = 2;
20501                     break;
20502                 default:
20503                     this.minViewMode = 0;
20504                     break;
20505             }
20506         }
20507         
20508         if (typeof(this.viewMode === 'string')) {
20509             switch (this.viewMode) {
20510                 case 'months':
20511                     this.viewMode = 1;
20512                     break;
20513                 case 'years':
20514                     this.viewMode = 2;
20515                     break;
20516                 default:
20517                     this.viewMode = 0;
20518                     break;
20519             }
20520         }
20521                 
20522         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
20523         
20524 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
20525         
20526         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20527         
20528         this.picker().on('mousedown', this.onMousedown, this);
20529         this.picker().on('click', this.onClick, this);
20530         
20531         this.picker().addClass('datepicker-dropdown');
20532         
20533         this.startViewMode = this.viewMode;
20534         
20535         if(this.singleMode){
20536             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
20537                 v.setVisibilityMode(Roo.Element.DISPLAY);
20538                 v.hide();
20539             });
20540             
20541             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20542                 v.setStyle('width', '189px');
20543             });
20544         }
20545         
20546         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
20547             if(!this.calendarWeeks){
20548                 v.remove();
20549                 return;
20550             }
20551             
20552             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20553             v.attr('colspan', function(i, val){
20554                 return parseInt(val) + 1;
20555             });
20556         });
20557                         
20558         
20559         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
20560         
20561         this.setStartDate(this.startDate);
20562         this.setEndDate(this.endDate);
20563         
20564         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
20565         
20566         this.fillDow();
20567         this.fillMonths();
20568         this.update();
20569         this.showMode();
20570         
20571         if(this.isInline) {
20572             this.showPopup();
20573         }
20574     },
20575     
20576     picker : function()
20577     {
20578         return this.pickerEl;
20579 //        return this.el.select('.datepicker', true).first();
20580     },
20581     
20582     fillDow: function()
20583     {
20584         var dowCnt = this.weekStart;
20585         
20586         var dow = {
20587             tag: 'tr',
20588             cn: [
20589                 
20590             ]
20591         };
20592         
20593         if(this.calendarWeeks){
20594             dow.cn.push({
20595                 tag: 'th',
20596                 cls: 'cw',
20597                 html: '&nbsp;'
20598             })
20599         }
20600         
20601         while (dowCnt < this.weekStart + 7) {
20602             dow.cn.push({
20603                 tag: 'th',
20604                 cls: 'dow',
20605                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
20606             });
20607         }
20608         
20609         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
20610     },
20611     
20612     fillMonths: function()
20613     {    
20614         var i = 0;
20615         var months = this.picker().select('>.datepicker-months td', true).first();
20616         
20617         months.dom.innerHTML = '';
20618         
20619         while (i < 12) {
20620             var month = {
20621                 tag: 'span',
20622                 cls: 'month',
20623                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
20624             };
20625             
20626             months.createChild(month);
20627         }
20628         
20629     },
20630     
20631     update: function()
20632     {
20633         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;
20634         
20635         if (this.date < this.startDate) {
20636             this.viewDate = new Date(this.startDate);
20637         } else if (this.date > this.endDate) {
20638             this.viewDate = new Date(this.endDate);
20639         } else {
20640             this.viewDate = new Date(this.date);
20641         }
20642         
20643         this.fill();
20644     },
20645     
20646     fill: function() 
20647     {
20648         var d = new Date(this.viewDate),
20649                 year = d.getUTCFullYear(),
20650                 month = d.getUTCMonth(),
20651                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
20652                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
20653                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
20654                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
20655                 currentDate = this.date && this.date.valueOf(),
20656                 today = this.UTCToday();
20657         
20658         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
20659         
20660 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20661         
20662 //        this.picker.select('>tfoot th.today').
20663 //                                              .text(dates[this.language].today)
20664 //                                              .toggle(this.todayBtn !== false);
20665     
20666         this.updateNavArrows();
20667         this.fillMonths();
20668                                                 
20669         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
20670         
20671         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
20672          
20673         prevMonth.setUTCDate(day);
20674         
20675         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
20676         
20677         var nextMonth = new Date(prevMonth);
20678         
20679         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
20680         
20681         nextMonth = nextMonth.valueOf();
20682         
20683         var fillMonths = false;
20684         
20685         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
20686         
20687         while(prevMonth.valueOf() <= nextMonth) {
20688             var clsName = '';
20689             
20690             if (prevMonth.getUTCDay() === this.weekStart) {
20691                 if(fillMonths){
20692                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
20693                 }
20694                     
20695                 fillMonths = {
20696                     tag: 'tr',
20697                     cn: []
20698                 };
20699                 
20700                 if(this.calendarWeeks){
20701                     // ISO 8601: First week contains first thursday.
20702                     // ISO also states week starts on Monday, but we can be more abstract here.
20703                     var
20704                     // Start of current week: based on weekstart/current date
20705                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
20706                     // Thursday of this week
20707                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
20708                     // First Thursday of year, year from thursday
20709                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
20710                     // Calendar week: ms between thursdays, div ms per day, div 7 days
20711                     calWeek =  (th - yth) / 864e5 / 7 + 1;
20712                     
20713                     fillMonths.cn.push({
20714                         tag: 'td',
20715                         cls: 'cw',
20716                         html: calWeek
20717                     });
20718                 }
20719             }
20720             
20721             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
20722                 clsName += ' old';
20723             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
20724                 clsName += ' new';
20725             }
20726             if (this.todayHighlight &&
20727                 prevMonth.getUTCFullYear() == today.getFullYear() &&
20728                 prevMonth.getUTCMonth() == today.getMonth() &&
20729                 prevMonth.getUTCDate() == today.getDate()) {
20730                 clsName += ' today';
20731             }
20732             
20733             if (currentDate && prevMonth.valueOf() === currentDate) {
20734                 clsName += ' active';
20735             }
20736             
20737             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
20738                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
20739                     clsName += ' disabled';
20740             }
20741             
20742             fillMonths.cn.push({
20743                 tag: 'td',
20744                 cls: 'day ' + clsName,
20745                 html: prevMonth.getDate()
20746             });
20747             
20748             prevMonth.setDate(prevMonth.getDate()+1);
20749         }
20750           
20751         var currentYear = this.date && this.date.getUTCFullYear();
20752         var currentMonth = this.date && this.date.getUTCMonth();
20753         
20754         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
20755         
20756         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
20757             v.removeClass('active');
20758             
20759             if(currentYear === year && k === currentMonth){
20760                 v.addClass('active');
20761             }
20762             
20763             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
20764                 v.addClass('disabled');
20765             }
20766             
20767         });
20768         
20769         
20770         year = parseInt(year/10, 10) * 10;
20771         
20772         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
20773         
20774         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
20775         
20776         year -= 1;
20777         for (var i = -1; i < 11; i++) {
20778             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
20779                 tag: 'span',
20780                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
20781                 html: year
20782             });
20783             
20784             year += 1;
20785         }
20786     },
20787     
20788     showMode: function(dir) 
20789     {
20790         if (dir) {
20791             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
20792         }
20793         
20794         Roo.each(this.picker().select('>div',true).elements, function(v){
20795             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20796             v.hide();
20797         });
20798         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
20799     },
20800     
20801     place: function()
20802     {
20803         if(this.isInline) {
20804             return;
20805         }
20806         
20807         this.picker().removeClass(['bottom', 'top']);
20808         
20809         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20810             /*
20811              * place to the top of element!
20812              *
20813              */
20814             
20815             this.picker().addClass('top');
20816             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20817             
20818             return;
20819         }
20820         
20821         this.picker().addClass('bottom');
20822         
20823         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20824     },
20825     
20826     parseDate : function(value)
20827     {
20828         if(!value || value instanceof Date){
20829             return value;
20830         }
20831         var v = Date.parseDate(value, this.format);
20832         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
20833             v = Date.parseDate(value, 'Y-m-d');
20834         }
20835         if(!v && this.altFormats){
20836             if(!this.altFormatsArray){
20837                 this.altFormatsArray = this.altFormats.split("|");
20838             }
20839             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
20840                 v = Date.parseDate(value, this.altFormatsArray[i]);
20841             }
20842         }
20843         return v;
20844     },
20845     
20846     formatDate : function(date, fmt)
20847     {   
20848         return (!date || !(date instanceof Date)) ?
20849         date : date.dateFormat(fmt || this.format);
20850     },
20851     
20852     onFocus : function()
20853     {
20854         Roo.bootstrap.DateField.superclass.onFocus.call(this);
20855         this.showPopup();
20856     },
20857     
20858     onBlur : function()
20859     {
20860         Roo.bootstrap.DateField.superclass.onBlur.call(this);
20861         
20862         var d = this.inputEl().getValue();
20863         
20864         this.setValue(d);
20865                 
20866         this.hidePopup();
20867     },
20868     
20869     showPopup : function()
20870     {
20871         this.picker().show();
20872         this.update();
20873         this.place();
20874         
20875         this.fireEvent('showpopup', this, this.date);
20876     },
20877     
20878     hidePopup : function()
20879     {
20880         if(this.isInline) {
20881             return;
20882         }
20883         this.picker().hide();
20884         this.viewMode = this.startViewMode;
20885         this.showMode();
20886         
20887         this.fireEvent('hidepopup', this, this.date);
20888         
20889     },
20890     
20891     onMousedown: function(e)
20892     {
20893         e.stopPropagation();
20894         e.preventDefault();
20895     },
20896     
20897     keyup: function(e)
20898     {
20899         Roo.bootstrap.DateField.superclass.keyup.call(this);
20900         this.update();
20901     },
20902
20903     setValue: function(v)
20904     {
20905         if(this.fireEvent('beforeselect', this, v) !== false){
20906             var d = new Date(this.parseDate(v) ).clearTime();
20907         
20908             if(isNaN(d.getTime())){
20909                 this.date = this.viewDate = '';
20910                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
20911                 return;
20912             }
20913
20914             v = this.formatDate(d);
20915
20916             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
20917
20918             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
20919
20920             this.update();
20921
20922             this.fireEvent('select', this, this.date);
20923         }
20924     },
20925     
20926     getValue: function()
20927     {
20928         return this.formatDate(this.date);
20929     },
20930     
20931     fireKey: function(e)
20932     {
20933         if (!this.picker().isVisible()){
20934             if (e.keyCode == 27) { // allow escape to hide and re-show picker
20935                 this.showPopup();
20936             }
20937             return;
20938         }
20939         
20940         var dateChanged = false,
20941         dir, day, month,
20942         newDate, newViewDate;
20943         
20944         switch(e.keyCode){
20945             case 27: // escape
20946                 this.hidePopup();
20947                 e.preventDefault();
20948                 break;
20949             case 37: // left
20950             case 39: // right
20951                 if (!this.keyboardNavigation) {
20952                     break;
20953                 }
20954                 dir = e.keyCode == 37 ? -1 : 1;
20955                 
20956                 if (e.ctrlKey){
20957                     newDate = this.moveYear(this.date, dir);
20958                     newViewDate = this.moveYear(this.viewDate, dir);
20959                 } else if (e.shiftKey){
20960                     newDate = this.moveMonth(this.date, dir);
20961                     newViewDate = this.moveMonth(this.viewDate, dir);
20962                 } else {
20963                     newDate = new Date(this.date);
20964                     newDate.setUTCDate(this.date.getUTCDate() + dir);
20965                     newViewDate = new Date(this.viewDate);
20966                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
20967                 }
20968                 if (this.dateWithinRange(newDate)){
20969                     this.date = newDate;
20970                     this.viewDate = newViewDate;
20971                     this.setValue(this.formatDate(this.date));
20972 //                    this.update();
20973                     e.preventDefault();
20974                     dateChanged = true;
20975                 }
20976                 break;
20977             case 38: // up
20978             case 40: // down
20979                 if (!this.keyboardNavigation) {
20980                     break;
20981                 }
20982                 dir = e.keyCode == 38 ? -1 : 1;
20983                 if (e.ctrlKey){
20984                     newDate = this.moveYear(this.date, dir);
20985                     newViewDate = this.moveYear(this.viewDate, dir);
20986                 } else if (e.shiftKey){
20987                     newDate = this.moveMonth(this.date, dir);
20988                     newViewDate = this.moveMonth(this.viewDate, dir);
20989                 } else {
20990                     newDate = new Date(this.date);
20991                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
20992                     newViewDate = new Date(this.viewDate);
20993                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
20994                 }
20995                 if (this.dateWithinRange(newDate)){
20996                     this.date = newDate;
20997                     this.viewDate = newViewDate;
20998                     this.setValue(this.formatDate(this.date));
20999 //                    this.update();
21000                     e.preventDefault();
21001                     dateChanged = true;
21002                 }
21003                 break;
21004             case 13: // enter
21005                 this.setValue(this.formatDate(this.date));
21006                 this.hidePopup();
21007                 e.preventDefault();
21008                 break;
21009             case 9: // tab
21010                 this.setValue(this.formatDate(this.date));
21011                 this.hidePopup();
21012                 break;
21013             case 16: // shift
21014             case 17: // ctrl
21015             case 18: // alt
21016                 break;
21017             default :
21018                 this.hidePopup();
21019                 
21020         }
21021     },
21022     
21023     
21024     onClick: function(e) 
21025     {
21026         e.stopPropagation();
21027         e.preventDefault();
21028         
21029         var target = e.getTarget();
21030         
21031         if(target.nodeName.toLowerCase() === 'i'){
21032             target = Roo.get(target).dom.parentNode;
21033         }
21034         
21035         var nodeName = target.nodeName;
21036         var className = target.className;
21037         var html = target.innerHTML;
21038         //Roo.log(nodeName);
21039         
21040         switch(nodeName.toLowerCase()) {
21041             case 'th':
21042                 switch(className) {
21043                     case 'switch':
21044                         this.showMode(1);
21045                         break;
21046                     case 'prev':
21047                     case 'next':
21048                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21049                         switch(this.viewMode){
21050                                 case 0:
21051                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21052                                         break;
21053                                 case 1:
21054                                 case 2:
21055                                         this.viewDate = this.moveYear(this.viewDate, dir);
21056                                         break;
21057                         }
21058                         this.fill();
21059                         break;
21060                     case 'today':
21061                         var date = new Date();
21062                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21063 //                        this.fill()
21064                         this.setValue(this.formatDate(this.date));
21065                         
21066                         this.hidePopup();
21067                         break;
21068                 }
21069                 break;
21070             case 'span':
21071                 if (className.indexOf('disabled') < 0) {
21072                     this.viewDate.setUTCDate(1);
21073                     if (className.indexOf('month') > -1) {
21074                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21075                     } else {
21076                         var year = parseInt(html, 10) || 0;
21077                         this.viewDate.setUTCFullYear(year);
21078                         
21079                     }
21080                     
21081                     if(this.singleMode){
21082                         this.setValue(this.formatDate(this.viewDate));
21083                         this.hidePopup();
21084                         return;
21085                     }
21086                     
21087                     this.showMode(-1);
21088                     this.fill();
21089                 }
21090                 break;
21091                 
21092             case 'td':
21093                 //Roo.log(className);
21094                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21095                     var day = parseInt(html, 10) || 1;
21096                     var year = this.viewDate.getUTCFullYear(),
21097                         month = this.viewDate.getUTCMonth();
21098
21099                     if (className.indexOf('old') > -1) {
21100                         if(month === 0 ){
21101                             month = 11;
21102                             year -= 1;
21103                         }else{
21104                             month -= 1;
21105                         }
21106                     } else if (className.indexOf('new') > -1) {
21107                         if (month == 11) {
21108                             month = 0;
21109                             year += 1;
21110                         } else {
21111                             month += 1;
21112                         }
21113                     }
21114                     //Roo.log([year,month,day]);
21115                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21116                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21117 //                    this.fill();
21118                     //Roo.log(this.formatDate(this.date));
21119                     this.setValue(this.formatDate(this.date));
21120                     this.hidePopup();
21121                 }
21122                 break;
21123         }
21124     },
21125     
21126     setStartDate: function(startDate)
21127     {
21128         this.startDate = startDate || -Infinity;
21129         if (this.startDate !== -Infinity) {
21130             this.startDate = this.parseDate(this.startDate);
21131         }
21132         this.update();
21133         this.updateNavArrows();
21134     },
21135
21136     setEndDate: function(endDate)
21137     {
21138         this.endDate = endDate || Infinity;
21139         if (this.endDate !== Infinity) {
21140             this.endDate = this.parseDate(this.endDate);
21141         }
21142         this.update();
21143         this.updateNavArrows();
21144     },
21145     
21146     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21147     {
21148         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21149         if (typeof(this.daysOfWeekDisabled) !== 'object') {
21150             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21151         }
21152         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21153             return parseInt(d, 10);
21154         });
21155         this.update();
21156         this.updateNavArrows();
21157     },
21158     
21159     updateNavArrows: function() 
21160     {
21161         if(this.singleMode){
21162             return;
21163         }
21164         
21165         var d = new Date(this.viewDate),
21166         year = d.getUTCFullYear(),
21167         month = d.getUTCMonth();
21168         
21169         Roo.each(this.picker().select('.prev', true).elements, function(v){
21170             v.show();
21171             switch (this.viewMode) {
21172                 case 0:
21173
21174                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21175                         v.hide();
21176                     }
21177                     break;
21178                 case 1:
21179                 case 2:
21180                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21181                         v.hide();
21182                     }
21183                     break;
21184             }
21185         });
21186         
21187         Roo.each(this.picker().select('.next', true).elements, function(v){
21188             v.show();
21189             switch (this.viewMode) {
21190                 case 0:
21191
21192                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21193                         v.hide();
21194                     }
21195                     break;
21196                 case 1:
21197                 case 2:
21198                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21199                         v.hide();
21200                     }
21201                     break;
21202             }
21203         })
21204     },
21205     
21206     moveMonth: function(date, dir)
21207     {
21208         if (!dir) {
21209             return date;
21210         }
21211         var new_date = new Date(date.valueOf()),
21212         day = new_date.getUTCDate(),
21213         month = new_date.getUTCMonth(),
21214         mag = Math.abs(dir),
21215         new_month, test;
21216         dir = dir > 0 ? 1 : -1;
21217         if (mag == 1){
21218             test = dir == -1
21219             // If going back one month, make sure month is not current month
21220             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21221             ? function(){
21222                 return new_date.getUTCMonth() == month;
21223             }
21224             // If going forward one month, make sure month is as expected
21225             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21226             : function(){
21227                 return new_date.getUTCMonth() != new_month;
21228             };
21229             new_month = month + dir;
21230             new_date.setUTCMonth(new_month);
21231             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21232             if (new_month < 0 || new_month > 11) {
21233                 new_month = (new_month + 12) % 12;
21234             }
21235         } else {
21236             // For magnitudes >1, move one month at a time...
21237             for (var i=0; i<mag; i++) {
21238                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21239                 new_date = this.moveMonth(new_date, dir);
21240             }
21241             // ...then reset the day, keeping it in the new month
21242             new_month = new_date.getUTCMonth();
21243             new_date.setUTCDate(day);
21244             test = function(){
21245                 return new_month != new_date.getUTCMonth();
21246             };
21247         }
21248         // Common date-resetting loop -- if date is beyond end of month, make it
21249         // end of month
21250         while (test()){
21251             new_date.setUTCDate(--day);
21252             new_date.setUTCMonth(new_month);
21253         }
21254         return new_date;
21255     },
21256
21257     moveYear: function(date, dir)
21258     {
21259         return this.moveMonth(date, dir*12);
21260     },
21261
21262     dateWithinRange: function(date)
21263     {
21264         return date >= this.startDate && date <= this.endDate;
21265     },
21266
21267     
21268     remove: function() 
21269     {
21270         this.picker().remove();
21271     },
21272     
21273     validateValue : function(value)
21274     {
21275         if(this.getVisibilityEl().hasClass('hidden')){
21276             return true;
21277         }
21278         
21279         if(value.length < 1)  {
21280             if(this.allowBlank){
21281                 return true;
21282             }
21283             return false;
21284         }
21285         
21286         if(value.length < this.minLength){
21287             return false;
21288         }
21289         if(value.length > this.maxLength){
21290             return false;
21291         }
21292         if(this.vtype){
21293             var vt = Roo.form.VTypes;
21294             if(!vt[this.vtype](value, this)){
21295                 return false;
21296             }
21297         }
21298         if(typeof this.validator == "function"){
21299             var msg = this.validator(value);
21300             if(msg !== true){
21301                 return false;
21302             }
21303         }
21304         
21305         if(this.regex && !this.regex.test(value)){
21306             return false;
21307         }
21308         
21309         if(typeof(this.parseDate(value)) == 'undefined'){
21310             return false;
21311         }
21312         
21313         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21314             return false;
21315         }      
21316         
21317         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21318             return false;
21319         } 
21320         
21321         
21322         return true;
21323     },
21324     
21325     reset : function()
21326     {
21327         this.date = this.viewDate = '';
21328         
21329         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21330     }
21331    
21332 });
21333
21334 Roo.apply(Roo.bootstrap.DateField,  {
21335     
21336     head : {
21337         tag: 'thead',
21338         cn: [
21339         {
21340             tag: 'tr',
21341             cn: [
21342             {
21343                 tag: 'th',
21344                 cls: 'prev',
21345                 html: '<i class="fa fa-arrow-left"/>'
21346             },
21347             {
21348                 tag: 'th',
21349                 cls: 'switch',
21350                 colspan: '5'
21351             },
21352             {
21353                 tag: 'th',
21354                 cls: 'next',
21355                 html: '<i class="fa fa-arrow-right"/>'
21356             }
21357
21358             ]
21359         }
21360         ]
21361     },
21362     
21363     content : {
21364         tag: 'tbody',
21365         cn: [
21366         {
21367             tag: 'tr',
21368             cn: [
21369             {
21370                 tag: 'td',
21371                 colspan: '7'
21372             }
21373             ]
21374         }
21375         ]
21376     },
21377     
21378     footer : {
21379         tag: 'tfoot',
21380         cn: [
21381         {
21382             tag: 'tr',
21383             cn: [
21384             {
21385                 tag: 'th',
21386                 colspan: '7',
21387                 cls: 'today'
21388             }
21389                     
21390             ]
21391         }
21392         ]
21393     },
21394     
21395     dates:{
21396         en: {
21397             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21398             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21399             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21400             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21401             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21402             today: "Today"
21403         }
21404     },
21405     
21406     modes: [
21407     {
21408         clsName: 'days',
21409         navFnc: 'Month',
21410         navStep: 1
21411     },
21412     {
21413         clsName: 'months',
21414         navFnc: 'FullYear',
21415         navStep: 1
21416     },
21417     {
21418         clsName: 'years',
21419         navFnc: 'FullYear',
21420         navStep: 10
21421     }]
21422 });
21423
21424 Roo.apply(Roo.bootstrap.DateField,  {
21425   
21426     template : {
21427         tag: 'div',
21428         cls: 'datepicker dropdown-menu roo-dynamic',
21429         cn: [
21430         {
21431             tag: 'div',
21432             cls: 'datepicker-days',
21433             cn: [
21434             {
21435                 tag: 'table',
21436                 cls: 'table-condensed',
21437                 cn:[
21438                 Roo.bootstrap.DateField.head,
21439                 {
21440                     tag: 'tbody'
21441                 },
21442                 Roo.bootstrap.DateField.footer
21443                 ]
21444             }
21445             ]
21446         },
21447         {
21448             tag: 'div',
21449             cls: 'datepicker-months',
21450             cn: [
21451             {
21452                 tag: 'table',
21453                 cls: 'table-condensed',
21454                 cn:[
21455                 Roo.bootstrap.DateField.head,
21456                 Roo.bootstrap.DateField.content,
21457                 Roo.bootstrap.DateField.footer
21458                 ]
21459             }
21460             ]
21461         },
21462         {
21463             tag: 'div',
21464             cls: 'datepicker-years',
21465             cn: [
21466             {
21467                 tag: 'table',
21468                 cls: 'table-condensed',
21469                 cn:[
21470                 Roo.bootstrap.DateField.head,
21471                 Roo.bootstrap.DateField.content,
21472                 Roo.bootstrap.DateField.footer
21473                 ]
21474             }
21475             ]
21476         }
21477         ]
21478     }
21479 });
21480
21481  
21482
21483  /*
21484  * - LGPL
21485  *
21486  * TimeField
21487  * 
21488  */
21489
21490 /**
21491  * @class Roo.bootstrap.TimeField
21492  * @extends Roo.bootstrap.Input
21493  * Bootstrap DateField class
21494  * 
21495  * 
21496  * @constructor
21497  * Create a new TimeField
21498  * @param {Object} config The config object
21499  */
21500
21501 Roo.bootstrap.TimeField = function(config){
21502     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
21503     this.addEvents({
21504             /**
21505              * @event show
21506              * Fires when this field show.
21507              * @param {Roo.bootstrap.DateField} thisthis
21508              * @param {Mixed} date The date value
21509              */
21510             show : true,
21511             /**
21512              * @event show
21513              * Fires when this field hide.
21514              * @param {Roo.bootstrap.DateField} this
21515              * @param {Mixed} date The date value
21516              */
21517             hide : true,
21518             /**
21519              * @event select
21520              * Fires when select a date.
21521              * @param {Roo.bootstrap.DateField} this
21522              * @param {Mixed} date The date value
21523              */
21524             select : true
21525         });
21526 };
21527
21528 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
21529     
21530     /**
21531      * @cfg {String} format
21532      * The default time format string which can be overriden for localization support.  The format must be
21533      * valid according to {@link Date#parseDate} (defaults to 'H:i').
21534      */
21535     format : "H:i",
21536        
21537     onRender: function(ct, position)
21538     {
21539         
21540         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
21541                 
21542         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
21543         
21544         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21545         
21546         this.pop = this.picker().select('>.datepicker-time',true).first();
21547         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21548         
21549         this.picker().on('mousedown', this.onMousedown, this);
21550         this.picker().on('click', this.onClick, this);
21551         
21552         this.picker().addClass('datepicker-dropdown');
21553     
21554         this.fillTime();
21555         this.update();
21556             
21557         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
21558         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
21559         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
21560         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
21561         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
21562         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
21563
21564     },
21565     
21566     fireKey: function(e){
21567         if (!this.picker().isVisible()){
21568             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21569                 this.show();
21570             }
21571             return;
21572         }
21573
21574         e.preventDefault();
21575         
21576         switch(e.keyCode){
21577             case 27: // escape
21578                 this.hide();
21579                 break;
21580             case 37: // left
21581             case 39: // right
21582                 this.onTogglePeriod();
21583                 break;
21584             case 38: // up
21585                 this.onIncrementMinutes();
21586                 break;
21587             case 40: // down
21588                 this.onDecrementMinutes();
21589                 break;
21590             case 13: // enter
21591             case 9: // tab
21592                 this.setTime();
21593                 break;
21594         }
21595     },
21596     
21597     onClick: function(e) {
21598         e.stopPropagation();
21599         e.preventDefault();
21600     },
21601     
21602     picker : function()
21603     {
21604         return this.el.select('.datepicker', true).first();
21605     },
21606     
21607     fillTime: function()
21608     {    
21609         var time = this.pop.select('tbody', true).first();
21610         
21611         time.dom.innerHTML = '';
21612         
21613         time.createChild({
21614             tag: 'tr',
21615             cn: [
21616                 {
21617                     tag: 'td',
21618                     cn: [
21619                         {
21620                             tag: 'a',
21621                             href: '#',
21622                             cls: 'btn',
21623                             cn: [
21624                                 {
21625                                     tag: 'span',
21626                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
21627                                 }
21628                             ]
21629                         } 
21630                     ]
21631                 },
21632                 {
21633                     tag: 'td',
21634                     cls: 'separator'
21635                 },
21636                 {
21637                     tag: 'td',
21638                     cn: [
21639                         {
21640                             tag: 'a',
21641                             href: '#',
21642                             cls: 'btn',
21643                             cn: [
21644                                 {
21645                                     tag: 'span',
21646                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
21647                                 }
21648                             ]
21649                         }
21650                     ]
21651                 },
21652                 {
21653                     tag: 'td',
21654                     cls: 'separator'
21655                 }
21656             ]
21657         });
21658         
21659         time.createChild({
21660             tag: 'tr',
21661             cn: [
21662                 {
21663                     tag: 'td',
21664                     cn: [
21665                         {
21666                             tag: 'span',
21667                             cls: 'timepicker-hour',
21668                             html: '00'
21669                         }  
21670                     ]
21671                 },
21672                 {
21673                     tag: 'td',
21674                     cls: 'separator',
21675                     html: ':'
21676                 },
21677                 {
21678                     tag: 'td',
21679                     cn: [
21680                         {
21681                             tag: 'span',
21682                             cls: 'timepicker-minute',
21683                             html: '00'
21684                         }  
21685                     ]
21686                 },
21687                 {
21688                     tag: 'td',
21689                     cls: 'separator'
21690                 },
21691                 {
21692                     tag: 'td',
21693                     cn: [
21694                         {
21695                             tag: 'button',
21696                             type: 'button',
21697                             cls: 'btn btn-primary period',
21698                             html: 'AM'
21699                             
21700                         }
21701                     ]
21702                 }
21703             ]
21704         });
21705         
21706         time.createChild({
21707             tag: 'tr',
21708             cn: [
21709                 {
21710                     tag: 'td',
21711                     cn: [
21712                         {
21713                             tag: 'a',
21714                             href: '#',
21715                             cls: 'btn',
21716                             cn: [
21717                                 {
21718                                     tag: 'span',
21719                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
21720                                 }
21721                             ]
21722                         }
21723                     ]
21724                 },
21725                 {
21726                     tag: 'td',
21727                     cls: 'separator'
21728                 },
21729                 {
21730                     tag: 'td',
21731                     cn: [
21732                         {
21733                             tag: 'a',
21734                             href: '#',
21735                             cls: 'btn',
21736                             cn: [
21737                                 {
21738                                     tag: 'span',
21739                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
21740                                 }
21741                             ]
21742                         }
21743                     ]
21744                 },
21745                 {
21746                     tag: 'td',
21747                     cls: 'separator'
21748                 }
21749             ]
21750         });
21751         
21752     },
21753     
21754     update: function()
21755     {
21756         
21757         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
21758         
21759         this.fill();
21760     },
21761     
21762     fill: function() 
21763     {
21764         var hours = this.time.getHours();
21765         var minutes = this.time.getMinutes();
21766         var period = 'AM';
21767         
21768         if(hours > 11){
21769             period = 'PM';
21770         }
21771         
21772         if(hours == 0){
21773             hours = 12;
21774         }
21775         
21776         
21777         if(hours > 12){
21778             hours = hours - 12;
21779         }
21780         
21781         if(hours < 10){
21782             hours = '0' + hours;
21783         }
21784         
21785         if(minutes < 10){
21786             minutes = '0' + minutes;
21787         }
21788         
21789         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
21790         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
21791         this.pop.select('button', true).first().dom.innerHTML = period;
21792         
21793     },
21794     
21795     place: function()
21796     {   
21797         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
21798         
21799         var cls = ['bottom'];
21800         
21801         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
21802             cls.pop();
21803             cls.push('top');
21804         }
21805         
21806         cls.push('right');
21807         
21808         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
21809             cls.pop();
21810             cls.push('left');
21811         }
21812         
21813         this.picker().addClass(cls.join('-'));
21814         
21815         var _this = this;
21816         
21817         Roo.each(cls, function(c){
21818             if(c == 'bottom'){
21819                 _this.picker().setTop(_this.inputEl().getHeight());
21820                 return;
21821             }
21822             if(c == 'top'){
21823                 _this.picker().setTop(0 - _this.picker().getHeight());
21824                 return;
21825             }
21826             
21827             if(c == 'left'){
21828                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
21829                 return;
21830             }
21831             if(c == 'right'){
21832                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
21833                 return;
21834             }
21835         });
21836         
21837     },
21838   
21839     onFocus : function()
21840     {
21841         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
21842         this.show();
21843     },
21844     
21845     onBlur : function()
21846     {
21847         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
21848         this.hide();
21849     },
21850     
21851     show : function()
21852     {
21853         this.picker().show();
21854         this.pop.show();
21855         this.update();
21856         this.place();
21857         
21858         this.fireEvent('show', this, this.date);
21859     },
21860     
21861     hide : function()
21862     {
21863         this.picker().hide();
21864         this.pop.hide();
21865         
21866         this.fireEvent('hide', this, this.date);
21867     },
21868     
21869     setTime : function()
21870     {
21871         this.hide();
21872         this.setValue(this.time.format(this.format));
21873         
21874         this.fireEvent('select', this, this.date);
21875         
21876         
21877     },
21878     
21879     onMousedown: function(e){
21880         e.stopPropagation();
21881         e.preventDefault();
21882     },
21883     
21884     onIncrementHours: function()
21885     {
21886         Roo.log('onIncrementHours');
21887         this.time = this.time.add(Date.HOUR, 1);
21888         this.update();
21889         
21890     },
21891     
21892     onDecrementHours: function()
21893     {
21894         Roo.log('onDecrementHours');
21895         this.time = this.time.add(Date.HOUR, -1);
21896         this.update();
21897     },
21898     
21899     onIncrementMinutes: function()
21900     {
21901         Roo.log('onIncrementMinutes');
21902         this.time = this.time.add(Date.MINUTE, 1);
21903         this.update();
21904     },
21905     
21906     onDecrementMinutes: function()
21907     {
21908         Roo.log('onDecrementMinutes');
21909         this.time = this.time.add(Date.MINUTE, -1);
21910         this.update();
21911     },
21912     
21913     onTogglePeriod: function()
21914     {
21915         Roo.log('onTogglePeriod');
21916         this.time = this.time.add(Date.HOUR, 12);
21917         this.update();
21918     }
21919     
21920    
21921 });
21922
21923 Roo.apply(Roo.bootstrap.TimeField,  {
21924     
21925     content : {
21926         tag: 'tbody',
21927         cn: [
21928             {
21929                 tag: 'tr',
21930                 cn: [
21931                 {
21932                     tag: 'td',
21933                     colspan: '7'
21934                 }
21935                 ]
21936             }
21937         ]
21938     },
21939     
21940     footer : {
21941         tag: 'tfoot',
21942         cn: [
21943             {
21944                 tag: 'tr',
21945                 cn: [
21946                 {
21947                     tag: 'th',
21948                     colspan: '7',
21949                     cls: '',
21950                     cn: [
21951                         {
21952                             tag: 'button',
21953                             cls: 'btn btn-info ok',
21954                             html: 'OK'
21955                         }
21956                     ]
21957                 }
21958
21959                 ]
21960             }
21961         ]
21962     }
21963 });
21964
21965 Roo.apply(Roo.bootstrap.TimeField,  {
21966   
21967     template : {
21968         tag: 'div',
21969         cls: 'datepicker dropdown-menu',
21970         cn: [
21971             {
21972                 tag: 'div',
21973                 cls: 'datepicker-time',
21974                 cn: [
21975                 {
21976                     tag: 'table',
21977                     cls: 'table-condensed',
21978                     cn:[
21979                     Roo.bootstrap.TimeField.content,
21980                     Roo.bootstrap.TimeField.footer
21981                     ]
21982                 }
21983                 ]
21984             }
21985         ]
21986     }
21987 });
21988
21989  
21990
21991  /*
21992  * - LGPL
21993  *
21994  * MonthField
21995  * 
21996  */
21997
21998 /**
21999  * @class Roo.bootstrap.MonthField
22000  * @extends Roo.bootstrap.Input
22001  * Bootstrap MonthField class
22002  * 
22003  * @cfg {String} language default en
22004  * 
22005  * @constructor
22006  * Create a new MonthField
22007  * @param {Object} config The config object
22008  */
22009
22010 Roo.bootstrap.MonthField = function(config){
22011     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22012     
22013     this.addEvents({
22014         /**
22015          * @event show
22016          * Fires when this field show.
22017          * @param {Roo.bootstrap.MonthField} this
22018          * @param {Mixed} date The date value
22019          */
22020         show : true,
22021         /**
22022          * @event show
22023          * Fires when this field hide.
22024          * @param {Roo.bootstrap.MonthField} this
22025          * @param {Mixed} date The date value
22026          */
22027         hide : true,
22028         /**
22029          * @event select
22030          * Fires when select a date.
22031          * @param {Roo.bootstrap.MonthField} this
22032          * @param {String} oldvalue The old value
22033          * @param {String} newvalue The new value
22034          */
22035         select : true
22036     });
22037 };
22038
22039 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22040     
22041     onRender: function(ct, position)
22042     {
22043         
22044         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22045         
22046         this.language = this.language || 'en';
22047         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22048         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22049         
22050         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22051         this.isInline = false;
22052         this.isInput = true;
22053         this.component = this.el.select('.add-on', true).first() || false;
22054         this.component = (this.component && this.component.length === 0) ? false : this.component;
22055         this.hasInput = this.component && this.inputEL().length;
22056         
22057         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22058         
22059         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22060         
22061         this.picker().on('mousedown', this.onMousedown, this);
22062         this.picker().on('click', this.onClick, this);
22063         
22064         this.picker().addClass('datepicker-dropdown');
22065         
22066         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22067             v.setStyle('width', '189px');
22068         });
22069         
22070         this.fillMonths();
22071         
22072         this.update();
22073         
22074         if(this.isInline) {
22075             this.show();
22076         }
22077         
22078     },
22079     
22080     setValue: function(v, suppressEvent)
22081     {   
22082         var o = this.getValue();
22083         
22084         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22085         
22086         this.update();
22087
22088         if(suppressEvent !== true){
22089             this.fireEvent('select', this, o, v);
22090         }
22091         
22092     },
22093     
22094     getValue: function()
22095     {
22096         return this.value;
22097     },
22098     
22099     onClick: function(e) 
22100     {
22101         e.stopPropagation();
22102         e.preventDefault();
22103         
22104         var target = e.getTarget();
22105         
22106         if(target.nodeName.toLowerCase() === 'i'){
22107             target = Roo.get(target).dom.parentNode;
22108         }
22109         
22110         var nodeName = target.nodeName;
22111         var className = target.className;
22112         var html = target.innerHTML;
22113         
22114         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22115             return;
22116         }
22117         
22118         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22119         
22120         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22121         
22122         this.hide();
22123                         
22124     },
22125     
22126     picker : function()
22127     {
22128         return this.pickerEl;
22129     },
22130     
22131     fillMonths: function()
22132     {    
22133         var i = 0;
22134         var months = this.picker().select('>.datepicker-months td', true).first();
22135         
22136         months.dom.innerHTML = '';
22137         
22138         while (i < 12) {
22139             var month = {
22140                 tag: 'span',
22141                 cls: 'month',
22142                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22143             };
22144             
22145             months.createChild(month);
22146         }
22147         
22148     },
22149     
22150     update: function()
22151     {
22152         var _this = this;
22153         
22154         if(typeof(this.vIndex) == 'undefined' && this.value.length){
22155             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22156         }
22157         
22158         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22159             e.removeClass('active');
22160             
22161             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22162                 e.addClass('active');
22163             }
22164         })
22165     },
22166     
22167     place: function()
22168     {
22169         if(this.isInline) {
22170             return;
22171         }
22172         
22173         this.picker().removeClass(['bottom', 'top']);
22174         
22175         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22176             /*
22177              * place to the top of element!
22178              *
22179              */
22180             
22181             this.picker().addClass('top');
22182             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22183             
22184             return;
22185         }
22186         
22187         this.picker().addClass('bottom');
22188         
22189         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22190     },
22191     
22192     onFocus : function()
22193     {
22194         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22195         this.show();
22196     },
22197     
22198     onBlur : function()
22199     {
22200         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22201         
22202         var d = this.inputEl().getValue();
22203         
22204         this.setValue(d);
22205                 
22206         this.hide();
22207     },
22208     
22209     show : function()
22210     {
22211         this.picker().show();
22212         this.picker().select('>.datepicker-months', true).first().show();
22213         this.update();
22214         this.place();
22215         
22216         this.fireEvent('show', this, this.date);
22217     },
22218     
22219     hide : function()
22220     {
22221         if(this.isInline) {
22222             return;
22223         }
22224         this.picker().hide();
22225         this.fireEvent('hide', this, this.date);
22226         
22227     },
22228     
22229     onMousedown: function(e)
22230     {
22231         e.stopPropagation();
22232         e.preventDefault();
22233     },
22234     
22235     keyup: function(e)
22236     {
22237         Roo.bootstrap.MonthField.superclass.keyup.call(this);
22238         this.update();
22239     },
22240
22241     fireKey: function(e)
22242     {
22243         if (!this.picker().isVisible()){
22244             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
22245                 this.show();
22246             }
22247             return;
22248         }
22249         
22250         var dir;
22251         
22252         switch(e.keyCode){
22253             case 27: // escape
22254                 this.hide();
22255                 e.preventDefault();
22256                 break;
22257             case 37: // left
22258             case 39: // right
22259                 dir = e.keyCode == 37 ? -1 : 1;
22260                 
22261                 this.vIndex = this.vIndex + dir;
22262                 
22263                 if(this.vIndex < 0){
22264                     this.vIndex = 0;
22265                 }
22266                 
22267                 if(this.vIndex > 11){
22268                     this.vIndex = 11;
22269                 }
22270                 
22271                 if(isNaN(this.vIndex)){
22272                     this.vIndex = 0;
22273                 }
22274                 
22275                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22276                 
22277                 break;
22278             case 38: // up
22279             case 40: // down
22280                 
22281                 dir = e.keyCode == 38 ? -1 : 1;
22282                 
22283                 this.vIndex = this.vIndex + dir * 4;
22284                 
22285                 if(this.vIndex < 0){
22286                     this.vIndex = 0;
22287                 }
22288                 
22289                 if(this.vIndex > 11){
22290                     this.vIndex = 11;
22291                 }
22292                 
22293                 if(isNaN(this.vIndex)){
22294                     this.vIndex = 0;
22295                 }
22296                 
22297                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22298                 break;
22299                 
22300             case 13: // enter
22301                 
22302                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22303                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22304                 }
22305                 
22306                 this.hide();
22307                 e.preventDefault();
22308                 break;
22309             case 9: // tab
22310                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22311                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22312                 }
22313                 this.hide();
22314                 break;
22315             case 16: // shift
22316             case 17: // ctrl
22317             case 18: // alt
22318                 break;
22319             default :
22320                 this.hide();
22321                 
22322         }
22323     },
22324     
22325     remove: function() 
22326     {
22327         this.picker().remove();
22328     }
22329    
22330 });
22331
22332 Roo.apply(Roo.bootstrap.MonthField,  {
22333     
22334     content : {
22335         tag: 'tbody',
22336         cn: [
22337         {
22338             tag: 'tr',
22339             cn: [
22340             {
22341                 tag: 'td',
22342                 colspan: '7'
22343             }
22344             ]
22345         }
22346         ]
22347     },
22348     
22349     dates:{
22350         en: {
22351             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22352             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22353         }
22354     }
22355 });
22356
22357 Roo.apply(Roo.bootstrap.MonthField,  {
22358   
22359     template : {
22360         tag: 'div',
22361         cls: 'datepicker dropdown-menu roo-dynamic',
22362         cn: [
22363             {
22364                 tag: 'div',
22365                 cls: 'datepicker-months',
22366                 cn: [
22367                 {
22368                     tag: 'table',
22369                     cls: 'table-condensed',
22370                     cn:[
22371                         Roo.bootstrap.DateField.content
22372                     ]
22373                 }
22374                 ]
22375             }
22376         ]
22377     }
22378 });
22379
22380  
22381
22382  
22383  /*
22384  * - LGPL
22385  *
22386  * CheckBox
22387  * 
22388  */
22389
22390 /**
22391  * @class Roo.bootstrap.CheckBox
22392  * @extends Roo.bootstrap.Input
22393  * Bootstrap CheckBox class
22394  * 
22395  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22396  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22397  * @cfg {String} boxLabel The text that appears beside the checkbox
22398  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22399  * @cfg {Boolean} checked initnal the element
22400  * @cfg {Boolean} inline inline the element (default false)
22401  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22402  * @cfg {String} tooltip label tooltip
22403  * 
22404  * @constructor
22405  * Create a new CheckBox
22406  * @param {Object} config The config object
22407  */
22408
22409 Roo.bootstrap.CheckBox = function(config){
22410     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22411    
22412     this.addEvents({
22413         /**
22414         * @event check
22415         * Fires when the element is checked or unchecked.
22416         * @param {Roo.bootstrap.CheckBox} this This input
22417         * @param {Boolean} checked The new checked value
22418         */
22419        check : true,
22420        /**
22421         * @event click
22422         * Fires when the element is click.
22423         * @param {Roo.bootstrap.CheckBox} this This input
22424         */
22425        click : true
22426     });
22427     
22428 };
22429
22430 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
22431   
22432     inputType: 'checkbox',
22433     inputValue: 1,
22434     valueOff: 0,
22435     boxLabel: false,
22436     checked: false,
22437     weight : false,
22438     inline: false,
22439     tooltip : '',
22440     
22441     // checkbox success does not make any sense really.. 
22442     invalidClass : "",
22443     validClass : "",
22444     
22445     
22446     getAutoCreate : function()
22447     {
22448         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22449         
22450         var id = Roo.id();
22451         
22452         var cfg = {};
22453         
22454         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22455         
22456         if(this.inline){
22457             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
22458         }
22459         
22460         var input =  {
22461             tag: 'input',
22462             id : id,
22463             type : this.inputType,
22464             value : this.inputValue,
22465             cls : 'roo-' + this.inputType, //'form-box',
22466             placeholder : this.placeholder || ''
22467             
22468         };
22469         
22470         if(this.inputType != 'radio'){
22471             var hidden =  {
22472                 tag: 'input',
22473                 type : 'hidden',
22474                 cls : 'roo-hidden-value',
22475                 value : this.checked ? this.inputValue : this.valueOff
22476             };
22477         }
22478         
22479             
22480         if (this.weight) { // Validity check?
22481             cfg.cls += " " + this.inputType + "-" + this.weight;
22482         }
22483         
22484         if (this.disabled) {
22485             input.disabled=true;
22486         }
22487         
22488         if(this.checked){
22489             input.checked = this.checked;
22490         }
22491         
22492         if (this.name) {
22493             
22494             input.name = this.name;
22495             
22496             if(this.inputType != 'radio'){
22497                 hidden.name = this.name;
22498                 input.name = '_hidden_' + this.name;
22499             }
22500         }
22501         
22502         if (this.size) {
22503             input.cls += ' input-' + this.size;
22504         }
22505         
22506         var settings=this;
22507         
22508         ['xs','sm','md','lg'].map(function(size){
22509             if (settings[size]) {
22510                 cfg.cls += ' col-' + size + '-' + settings[size];
22511             }
22512         });
22513         
22514         var inputblock = input;
22515          
22516         if (this.before || this.after) {
22517             
22518             inputblock = {
22519                 cls : 'input-group',
22520                 cn :  [] 
22521             };
22522             
22523             if (this.before) {
22524                 inputblock.cn.push({
22525                     tag :'span',
22526                     cls : 'input-group-addon',
22527                     html : this.before
22528                 });
22529             }
22530             
22531             inputblock.cn.push(input);
22532             
22533             if(this.inputType != 'radio'){
22534                 inputblock.cn.push(hidden);
22535             }
22536             
22537             if (this.after) {
22538                 inputblock.cn.push({
22539                     tag :'span',
22540                     cls : 'input-group-addon',
22541                     html : this.after
22542                 });
22543             }
22544             
22545         }
22546         var boxLabelCfg = false;
22547         
22548         if(this.boxLabel){
22549            
22550             boxLabelCfg = {
22551                 tag: 'label',
22552                 //'for': id, // box label is handled by onclick - so no for...
22553                 cls: 'box-label',
22554                 html: this.boxLabel
22555             };
22556             if(this.tooltip){
22557                 boxLabelCfg.tooltip = this.tooltip;
22558             }
22559              
22560         }
22561         
22562         
22563         if (align ==='left' && this.fieldLabel.length) {
22564 //                Roo.log("left and has label");
22565             cfg.cn = [
22566                 {
22567                     tag: 'label',
22568                     'for' :  id,
22569                     cls : 'control-label',
22570                     html : this.fieldLabel
22571                 },
22572                 {
22573                     cls : "", 
22574                     cn: [
22575                         inputblock
22576                     ]
22577                 }
22578             ];
22579             
22580             if (boxLabelCfg) {
22581                 cfg.cn[1].cn.push(boxLabelCfg);
22582             }
22583             
22584             if(this.labelWidth > 12){
22585                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
22586             }
22587             
22588             if(this.labelWidth < 13 && this.labelmd == 0){
22589                 this.labelmd = this.labelWidth;
22590             }
22591             
22592             if(this.labellg > 0){
22593                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
22594                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
22595             }
22596             
22597             if(this.labelmd > 0){
22598                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
22599                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
22600             }
22601             
22602             if(this.labelsm > 0){
22603                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
22604                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
22605             }
22606             
22607             if(this.labelxs > 0){
22608                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
22609                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
22610             }
22611             
22612         } else if ( this.fieldLabel.length) {
22613 //                Roo.log(" label");
22614                 cfg.cn = [
22615                    
22616                     {
22617                         tag: this.boxLabel ? 'span' : 'label',
22618                         'for': id,
22619                         cls: 'control-label box-input-label',
22620                         //cls : 'input-group-addon',
22621                         html : this.fieldLabel
22622                     },
22623                     
22624                     inputblock
22625                     
22626                 ];
22627                 if (boxLabelCfg) {
22628                     cfg.cn.push(boxLabelCfg);
22629                 }
22630
22631         } else {
22632             
22633 //                Roo.log(" no label && no align");
22634                 cfg.cn = [  inputblock ] ;
22635                 if (boxLabelCfg) {
22636                     cfg.cn.push(boxLabelCfg);
22637                 }
22638
22639                 
22640         }
22641         
22642        
22643         
22644         if(this.inputType != 'radio'){
22645             cfg.cn.push(hidden);
22646         }
22647         
22648         return cfg;
22649         
22650     },
22651     
22652     /**
22653      * return the real input element.
22654      */
22655     inputEl: function ()
22656     {
22657         return this.el.select('input.roo-' + this.inputType,true).first();
22658     },
22659     hiddenEl: function ()
22660     {
22661         return this.el.select('input.roo-hidden-value',true).first();
22662     },
22663     
22664     labelEl: function()
22665     {
22666         return this.el.select('label.control-label',true).first();
22667     },
22668     /* depricated... */
22669     
22670     label: function()
22671     {
22672         return this.labelEl();
22673     },
22674     
22675     boxLabelEl: function()
22676     {
22677         return this.el.select('label.box-label',true).first();
22678     },
22679     
22680     initEvents : function()
22681     {
22682 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
22683         
22684         this.inputEl().on('click', this.onClick,  this);
22685         
22686         if (this.boxLabel) { 
22687             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
22688         }
22689         
22690         this.startValue = this.getValue();
22691         
22692         if(this.groupId){
22693             Roo.bootstrap.CheckBox.register(this);
22694         }
22695     },
22696     
22697     onClick : function(e)
22698     {   
22699         if(this.fireEvent('click', this, e) !== false){
22700             this.setChecked(!this.checked);
22701         }
22702         
22703     },
22704     
22705     setChecked : function(state,suppressEvent)
22706     {
22707         this.startValue = this.getValue();
22708
22709         if(this.inputType == 'radio'){
22710             
22711             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22712                 e.dom.checked = false;
22713             });
22714             
22715             this.inputEl().dom.checked = true;
22716             
22717             this.inputEl().dom.value = this.inputValue;
22718             
22719             if(suppressEvent !== true){
22720                 this.fireEvent('check', this, true);
22721             }
22722             
22723             this.validate();
22724             
22725             return;
22726         }
22727         
22728         this.checked = state;
22729         
22730         this.inputEl().dom.checked = state;
22731         
22732         
22733         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
22734         
22735         if(suppressEvent !== true){
22736             this.fireEvent('check', this, state);
22737         }
22738         
22739         this.validate();
22740     },
22741     
22742     getValue : function()
22743     {
22744         if(this.inputType == 'radio'){
22745             return this.getGroupValue();
22746         }
22747         
22748         return this.hiddenEl().dom.value;
22749         
22750     },
22751     
22752     getGroupValue : function()
22753     {
22754         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
22755             return '';
22756         }
22757         
22758         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
22759     },
22760     
22761     setValue : function(v,suppressEvent)
22762     {
22763         if(this.inputType == 'radio'){
22764             this.setGroupValue(v, suppressEvent);
22765             return;
22766         }
22767         
22768         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
22769         
22770         this.validate();
22771     },
22772     
22773     setGroupValue : function(v, suppressEvent)
22774     {
22775         this.startValue = this.getValue();
22776         
22777         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22778             e.dom.checked = false;
22779             
22780             if(e.dom.value == v){
22781                 e.dom.checked = true;
22782             }
22783         });
22784         
22785         if(suppressEvent !== true){
22786             this.fireEvent('check', this, true);
22787         }
22788
22789         this.validate();
22790         
22791         return;
22792     },
22793     
22794     validate : function()
22795     {
22796         if(this.getVisibilityEl().hasClass('hidden')){
22797             return true;
22798         }
22799         
22800         if(
22801                 this.disabled || 
22802                 (this.inputType == 'radio' && this.validateRadio()) ||
22803                 (this.inputType == 'checkbox' && this.validateCheckbox())
22804         ){
22805             this.markValid();
22806             return true;
22807         }
22808         
22809         this.markInvalid();
22810         return false;
22811     },
22812     
22813     validateRadio : function()
22814     {
22815         if(this.getVisibilityEl().hasClass('hidden')){
22816             return true;
22817         }
22818         
22819         if(this.allowBlank){
22820             return true;
22821         }
22822         
22823         var valid = false;
22824         
22825         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22826             if(!e.dom.checked){
22827                 return;
22828             }
22829             
22830             valid = true;
22831             
22832             return false;
22833         });
22834         
22835         return valid;
22836     },
22837     
22838     validateCheckbox : function()
22839     {
22840         if(!this.groupId){
22841             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
22842             //return (this.getValue() == this.inputValue) ? true : false;
22843         }
22844         
22845         var group = Roo.bootstrap.CheckBox.get(this.groupId);
22846         
22847         if(!group){
22848             return false;
22849         }
22850         
22851         var r = false;
22852         
22853         for(var i in group){
22854             if(group[i].el.isVisible(true)){
22855                 r = false;
22856                 break;
22857             }
22858             
22859             r = true;
22860         }
22861         
22862         for(var i in group){
22863             if(r){
22864                 break;
22865             }
22866             
22867             r = (group[i].getValue() == group[i].inputValue) ? true : false;
22868         }
22869         
22870         return r;
22871     },
22872     
22873     /**
22874      * Mark this field as valid
22875      */
22876     markValid : function()
22877     {
22878         var _this = this;
22879         
22880         this.fireEvent('valid', this);
22881         
22882         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
22883         
22884         if(this.groupId){
22885             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
22886         }
22887         
22888         if(label){
22889             label.markValid();
22890         }
22891
22892         if(this.inputType == 'radio'){
22893             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22894                 var fg = e.findParent('.form-group', false, true);
22895                 if (Roo.bootstrap.version == 3) {
22896                     fg.removeClass([_this.invalidClass, _this.validClass]);
22897                     fg.addClass(_this.validClass);
22898                 } else {
22899                     fg.removeClass(['is-valid', 'is-invalid']);
22900                     fg.addClass('is-valid');
22901                 }
22902             });
22903             
22904             return;
22905         }
22906
22907         if(!this.groupId){
22908             var fg = this.el.findParent('.form-group', false, true);
22909             if (Roo.bootstrap.version == 3) {
22910                 fg.removeClass([this.invalidClass, this.validClass]);
22911                 fg.addClass(this.validClass);
22912             } else {
22913                 fg.removeClass(['is-valid', 'is-invalid']);
22914                 fg.addClass('is-valid');
22915             }
22916             return;
22917         }
22918         
22919         var group = Roo.bootstrap.CheckBox.get(this.groupId);
22920         
22921         if(!group){
22922             return;
22923         }
22924         
22925         for(var i in group){
22926             var fg = group[i].el.findParent('.form-group', false, true);
22927             if (Roo.bootstrap.version == 3) {
22928                 fg.removeClass([this.invalidClass, this.validClass]);
22929                 fg.addClass(this.validClass);
22930             } else {
22931                 fg.removeClass(['is-valid', 'is-invalid']);
22932                 fg.addClass('is-valid');
22933             }
22934         }
22935     },
22936     
22937      /**
22938      * Mark this field as invalid
22939      * @param {String} msg The validation message
22940      */
22941     markInvalid : function(msg)
22942     {
22943         if(this.allowBlank){
22944             return;
22945         }
22946         
22947         var _this = this;
22948         
22949         this.fireEvent('invalid', this, msg);
22950         
22951         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
22952         
22953         if(this.groupId){
22954             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
22955         }
22956         
22957         if(label){
22958             label.markInvalid();
22959         }
22960             
22961         if(this.inputType == 'radio'){
22962             
22963             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22964                 var fg = e.findParent('.form-group', false, true);
22965                 if (Roo.bootstrap.version == 3) {
22966                     fg.removeClass([_this.invalidClass, _this.validClass]);
22967                     fg.addClass(_this.invalidClass);
22968                 } else {
22969                     fg.removeClass(['is-invalid', 'is-valid']);
22970                     fg.addClass('is-invalid');
22971                 }
22972             });
22973             
22974             return;
22975         }
22976         
22977         if(!this.groupId){
22978             var fg = this.el.findParent('.form-group', false, true);
22979             if (Roo.bootstrap.version == 3) {
22980                 fg.removeClass([_this.invalidClass, _this.validClass]);
22981                 fg.addClass(_this.invalidClass);
22982             } else {
22983                 fg.removeClass(['is-invalid', 'is-valid']);
22984                 fg.addClass('is-invalid');
22985             }
22986             return;
22987         }
22988         
22989         var group = Roo.bootstrap.CheckBox.get(this.groupId);
22990         
22991         if(!group){
22992             return;
22993         }
22994         
22995         for(var i in group){
22996             var fg = group[i].el.findParent('.form-group', false, true);
22997             if (Roo.bootstrap.version == 3) {
22998                 fg.removeClass([_this.invalidClass, _this.validClass]);
22999                 fg.addClass(_this.invalidClass);
23000             } else {
23001                 fg.removeClass(['is-invalid', 'is-valid']);
23002                 fg.addClass('is-invalid');
23003             }
23004         }
23005         
23006     },
23007     
23008     clearInvalid : function()
23009     {
23010         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23011         
23012         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23013         
23014         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23015         
23016         if (label && label.iconEl) {
23017             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23018             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23019         }
23020     },
23021     
23022     disable : function()
23023     {
23024         if(this.inputType != 'radio'){
23025             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23026             return;
23027         }
23028         
23029         var _this = this;
23030         
23031         if(this.rendered){
23032             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23033                 _this.getActionEl().addClass(this.disabledClass);
23034                 e.dom.disabled = true;
23035             });
23036         }
23037         
23038         this.disabled = true;
23039         this.fireEvent("disable", this);
23040         return this;
23041     },
23042
23043     enable : function()
23044     {
23045         if(this.inputType != 'radio'){
23046             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23047             return;
23048         }
23049         
23050         var _this = this;
23051         
23052         if(this.rendered){
23053             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23054                 _this.getActionEl().removeClass(this.disabledClass);
23055                 e.dom.disabled = false;
23056             });
23057         }
23058         
23059         this.disabled = false;
23060         this.fireEvent("enable", this);
23061         return this;
23062     },
23063     
23064     setBoxLabel : function(v)
23065     {
23066         this.boxLabel = v;
23067         
23068         if(this.rendered){
23069             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23070         }
23071     }
23072
23073 });
23074
23075 Roo.apply(Roo.bootstrap.CheckBox, {
23076     
23077     groups: {},
23078     
23079      /**
23080     * register a CheckBox Group
23081     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23082     */
23083     register : function(checkbox)
23084     {
23085         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23086             this.groups[checkbox.groupId] = {};
23087         }
23088         
23089         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23090             return;
23091         }
23092         
23093         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23094         
23095     },
23096     /**
23097     * fetch a CheckBox Group based on the group ID
23098     * @param {string} the group ID
23099     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23100     */
23101     get: function(groupId) {
23102         if (typeof(this.groups[groupId]) == 'undefined') {
23103             return false;
23104         }
23105         
23106         return this.groups[groupId] ;
23107     }
23108     
23109     
23110 });
23111 /*
23112  * - LGPL
23113  *
23114  * RadioItem
23115  * 
23116  */
23117
23118 /**
23119  * @class Roo.bootstrap.Radio
23120  * @extends Roo.bootstrap.Component
23121  * Bootstrap Radio class
23122  * @cfg {String} boxLabel - the label associated
23123  * @cfg {String} value - the value of radio
23124  * 
23125  * @constructor
23126  * Create a new Radio
23127  * @param {Object} config The config object
23128  */
23129 Roo.bootstrap.Radio = function(config){
23130     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23131     
23132 };
23133
23134 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23135     
23136     boxLabel : '',
23137     
23138     value : '',
23139     
23140     getAutoCreate : function()
23141     {
23142         var cfg = {
23143             tag : 'div',
23144             cls : 'form-group radio',
23145             cn : [
23146                 {
23147                     tag : 'label',
23148                     cls : 'box-label',
23149                     html : this.boxLabel
23150                 }
23151             ]
23152         };
23153         
23154         return cfg;
23155     },
23156     
23157     initEvents : function() 
23158     {
23159         this.parent().register(this);
23160         
23161         this.el.on('click', this.onClick, this);
23162         
23163     },
23164     
23165     onClick : function(e)
23166     {
23167         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23168             this.setChecked(true);
23169         }
23170     },
23171     
23172     setChecked : function(state, suppressEvent)
23173     {
23174         this.parent().setValue(this.value, suppressEvent);
23175         
23176     },
23177     
23178     setBoxLabel : function(v)
23179     {
23180         this.boxLabel = v;
23181         
23182         if(this.rendered){
23183             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23184         }
23185     }
23186     
23187 });
23188  
23189
23190  /*
23191  * - LGPL
23192  *
23193  * Input
23194  * 
23195  */
23196
23197 /**
23198  * @class Roo.bootstrap.SecurePass
23199  * @extends Roo.bootstrap.Input
23200  * Bootstrap SecurePass class
23201  *
23202  * 
23203  * @constructor
23204  * Create a new SecurePass
23205  * @param {Object} config The config object
23206  */
23207  
23208 Roo.bootstrap.SecurePass = function (config) {
23209     // these go here, so the translation tool can replace them..
23210     this.errors = {
23211         PwdEmpty: "Please type a password, and then retype it to confirm.",
23212         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23213         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23214         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23215         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23216         FNInPwd: "Your password can't contain your first name. Please type a different password.",
23217         LNInPwd: "Your password can't contain your last name. Please type a different password.",
23218         TooWeak: "Your password is Too Weak."
23219     },
23220     this.meterLabel = "Password strength:";
23221     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23222     this.meterClass = [
23223         "roo-password-meter-tooweak", 
23224         "roo-password-meter-weak", 
23225         "roo-password-meter-medium", 
23226         "roo-password-meter-strong", 
23227         "roo-password-meter-grey"
23228     ];
23229     
23230     this.errors = {};
23231     
23232     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23233 }
23234
23235 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23236     /**
23237      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23238      * {
23239      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
23240      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23241      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23242      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23243      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23244      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
23245      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
23246      * })
23247      */
23248     // private
23249     
23250     meterWidth: 300,
23251     errorMsg :'',    
23252     errors: false,
23253     imageRoot: '/',
23254     /**
23255      * @cfg {String/Object} Label for the strength meter (defaults to
23256      * 'Password strength:')
23257      */
23258     // private
23259     meterLabel: '',
23260     /**
23261      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23262      * ['Weak', 'Medium', 'Strong'])
23263      */
23264     // private    
23265     pwdStrengths: false,    
23266     // private
23267     strength: 0,
23268     // private
23269     _lastPwd: null,
23270     // private
23271     kCapitalLetter: 0,
23272     kSmallLetter: 1,
23273     kDigit: 2,
23274     kPunctuation: 3,
23275     
23276     insecure: false,
23277     // private
23278     initEvents: function ()
23279     {
23280         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23281
23282         if (this.el.is('input[type=password]') && Roo.isSafari) {
23283             this.el.on('keydown', this.SafariOnKeyDown, this);
23284         }
23285
23286         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23287     },
23288     // private
23289     onRender: function (ct, position)
23290     {
23291         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23292         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23293         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23294
23295         this.trigger.createChild({
23296                    cn: [
23297                     {
23298                     //id: 'PwdMeter',
23299                     tag: 'div',
23300                     cls: 'roo-password-meter-grey col-xs-12',
23301                     style: {
23302                         //width: 0,
23303                         //width: this.meterWidth + 'px'                                                
23304                         }
23305                     },
23306                     {                            
23307                          cls: 'roo-password-meter-text'                          
23308                     }
23309                 ]            
23310         });
23311
23312          
23313         if (this.hideTrigger) {
23314             this.trigger.setDisplayed(false);
23315         }
23316         this.setSize(this.width || '', this.height || '');
23317     },
23318     // private
23319     onDestroy: function ()
23320     {
23321         if (this.trigger) {
23322             this.trigger.removeAllListeners();
23323             this.trigger.remove();
23324         }
23325         if (this.wrap) {
23326             this.wrap.remove();
23327         }
23328         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23329     },
23330     // private
23331     checkStrength: function ()
23332     {
23333         var pwd = this.inputEl().getValue();
23334         if (pwd == this._lastPwd) {
23335             return;
23336         }
23337
23338         var strength;
23339         if (this.ClientSideStrongPassword(pwd)) {
23340             strength = 3;
23341         } else if (this.ClientSideMediumPassword(pwd)) {
23342             strength = 2;
23343         } else if (this.ClientSideWeakPassword(pwd)) {
23344             strength = 1;
23345         } else {
23346             strength = 0;
23347         }
23348         
23349         Roo.log('strength1: ' + strength);
23350         
23351         //var pm = this.trigger.child('div/div/div').dom;
23352         var pm = this.trigger.child('div/div');
23353         pm.removeClass(this.meterClass);
23354         pm.addClass(this.meterClass[strength]);
23355                 
23356         
23357         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23358                 
23359         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23360         
23361         this._lastPwd = pwd;
23362     },
23363     reset: function ()
23364     {
23365         Roo.bootstrap.SecurePass.superclass.reset.call(this);
23366         
23367         this._lastPwd = '';
23368         
23369         var pm = this.trigger.child('div/div');
23370         pm.removeClass(this.meterClass);
23371         pm.addClass('roo-password-meter-grey');        
23372         
23373         
23374         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23375         
23376         pt.innerHTML = '';
23377         this.inputEl().dom.type='password';
23378     },
23379     // private
23380     validateValue: function (value)
23381     {
23382         
23383         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23384             return false;
23385         }
23386         if (value.length == 0) {
23387             if (this.allowBlank) {
23388                 this.clearInvalid();
23389                 return true;
23390             }
23391
23392             this.markInvalid(this.errors.PwdEmpty);
23393             this.errorMsg = this.errors.PwdEmpty;
23394             return false;
23395         }
23396         
23397         if(this.insecure){
23398             return true;
23399         }
23400         
23401         if ('[\x21-\x7e]*'.match(value)) {
23402             this.markInvalid(this.errors.PwdBadChar);
23403             this.errorMsg = this.errors.PwdBadChar;
23404             return false;
23405         }
23406         if (value.length < 6) {
23407             this.markInvalid(this.errors.PwdShort);
23408             this.errorMsg = this.errors.PwdShort;
23409             return false;
23410         }
23411         if (value.length > 16) {
23412             this.markInvalid(this.errors.PwdLong);
23413             this.errorMsg = this.errors.PwdLong;
23414             return false;
23415         }
23416         var strength;
23417         if (this.ClientSideStrongPassword(value)) {
23418             strength = 3;
23419         } else if (this.ClientSideMediumPassword(value)) {
23420             strength = 2;
23421         } else if (this.ClientSideWeakPassword(value)) {
23422             strength = 1;
23423         } else {
23424             strength = 0;
23425         }
23426
23427         
23428         if (strength < 2) {
23429             //this.markInvalid(this.errors.TooWeak);
23430             this.errorMsg = this.errors.TooWeak;
23431             //return false;
23432         }
23433         
23434         
23435         console.log('strength2: ' + strength);
23436         
23437         //var pm = this.trigger.child('div/div/div').dom;
23438         
23439         var pm = this.trigger.child('div/div');
23440         pm.removeClass(this.meterClass);
23441         pm.addClass(this.meterClass[strength]);
23442                 
23443         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23444                 
23445         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23446         
23447         this.errorMsg = ''; 
23448         return true;
23449     },
23450     // private
23451     CharacterSetChecks: function (type)
23452     {
23453         this.type = type;
23454         this.fResult = false;
23455     },
23456     // private
23457     isctype: function (character, type)
23458     {
23459         switch (type) {  
23460             case this.kCapitalLetter:
23461                 if (character >= 'A' && character <= 'Z') {
23462                     return true;
23463                 }
23464                 break;
23465             
23466             case this.kSmallLetter:
23467                 if (character >= 'a' && character <= 'z') {
23468                     return true;
23469                 }
23470                 break;
23471             
23472             case this.kDigit:
23473                 if (character >= '0' && character <= '9') {
23474                     return true;
23475                 }
23476                 break;
23477             
23478             case this.kPunctuation:
23479                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
23480                     return true;
23481                 }
23482                 break;
23483             
23484             default:
23485                 return false;
23486         }
23487
23488     },
23489     // private
23490     IsLongEnough: function (pwd, size)
23491     {
23492         return !(pwd == null || isNaN(size) || pwd.length < size);
23493     },
23494     // private
23495     SpansEnoughCharacterSets: function (word, nb)
23496     {
23497         if (!this.IsLongEnough(word, nb))
23498         {
23499             return false;
23500         }
23501
23502         var characterSetChecks = new Array(
23503             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
23504             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
23505         );
23506         
23507         for (var index = 0; index < word.length; ++index) {
23508             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23509                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
23510                     characterSetChecks[nCharSet].fResult = true;
23511                     break;
23512                 }
23513             }
23514         }
23515
23516         var nCharSets = 0;
23517         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23518             if (characterSetChecks[nCharSet].fResult) {
23519                 ++nCharSets;
23520             }
23521         }
23522
23523         if (nCharSets < nb) {
23524             return false;
23525         }
23526         return true;
23527     },
23528     // private
23529     ClientSideStrongPassword: function (pwd)
23530     {
23531         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
23532     },
23533     // private
23534     ClientSideMediumPassword: function (pwd)
23535     {
23536         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
23537     },
23538     // private
23539     ClientSideWeakPassword: function (pwd)
23540     {
23541         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
23542     }
23543           
23544 })//<script type="text/javascript">
23545
23546 /*
23547  * Based  Ext JS Library 1.1.1
23548  * Copyright(c) 2006-2007, Ext JS, LLC.
23549  * LGPL
23550  *
23551  */
23552  
23553 /**
23554  * @class Roo.HtmlEditorCore
23555  * @extends Roo.Component
23556  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
23557  *
23558  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23559  */
23560
23561 Roo.HtmlEditorCore = function(config){
23562     
23563     
23564     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
23565     
23566     
23567     this.addEvents({
23568         /**
23569          * @event initialize
23570          * Fires when the editor is fully initialized (including the iframe)
23571          * @param {Roo.HtmlEditorCore} this
23572          */
23573         initialize: true,
23574         /**
23575          * @event activate
23576          * Fires when the editor is first receives the focus. Any insertion must wait
23577          * until after this event.
23578          * @param {Roo.HtmlEditorCore} this
23579          */
23580         activate: true,
23581          /**
23582          * @event beforesync
23583          * Fires before the textarea is updated with content from the editor iframe. Return false
23584          * to cancel the sync.
23585          * @param {Roo.HtmlEditorCore} this
23586          * @param {String} html
23587          */
23588         beforesync: true,
23589          /**
23590          * @event beforepush
23591          * Fires before the iframe editor is updated with content from the textarea. Return false
23592          * to cancel the push.
23593          * @param {Roo.HtmlEditorCore} this
23594          * @param {String} html
23595          */
23596         beforepush: true,
23597          /**
23598          * @event sync
23599          * Fires when the textarea is updated with content from the editor iframe.
23600          * @param {Roo.HtmlEditorCore} this
23601          * @param {String} html
23602          */
23603         sync: true,
23604          /**
23605          * @event push
23606          * Fires when the iframe editor is updated with content from the textarea.
23607          * @param {Roo.HtmlEditorCore} this
23608          * @param {String} html
23609          */
23610         push: true,
23611         
23612         /**
23613          * @event editorevent
23614          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23615          * @param {Roo.HtmlEditorCore} this
23616          */
23617         editorevent: true
23618         
23619     });
23620     
23621     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
23622     
23623     // defaults : white / black...
23624     this.applyBlacklists();
23625     
23626     
23627     
23628 };
23629
23630
23631 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
23632
23633
23634      /**
23635      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
23636      */
23637     
23638     owner : false,
23639     
23640      /**
23641      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23642      *                        Roo.resizable.
23643      */
23644     resizable : false,
23645      /**
23646      * @cfg {Number} height (in pixels)
23647      */   
23648     height: 300,
23649    /**
23650      * @cfg {Number} width (in pixels)
23651      */   
23652     width: 500,
23653     
23654     /**
23655      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23656      * 
23657      */
23658     stylesheets: false,
23659     
23660     // id of frame..
23661     frameId: false,
23662     
23663     // private properties
23664     validationEvent : false,
23665     deferHeight: true,
23666     initialized : false,
23667     activated : false,
23668     sourceEditMode : false,
23669     onFocus : Roo.emptyFn,
23670     iframePad:3,
23671     hideMode:'offsets',
23672     
23673     clearUp: true,
23674     
23675     // blacklist + whitelisted elements..
23676     black: false,
23677     white: false,
23678      
23679     bodyCls : '',
23680
23681     /**
23682      * Protected method that will not generally be called directly. It
23683      * is called when the editor initializes the iframe with HTML contents. Override this method if you
23684      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
23685      */
23686     getDocMarkup : function(){
23687         // body styles..
23688         var st = '';
23689         
23690         // inherit styels from page...?? 
23691         if (this.stylesheets === false) {
23692             
23693             Roo.get(document.head).select('style').each(function(node) {
23694                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23695             });
23696             
23697             Roo.get(document.head).select('link').each(function(node) { 
23698                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23699             });
23700             
23701         } else if (!this.stylesheets.length) {
23702                 // simple..
23703                 st = '<style type="text/css">' +
23704                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23705                    '</style>';
23706         } else { 
23707             st = '<style type="text/css">' +
23708                     this.stylesheets +
23709                 '</style>';
23710         }
23711         
23712         st +=  '<style type="text/css">' +
23713             'IMG { cursor: pointer } ' +
23714         '</style>';
23715
23716         var cls = 'roo-htmleditor-body';
23717         
23718         if(this.bodyCls.length){
23719             cls += ' ' + this.bodyCls;
23720         }
23721         
23722         return '<html><head>' + st  +
23723             //<style type="text/css">' +
23724             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23725             //'</style>' +
23726             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
23727     },
23728
23729     // private
23730     onRender : function(ct, position)
23731     {
23732         var _t = this;
23733         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
23734         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
23735         
23736         
23737         this.el.dom.style.border = '0 none';
23738         this.el.dom.setAttribute('tabIndex', -1);
23739         this.el.addClass('x-hidden hide');
23740         
23741         
23742         
23743         if(Roo.isIE){ // fix IE 1px bogus margin
23744             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
23745         }
23746        
23747         
23748         this.frameId = Roo.id();
23749         
23750          
23751         
23752         var iframe = this.owner.wrap.createChild({
23753             tag: 'iframe',
23754             cls: 'form-control', // bootstrap..
23755             id: this.frameId,
23756             name: this.frameId,
23757             frameBorder : 'no',
23758             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
23759         }, this.el
23760         );
23761         
23762         
23763         this.iframe = iframe.dom;
23764
23765          this.assignDocWin();
23766         
23767         this.doc.designMode = 'on';
23768        
23769         this.doc.open();
23770         this.doc.write(this.getDocMarkup());
23771         this.doc.close();
23772
23773         
23774         var task = { // must defer to wait for browser to be ready
23775             run : function(){
23776                 //console.log("run task?" + this.doc.readyState);
23777                 this.assignDocWin();
23778                 if(this.doc.body || this.doc.readyState == 'complete'){
23779                     try {
23780                         this.doc.designMode="on";
23781                     } catch (e) {
23782                         return;
23783                     }
23784                     Roo.TaskMgr.stop(task);
23785                     this.initEditor.defer(10, this);
23786                 }
23787             },
23788             interval : 10,
23789             duration: 10000,
23790             scope: this
23791         };
23792         Roo.TaskMgr.start(task);
23793
23794     },
23795
23796     // private
23797     onResize : function(w, h)
23798     {
23799          Roo.log('resize: ' +w + ',' + h );
23800         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
23801         if(!this.iframe){
23802             return;
23803         }
23804         if(typeof w == 'number'){
23805             
23806             this.iframe.style.width = w + 'px';
23807         }
23808         if(typeof h == 'number'){
23809             
23810             this.iframe.style.height = h + 'px';
23811             if(this.doc){
23812                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
23813             }
23814         }
23815         
23816     },
23817
23818     /**
23819      * Toggles the editor between standard and source edit mode.
23820      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23821      */
23822     toggleSourceEdit : function(sourceEditMode){
23823         
23824         this.sourceEditMode = sourceEditMode === true;
23825         
23826         if(this.sourceEditMode){
23827  
23828             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
23829             
23830         }else{
23831             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
23832             //this.iframe.className = '';
23833             this.deferFocus();
23834         }
23835         //this.setSize(this.owner.wrap.getSize());
23836         //this.fireEvent('editmodechange', this, this.sourceEditMode);
23837     },
23838
23839     
23840   
23841
23842     /**
23843      * Protected method that will not generally be called directly. If you need/want
23844      * custom HTML cleanup, this is the method you should override.
23845      * @param {String} html The HTML to be cleaned
23846      * return {String} The cleaned HTML
23847      */
23848     cleanHtml : function(html){
23849         html = String(html);
23850         if(html.length > 5){
23851             if(Roo.isSafari){ // strip safari nonsense
23852                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
23853             }
23854         }
23855         if(html == '&nbsp;'){
23856             html = '';
23857         }
23858         return html;
23859     },
23860
23861     /**
23862      * HTML Editor -> Textarea
23863      * Protected method that will not generally be called directly. Syncs the contents
23864      * of the editor iframe with the textarea.
23865      */
23866     syncValue : function(){
23867         if(this.initialized){
23868             var bd = (this.doc.body || this.doc.documentElement);
23869             //this.cleanUpPaste(); -- this is done else where and causes havoc..
23870             var html = bd.innerHTML;
23871             if(Roo.isSafari){
23872                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
23873                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
23874                 if(m && m[1]){
23875                     html = '<div style="'+m[0]+'">' + html + '</div>';
23876                 }
23877             }
23878             html = this.cleanHtml(html);
23879             // fix up the special chars.. normaly like back quotes in word...
23880             // however we do not want to do this with chinese..
23881             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
23882                 
23883                 var cc = match.charCodeAt();
23884
23885                 // Get the character value, handling surrogate pairs
23886                 if (match.length == 2) {
23887                     // It's a surrogate pair, calculate the Unicode code point
23888                     var high = match.charCodeAt(0) - 0xD800;
23889                     var low  = match.charCodeAt(1) - 0xDC00;
23890                     cc = (high * 0x400) + low + 0x10000;
23891                 }  else if (
23892                     (cc >= 0x4E00 && cc < 0xA000 ) ||
23893                     (cc >= 0x3400 && cc < 0x4E00 ) ||
23894                     (cc >= 0xf900 && cc < 0xfb00 )
23895                 ) {
23896                         return match;
23897                 }  
23898          
23899                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
23900                 return "&#" + cc + ";";
23901                 
23902                 
23903             });
23904             
23905             
23906              
23907             if(this.owner.fireEvent('beforesync', this, html) !== false){
23908                 this.el.dom.value = html;
23909                 this.owner.fireEvent('sync', this, html);
23910             }
23911         }
23912     },
23913
23914     /**
23915      * Protected method that will not generally be called directly. Pushes the value of the textarea
23916      * into the iframe editor.
23917      */
23918     pushValue : function(){
23919         if(this.initialized){
23920             var v = this.el.dom.value.trim();
23921             
23922 //            if(v.length < 1){
23923 //                v = '&#160;';
23924 //            }
23925             
23926             if(this.owner.fireEvent('beforepush', this, v) !== false){
23927                 var d = (this.doc.body || this.doc.documentElement);
23928                 d.innerHTML = v;
23929                 this.cleanUpPaste();
23930                 this.el.dom.value = d.innerHTML;
23931                 this.owner.fireEvent('push', this, v);
23932             }
23933         }
23934     },
23935
23936     // private
23937     deferFocus : function(){
23938         this.focus.defer(10, this);
23939     },
23940
23941     // doc'ed in Field
23942     focus : function(){
23943         if(this.win && !this.sourceEditMode){
23944             this.win.focus();
23945         }else{
23946             this.el.focus();
23947         }
23948     },
23949     
23950     assignDocWin: function()
23951     {
23952         var iframe = this.iframe;
23953         
23954          if(Roo.isIE){
23955             this.doc = iframe.contentWindow.document;
23956             this.win = iframe.contentWindow;
23957         } else {
23958 //            if (!Roo.get(this.frameId)) {
23959 //                return;
23960 //            }
23961 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
23962 //            this.win = Roo.get(this.frameId).dom.contentWindow;
23963             
23964             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
23965                 return;
23966             }
23967             
23968             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
23969             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
23970         }
23971     },
23972     
23973     // private
23974     initEditor : function(){
23975         //console.log("INIT EDITOR");
23976         this.assignDocWin();
23977         
23978         
23979         
23980         this.doc.designMode="on";
23981         this.doc.open();
23982         this.doc.write(this.getDocMarkup());
23983         this.doc.close();
23984         
23985         var dbody = (this.doc.body || this.doc.documentElement);
23986         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
23987         // this copies styles from the containing element into thsi one..
23988         // not sure why we need all of this..
23989         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
23990         
23991         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
23992         //ss['background-attachment'] = 'fixed'; // w3c
23993         dbody.bgProperties = 'fixed'; // ie
23994         //Roo.DomHelper.applyStyles(dbody, ss);
23995         Roo.EventManager.on(this.doc, {
23996             //'mousedown': this.onEditorEvent,
23997             'mouseup': this.onEditorEvent,
23998             'dblclick': this.onEditorEvent,
23999             'click': this.onEditorEvent,
24000             'keyup': this.onEditorEvent,
24001             buffer:100,
24002             scope: this
24003         });
24004         if(Roo.isGecko){
24005             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24006         }
24007         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24008             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24009         }
24010         this.initialized = true;
24011
24012         this.owner.fireEvent('initialize', this);
24013         this.pushValue();
24014     },
24015
24016     // private
24017     onDestroy : function(){
24018         
24019         
24020         
24021         if(this.rendered){
24022             
24023             //for (var i =0; i < this.toolbars.length;i++) {
24024             //    // fixme - ask toolbars for heights?
24025             //    this.toolbars[i].onDestroy();
24026            // }
24027             
24028             //this.wrap.dom.innerHTML = '';
24029             //this.wrap.remove();
24030         }
24031     },
24032
24033     // private
24034     onFirstFocus : function(){
24035         
24036         this.assignDocWin();
24037         
24038         
24039         this.activated = true;
24040          
24041     
24042         if(Roo.isGecko){ // prevent silly gecko errors
24043             this.win.focus();
24044             var s = this.win.getSelection();
24045             if(!s.focusNode || s.focusNode.nodeType != 3){
24046                 var r = s.getRangeAt(0);
24047                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24048                 r.collapse(true);
24049                 this.deferFocus();
24050             }
24051             try{
24052                 this.execCmd('useCSS', true);
24053                 this.execCmd('styleWithCSS', false);
24054             }catch(e){}
24055         }
24056         this.owner.fireEvent('activate', this);
24057     },
24058
24059     // private
24060     adjustFont: function(btn){
24061         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24062         //if(Roo.isSafari){ // safari
24063         //    adjust *= 2;
24064        // }
24065         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24066         if(Roo.isSafari){ // safari
24067             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24068             v =  (v < 10) ? 10 : v;
24069             v =  (v > 48) ? 48 : v;
24070             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24071             
24072         }
24073         
24074         
24075         v = Math.max(1, v+adjust);
24076         
24077         this.execCmd('FontSize', v  );
24078     },
24079
24080     onEditorEvent : function(e)
24081     {
24082         this.owner.fireEvent('editorevent', this, e);
24083       //  this.updateToolbar();
24084         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24085     },
24086
24087     insertTag : function(tg)
24088     {
24089         // could be a bit smarter... -> wrap the current selected tRoo..
24090         if (tg.toLowerCase() == 'span' ||
24091             tg.toLowerCase() == 'code' ||
24092             tg.toLowerCase() == 'sup' ||
24093             tg.toLowerCase() == 'sub' 
24094             ) {
24095             
24096             range = this.createRange(this.getSelection());
24097             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24098             wrappingNode.appendChild(range.extractContents());
24099             range.insertNode(wrappingNode);
24100
24101             return;
24102             
24103             
24104             
24105         }
24106         this.execCmd("formatblock",   tg);
24107         
24108     },
24109     
24110     insertText : function(txt)
24111     {
24112         
24113         
24114         var range = this.createRange();
24115         range.deleteContents();
24116                //alert(Sender.getAttribute('label'));
24117                
24118         range.insertNode(this.doc.createTextNode(txt));
24119     } ,
24120     
24121      
24122
24123     /**
24124      * Executes a Midas editor command on the editor document and performs necessary focus and
24125      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24126      * @param {String} cmd The Midas command
24127      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24128      */
24129     relayCmd : function(cmd, value){
24130         this.win.focus();
24131         this.execCmd(cmd, value);
24132         this.owner.fireEvent('editorevent', this);
24133         //this.updateToolbar();
24134         this.owner.deferFocus();
24135     },
24136
24137     /**
24138      * Executes a Midas editor command directly on the editor document.
24139      * For visual commands, you should use {@link #relayCmd} instead.
24140      * <b>This should only be called after the editor is initialized.</b>
24141      * @param {String} cmd The Midas command
24142      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24143      */
24144     execCmd : function(cmd, value){
24145         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24146         this.syncValue();
24147     },
24148  
24149  
24150    
24151     /**
24152      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24153      * to insert tRoo.
24154      * @param {String} text | dom node.. 
24155      */
24156     insertAtCursor : function(text)
24157     {
24158         
24159         if(!this.activated){
24160             return;
24161         }
24162         /*
24163         if(Roo.isIE){
24164             this.win.focus();
24165             var r = this.doc.selection.createRange();
24166             if(r){
24167                 r.collapse(true);
24168                 r.pasteHTML(text);
24169                 this.syncValue();
24170                 this.deferFocus();
24171             
24172             }
24173             return;
24174         }
24175         */
24176         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24177             this.win.focus();
24178             
24179             
24180             // from jquery ui (MIT licenced)
24181             var range, node;
24182             var win = this.win;
24183             
24184             if (win.getSelection && win.getSelection().getRangeAt) {
24185                 range = win.getSelection().getRangeAt(0);
24186                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24187                 range.insertNode(node);
24188             } else if (win.document.selection && win.document.selection.createRange) {
24189                 // no firefox support
24190                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24191                 win.document.selection.createRange().pasteHTML(txt);
24192             } else {
24193                 // no firefox support
24194                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24195                 this.execCmd('InsertHTML', txt);
24196             } 
24197             
24198             this.syncValue();
24199             
24200             this.deferFocus();
24201         }
24202     },
24203  // private
24204     mozKeyPress : function(e){
24205         if(e.ctrlKey){
24206             var c = e.getCharCode(), cmd;
24207           
24208             if(c > 0){
24209                 c = String.fromCharCode(c).toLowerCase();
24210                 switch(c){
24211                     case 'b':
24212                         cmd = 'bold';
24213                         break;
24214                     case 'i':
24215                         cmd = 'italic';
24216                         break;
24217                     
24218                     case 'u':
24219                         cmd = 'underline';
24220                         break;
24221                     
24222                     case 'v':
24223                         this.cleanUpPaste.defer(100, this);
24224                         return;
24225                         
24226                 }
24227                 if(cmd){
24228                     this.win.focus();
24229                     this.execCmd(cmd);
24230                     this.deferFocus();
24231                     e.preventDefault();
24232                 }
24233                 
24234             }
24235         }
24236     },
24237
24238     // private
24239     fixKeys : function(){ // load time branching for fastest keydown performance
24240         if(Roo.isIE){
24241             return function(e){
24242                 var k = e.getKey(), r;
24243                 if(k == e.TAB){
24244                     e.stopEvent();
24245                     r = this.doc.selection.createRange();
24246                     if(r){
24247                         r.collapse(true);
24248                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24249                         this.deferFocus();
24250                     }
24251                     return;
24252                 }
24253                 
24254                 if(k == e.ENTER){
24255                     r = this.doc.selection.createRange();
24256                     if(r){
24257                         var target = r.parentElement();
24258                         if(!target || target.tagName.toLowerCase() != 'li'){
24259                             e.stopEvent();
24260                             r.pasteHTML('<br />');
24261                             r.collapse(false);
24262                             r.select();
24263                         }
24264                     }
24265                 }
24266                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24267                     this.cleanUpPaste.defer(100, this);
24268                     return;
24269                 }
24270                 
24271                 
24272             };
24273         }else if(Roo.isOpera){
24274             return function(e){
24275                 var k = e.getKey();
24276                 if(k == e.TAB){
24277                     e.stopEvent();
24278                     this.win.focus();
24279                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24280                     this.deferFocus();
24281                 }
24282                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24283                     this.cleanUpPaste.defer(100, this);
24284                     return;
24285                 }
24286                 
24287             };
24288         }else if(Roo.isSafari){
24289             return function(e){
24290                 var k = e.getKey();
24291                 
24292                 if(k == e.TAB){
24293                     e.stopEvent();
24294                     this.execCmd('InsertText','\t');
24295                     this.deferFocus();
24296                     return;
24297                 }
24298                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24299                     this.cleanUpPaste.defer(100, this);
24300                     return;
24301                 }
24302                 
24303              };
24304         }
24305     }(),
24306     
24307     getAllAncestors: function()
24308     {
24309         var p = this.getSelectedNode();
24310         var a = [];
24311         if (!p) {
24312             a.push(p); // push blank onto stack..
24313             p = this.getParentElement();
24314         }
24315         
24316         
24317         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24318             a.push(p);
24319             p = p.parentNode;
24320         }
24321         a.push(this.doc.body);
24322         return a;
24323     },
24324     lastSel : false,
24325     lastSelNode : false,
24326     
24327     
24328     getSelection : function() 
24329     {
24330         this.assignDocWin();
24331         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24332     },
24333     
24334     getSelectedNode: function() 
24335     {
24336         // this may only work on Gecko!!!
24337         
24338         // should we cache this!!!!
24339         
24340         
24341         
24342          
24343         var range = this.createRange(this.getSelection()).cloneRange();
24344         
24345         if (Roo.isIE) {
24346             var parent = range.parentElement();
24347             while (true) {
24348                 var testRange = range.duplicate();
24349                 testRange.moveToElementText(parent);
24350                 if (testRange.inRange(range)) {
24351                     break;
24352                 }
24353                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24354                     break;
24355                 }
24356                 parent = parent.parentElement;
24357             }
24358             return parent;
24359         }
24360         
24361         // is ancestor a text element.
24362         var ac =  range.commonAncestorContainer;
24363         if (ac.nodeType == 3) {
24364             ac = ac.parentNode;
24365         }
24366         
24367         var ar = ac.childNodes;
24368          
24369         var nodes = [];
24370         var other_nodes = [];
24371         var has_other_nodes = false;
24372         for (var i=0;i<ar.length;i++) {
24373             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24374                 continue;
24375             }
24376             // fullly contained node.
24377             
24378             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24379                 nodes.push(ar[i]);
24380                 continue;
24381             }
24382             
24383             // probably selected..
24384             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24385                 other_nodes.push(ar[i]);
24386                 continue;
24387             }
24388             // outer..
24389             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24390                 continue;
24391             }
24392             
24393             
24394             has_other_nodes = true;
24395         }
24396         if (!nodes.length && other_nodes.length) {
24397             nodes= other_nodes;
24398         }
24399         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24400             return false;
24401         }
24402         
24403         return nodes[0];
24404     },
24405     createRange: function(sel)
24406     {
24407         // this has strange effects when using with 
24408         // top toolbar - not sure if it's a great idea.
24409         //this.editor.contentWindow.focus();
24410         if (typeof sel != "undefined") {
24411             try {
24412                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24413             } catch(e) {
24414                 return this.doc.createRange();
24415             }
24416         } else {
24417             return this.doc.createRange();
24418         }
24419     },
24420     getParentElement: function()
24421     {
24422         
24423         this.assignDocWin();
24424         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24425         
24426         var range = this.createRange(sel);
24427          
24428         try {
24429             var p = range.commonAncestorContainer;
24430             while (p.nodeType == 3) { // text node
24431                 p = p.parentNode;
24432             }
24433             return p;
24434         } catch (e) {
24435             return null;
24436         }
24437     
24438     },
24439     /***
24440      *
24441      * Range intersection.. the hard stuff...
24442      *  '-1' = before
24443      *  '0' = hits..
24444      *  '1' = after.
24445      *         [ -- selected range --- ]
24446      *   [fail]                        [fail]
24447      *
24448      *    basically..
24449      *      if end is before start or  hits it. fail.
24450      *      if start is after end or hits it fail.
24451      *
24452      *   if either hits (but other is outside. - then it's not 
24453      *   
24454      *    
24455      **/
24456     
24457     
24458     // @see http://www.thismuchiknow.co.uk/?p=64.
24459     rangeIntersectsNode : function(range, node)
24460     {
24461         var nodeRange = node.ownerDocument.createRange();
24462         try {
24463             nodeRange.selectNode(node);
24464         } catch (e) {
24465             nodeRange.selectNodeContents(node);
24466         }
24467     
24468         var rangeStartRange = range.cloneRange();
24469         rangeStartRange.collapse(true);
24470     
24471         var rangeEndRange = range.cloneRange();
24472         rangeEndRange.collapse(false);
24473     
24474         var nodeStartRange = nodeRange.cloneRange();
24475         nodeStartRange.collapse(true);
24476     
24477         var nodeEndRange = nodeRange.cloneRange();
24478         nodeEndRange.collapse(false);
24479     
24480         return rangeStartRange.compareBoundaryPoints(
24481                  Range.START_TO_START, nodeEndRange) == -1 &&
24482                rangeEndRange.compareBoundaryPoints(
24483                  Range.START_TO_START, nodeStartRange) == 1;
24484         
24485          
24486     },
24487     rangeCompareNode : function(range, node)
24488     {
24489         var nodeRange = node.ownerDocument.createRange();
24490         try {
24491             nodeRange.selectNode(node);
24492         } catch (e) {
24493             nodeRange.selectNodeContents(node);
24494         }
24495         
24496         
24497         range.collapse(true);
24498     
24499         nodeRange.collapse(true);
24500      
24501         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
24502         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
24503          
24504         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
24505         
24506         var nodeIsBefore   =  ss == 1;
24507         var nodeIsAfter    = ee == -1;
24508         
24509         if (nodeIsBefore && nodeIsAfter) {
24510             return 0; // outer
24511         }
24512         if (!nodeIsBefore && nodeIsAfter) {
24513             return 1; //right trailed.
24514         }
24515         
24516         if (nodeIsBefore && !nodeIsAfter) {
24517             return 2;  // left trailed.
24518         }
24519         // fully contined.
24520         return 3;
24521     },
24522
24523     // private? - in a new class?
24524     cleanUpPaste :  function()
24525     {
24526         // cleans up the whole document..
24527         Roo.log('cleanuppaste');
24528         
24529         this.cleanUpChildren(this.doc.body);
24530         var clean = this.cleanWordChars(this.doc.body.innerHTML);
24531         if (clean != this.doc.body.innerHTML) {
24532             this.doc.body.innerHTML = clean;
24533         }
24534         
24535     },
24536     
24537     cleanWordChars : function(input) {// change the chars to hex code
24538         var he = Roo.HtmlEditorCore;
24539         
24540         var output = input;
24541         Roo.each(he.swapCodes, function(sw) { 
24542             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
24543             
24544             output = output.replace(swapper, sw[1]);
24545         });
24546         
24547         return output;
24548     },
24549     
24550     
24551     cleanUpChildren : function (n)
24552     {
24553         if (!n.childNodes.length) {
24554             return;
24555         }
24556         for (var i = n.childNodes.length-1; i > -1 ; i--) {
24557            this.cleanUpChild(n.childNodes[i]);
24558         }
24559     },
24560     
24561     
24562         
24563     
24564     cleanUpChild : function (node)
24565     {
24566         var ed = this;
24567         //console.log(node);
24568         if (node.nodeName == "#text") {
24569             // clean up silly Windows -- stuff?
24570             return; 
24571         }
24572         if (node.nodeName == "#comment") {
24573             node.parentNode.removeChild(node);
24574             // clean up silly Windows -- stuff?
24575             return; 
24576         }
24577         var lcname = node.tagName.toLowerCase();
24578         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
24579         // whitelist of tags..
24580         
24581         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
24582             // remove node.
24583             node.parentNode.removeChild(node);
24584             return;
24585             
24586         }
24587         
24588         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
24589         
24590         // spans with no attributes - just remove them..
24591         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
24592             remove_keep_children = true;
24593         }
24594         
24595         // remove <a name=....> as rendering on yahoo mailer is borked with this.
24596         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
24597         
24598         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
24599         //    remove_keep_children = true;
24600         //}
24601         
24602         if (remove_keep_children) {
24603             this.cleanUpChildren(node);
24604             // inserts everything just before this node...
24605             while (node.childNodes.length) {
24606                 var cn = node.childNodes[0];
24607                 node.removeChild(cn);
24608                 node.parentNode.insertBefore(cn, node);
24609             }
24610             node.parentNode.removeChild(node);
24611             return;
24612         }
24613         
24614         if (!node.attributes || !node.attributes.length) {
24615             
24616           
24617             
24618             
24619             this.cleanUpChildren(node);
24620             return;
24621         }
24622         
24623         function cleanAttr(n,v)
24624         {
24625             
24626             if (v.match(/^\./) || v.match(/^\//)) {
24627                 return;
24628             }
24629             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
24630                 return;
24631             }
24632             if (v.match(/^#/)) {
24633                 return;
24634             }
24635 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
24636             node.removeAttribute(n);
24637             
24638         }
24639         
24640         var cwhite = this.cwhite;
24641         var cblack = this.cblack;
24642             
24643         function cleanStyle(n,v)
24644         {
24645             if (v.match(/expression/)) { //XSS?? should we even bother..
24646                 node.removeAttribute(n);
24647                 return;
24648             }
24649             
24650             var parts = v.split(/;/);
24651             var clean = [];
24652             
24653             Roo.each(parts, function(p) {
24654                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
24655                 if (!p.length) {
24656                     return true;
24657                 }
24658                 var l = p.split(':').shift().replace(/\s+/g,'');
24659                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
24660                 
24661                 if ( cwhite.length && cblack.indexOf(l) > -1) {
24662 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24663                     //node.removeAttribute(n);
24664                     return true;
24665                 }
24666                 //Roo.log()
24667                 // only allow 'c whitelisted system attributes'
24668                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
24669 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24670                     //node.removeAttribute(n);
24671                     return true;
24672                 }
24673                 
24674                 
24675                  
24676                 
24677                 clean.push(p);
24678                 return true;
24679             });
24680             if (clean.length) { 
24681                 node.setAttribute(n, clean.join(';'));
24682             } else {
24683                 node.removeAttribute(n);
24684             }
24685             
24686         }
24687         
24688         
24689         for (var i = node.attributes.length-1; i > -1 ; i--) {
24690             var a = node.attributes[i];
24691             //console.log(a);
24692             
24693             if (a.name.toLowerCase().substr(0,2)=='on')  {
24694                 node.removeAttribute(a.name);
24695                 continue;
24696             }
24697             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
24698                 node.removeAttribute(a.name);
24699                 continue;
24700             }
24701             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
24702                 cleanAttr(a.name,a.value); // fixme..
24703                 continue;
24704             }
24705             if (a.name == 'style') {
24706                 cleanStyle(a.name,a.value);
24707                 continue;
24708             }
24709             /// clean up MS crap..
24710             // tecnically this should be a list of valid class'es..
24711             
24712             
24713             if (a.name == 'class') {
24714                 if (a.value.match(/^Mso/)) {
24715                     node.removeAttribute('class');
24716                 }
24717                 
24718                 if (a.value.match(/^body$/)) {
24719                     node.removeAttribute('class');
24720                 }
24721                 continue;
24722             }
24723             
24724             // style cleanup!?
24725             // class cleanup?
24726             
24727         }
24728         
24729         
24730         this.cleanUpChildren(node);
24731         
24732         
24733     },
24734     
24735     /**
24736      * Clean up MS wordisms...
24737      */
24738     cleanWord : function(node)
24739     {
24740         if (!node) {
24741             this.cleanWord(this.doc.body);
24742             return;
24743         }
24744         
24745         if(
24746                 node.nodeName == 'SPAN' &&
24747                 !node.hasAttributes() &&
24748                 node.childNodes.length == 1 &&
24749                 node.firstChild.nodeName == "#text"  
24750         ) {
24751             var textNode = node.firstChild;
24752             node.removeChild(textNode);
24753             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
24754                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
24755             }
24756             node.parentNode.insertBefore(textNode, node);
24757             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
24758                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
24759             }
24760             node.parentNode.removeChild(node);
24761         }
24762         
24763         if (node.nodeName == "#text") {
24764             // clean up silly Windows -- stuff?
24765             return; 
24766         }
24767         if (node.nodeName == "#comment") {
24768             node.parentNode.removeChild(node);
24769             // clean up silly Windows -- stuff?
24770             return; 
24771         }
24772         
24773         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
24774             node.parentNode.removeChild(node);
24775             return;
24776         }
24777         //Roo.log(node.tagName);
24778         // remove - but keep children..
24779         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
24780             //Roo.log('-- removed');
24781             while (node.childNodes.length) {
24782                 var cn = node.childNodes[0];
24783                 node.removeChild(cn);
24784                 node.parentNode.insertBefore(cn, node);
24785                 // move node to parent - and clean it..
24786                 this.cleanWord(cn);
24787             }
24788             node.parentNode.removeChild(node);
24789             /// no need to iterate chidlren = it's got none..
24790             //this.iterateChildren(node, this.cleanWord);
24791             return;
24792         }
24793         // clean styles
24794         if (node.className.length) {
24795             
24796             var cn = node.className.split(/\W+/);
24797             var cna = [];
24798             Roo.each(cn, function(cls) {
24799                 if (cls.match(/Mso[a-zA-Z]+/)) {
24800                     return;
24801                 }
24802                 cna.push(cls);
24803             });
24804             node.className = cna.length ? cna.join(' ') : '';
24805             if (!cna.length) {
24806                 node.removeAttribute("class");
24807             }
24808         }
24809         
24810         if (node.hasAttribute("lang")) {
24811             node.removeAttribute("lang");
24812         }
24813         
24814         if (node.hasAttribute("style")) {
24815             
24816             var styles = node.getAttribute("style").split(";");
24817             var nstyle = [];
24818             Roo.each(styles, function(s) {
24819                 if (!s.match(/:/)) {
24820                     return;
24821                 }
24822                 var kv = s.split(":");
24823                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
24824                     return;
24825                 }
24826                 // what ever is left... we allow.
24827                 nstyle.push(s);
24828             });
24829             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
24830             if (!nstyle.length) {
24831                 node.removeAttribute('style');
24832             }
24833         }
24834         this.iterateChildren(node, this.cleanWord);
24835         
24836         
24837         
24838     },
24839     /**
24840      * iterateChildren of a Node, calling fn each time, using this as the scole..
24841      * @param {DomNode} node node to iterate children of.
24842      * @param {Function} fn method of this class to call on each item.
24843      */
24844     iterateChildren : function(node, fn)
24845     {
24846         if (!node.childNodes.length) {
24847                 return;
24848         }
24849         for (var i = node.childNodes.length-1; i > -1 ; i--) {
24850            fn.call(this, node.childNodes[i])
24851         }
24852     },
24853     
24854     
24855     /**
24856      * cleanTableWidths.
24857      *
24858      * Quite often pasting from word etc.. results in tables with column and widths.
24859      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
24860      *
24861      */
24862     cleanTableWidths : function(node)
24863     {
24864          
24865          
24866         if (!node) {
24867             this.cleanTableWidths(this.doc.body);
24868             return;
24869         }
24870         
24871         // ignore list...
24872         if (node.nodeName == "#text" || node.nodeName == "#comment") {
24873             return; 
24874         }
24875         Roo.log(node.tagName);
24876         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
24877             this.iterateChildren(node, this.cleanTableWidths);
24878             return;
24879         }
24880         if (node.hasAttribute('width')) {
24881             node.removeAttribute('width');
24882         }
24883         
24884          
24885         if (node.hasAttribute("style")) {
24886             // pretty basic...
24887             
24888             var styles = node.getAttribute("style").split(";");
24889             var nstyle = [];
24890             Roo.each(styles, function(s) {
24891                 if (!s.match(/:/)) {
24892                     return;
24893                 }
24894                 var kv = s.split(":");
24895                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
24896                     return;
24897                 }
24898                 // what ever is left... we allow.
24899                 nstyle.push(s);
24900             });
24901             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
24902             if (!nstyle.length) {
24903                 node.removeAttribute('style');
24904             }
24905         }
24906         
24907         this.iterateChildren(node, this.cleanTableWidths);
24908         
24909         
24910     },
24911     
24912     
24913     
24914     
24915     domToHTML : function(currentElement, depth, nopadtext) {
24916         
24917         depth = depth || 0;
24918         nopadtext = nopadtext || false;
24919     
24920         if (!currentElement) {
24921             return this.domToHTML(this.doc.body);
24922         }
24923         
24924         //Roo.log(currentElement);
24925         var j;
24926         var allText = false;
24927         var nodeName = currentElement.nodeName;
24928         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
24929         
24930         if  (nodeName == '#text') {
24931             
24932             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
24933         }
24934         
24935         
24936         var ret = '';
24937         if (nodeName != 'BODY') {
24938              
24939             var i = 0;
24940             // Prints the node tagName, such as <A>, <IMG>, etc
24941             if (tagName) {
24942                 var attr = [];
24943                 for(i = 0; i < currentElement.attributes.length;i++) {
24944                     // quoting?
24945                     var aname = currentElement.attributes.item(i).name;
24946                     if (!currentElement.attributes.item(i).value.length) {
24947                         continue;
24948                     }
24949                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
24950                 }
24951                 
24952                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
24953             } 
24954             else {
24955                 
24956                 // eack
24957             }
24958         } else {
24959             tagName = false;
24960         }
24961         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
24962             return ret;
24963         }
24964         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
24965             nopadtext = true;
24966         }
24967         
24968         
24969         // Traverse the tree
24970         i = 0;
24971         var currentElementChild = currentElement.childNodes.item(i);
24972         var allText = true;
24973         var innerHTML  = '';
24974         lastnode = '';
24975         while (currentElementChild) {
24976             // Formatting code (indent the tree so it looks nice on the screen)
24977             var nopad = nopadtext;
24978             if (lastnode == 'SPAN') {
24979                 nopad  = true;
24980             }
24981             // text
24982             if  (currentElementChild.nodeName == '#text') {
24983                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
24984                 toadd = nopadtext ? toadd : toadd.trim();
24985                 if (!nopad && toadd.length > 80) {
24986                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
24987                 }
24988                 innerHTML  += toadd;
24989                 
24990                 i++;
24991                 currentElementChild = currentElement.childNodes.item(i);
24992                 lastNode = '';
24993                 continue;
24994             }
24995             allText = false;
24996             
24997             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
24998                 
24999             // Recursively traverse the tree structure of the child node
25000             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25001             lastnode = currentElementChild.nodeName;
25002             i++;
25003             currentElementChild=currentElement.childNodes.item(i);
25004         }
25005         
25006         ret += innerHTML;
25007         
25008         if (!allText) {
25009                 // The remaining code is mostly for formatting the tree
25010             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25011         }
25012         
25013         
25014         if (tagName) {
25015             ret+= "</"+tagName+">";
25016         }
25017         return ret;
25018         
25019     },
25020         
25021     applyBlacklists : function()
25022     {
25023         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25024         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25025         
25026         this.white = [];
25027         this.black = [];
25028         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25029             if (b.indexOf(tag) > -1) {
25030                 return;
25031             }
25032             this.white.push(tag);
25033             
25034         }, this);
25035         
25036         Roo.each(w, function(tag) {
25037             if (b.indexOf(tag) > -1) {
25038                 return;
25039             }
25040             if (this.white.indexOf(tag) > -1) {
25041                 return;
25042             }
25043             this.white.push(tag);
25044             
25045         }, this);
25046         
25047         
25048         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25049             if (w.indexOf(tag) > -1) {
25050                 return;
25051             }
25052             this.black.push(tag);
25053             
25054         }, this);
25055         
25056         Roo.each(b, function(tag) {
25057             if (w.indexOf(tag) > -1) {
25058                 return;
25059             }
25060             if (this.black.indexOf(tag) > -1) {
25061                 return;
25062             }
25063             this.black.push(tag);
25064             
25065         }, this);
25066         
25067         
25068         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25069         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25070         
25071         this.cwhite = [];
25072         this.cblack = [];
25073         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25074             if (b.indexOf(tag) > -1) {
25075                 return;
25076             }
25077             this.cwhite.push(tag);
25078             
25079         }, this);
25080         
25081         Roo.each(w, function(tag) {
25082             if (b.indexOf(tag) > -1) {
25083                 return;
25084             }
25085             if (this.cwhite.indexOf(tag) > -1) {
25086                 return;
25087             }
25088             this.cwhite.push(tag);
25089             
25090         }, this);
25091         
25092         
25093         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25094             if (w.indexOf(tag) > -1) {
25095                 return;
25096             }
25097             this.cblack.push(tag);
25098             
25099         }, this);
25100         
25101         Roo.each(b, function(tag) {
25102             if (w.indexOf(tag) > -1) {
25103                 return;
25104             }
25105             if (this.cblack.indexOf(tag) > -1) {
25106                 return;
25107             }
25108             this.cblack.push(tag);
25109             
25110         }, this);
25111     },
25112     
25113     setStylesheets : function(stylesheets)
25114     {
25115         if(typeof(stylesheets) == 'string'){
25116             Roo.get(this.iframe.contentDocument.head).createChild({
25117                 tag : 'link',
25118                 rel : 'stylesheet',
25119                 type : 'text/css',
25120                 href : stylesheets
25121             });
25122             
25123             return;
25124         }
25125         var _this = this;
25126      
25127         Roo.each(stylesheets, function(s) {
25128             if(!s.length){
25129                 return;
25130             }
25131             
25132             Roo.get(_this.iframe.contentDocument.head).createChild({
25133                 tag : 'link',
25134                 rel : 'stylesheet',
25135                 type : 'text/css',
25136                 href : s
25137             });
25138         });
25139
25140         
25141     },
25142     
25143     removeStylesheets : function()
25144     {
25145         var _this = this;
25146         
25147         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25148             s.remove();
25149         });
25150     },
25151     
25152     setStyle : function(style)
25153     {
25154         Roo.get(this.iframe.contentDocument.head).createChild({
25155             tag : 'style',
25156             type : 'text/css',
25157             html : style
25158         });
25159
25160         return;
25161     }
25162     
25163     // hide stuff that is not compatible
25164     /**
25165      * @event blur
25166      * @hide
25167      */
25168     /**
25169      * @event change
25170      * @hide
25171      */
25172     /**
25173      * @event focus
25174      * @hide
25175      */
25176     /**
25177      * @event specialkey
25178      * @hide
25179      */
25180     /**
25181      * @cfg {String} fieldClass @hide
25182      */
25183     /**
25184      * @cfg {String} focusClass @hide
25185      */
25186     /**
25187      * @cfg {String} autoCreate @hide
25188      */
25189     /**
25190      * @cfg {String} inputType @hide
25191      */
25192     /**
25193      * @cfg {String} invalidClass @hide
25194      */
25195     /**
25196      * @cfg {String} invalidText @hide
25197      */
25198     /**
25199      * @cfg {String} msgFx @hide
25200      */
25201     /**
25202      * @cfg {String} validateOnBlur @hide
25203      */
25204 });
25205
25206 Roo.HtmlEditorCore.white = [
25207         'area', 'br', 'img', 'input', 'hr', 'wbr',
25208         
25209        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25210        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25211        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25212        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25213        'table',   'ul',         'xmp', 
25214        
25215        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25216       'thead',   'tr', 
25217      
25218       'dir', 'menu', 'ol', 'ul', 'dl',
25219        
25220       'embed',  'object'
25221 ];
25222
25223
25224 Roo.HtmlEditorCore.black = [
25225     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25226         'applet', // 
25227         'base',   'basefont', 'bgsound', 'blink',  'body', 
25228         'frame',  'frameset', 'head',    'html',   'ilayer', 
25229         'iframe', 'layer',  'link',     'meta',    'object',   
25230         'script', 'style' ,'title',  'xml' // clean later..
25231 ];
25232 Roo.HtmlEditorCore.clean = [
25233     'script', 'style', 'title', 'xml'
25234 ];
25235 Roo.HtmlEditorCore.remove = [
25236     'font'
25237 ];
25238 // attributes..
25239
25240 Roo.HtmlEditorCore.ablack = [
25241     'on'
25242 ];
25243     
25244 Roo.HtmlEditorCore.aclean = [ 
25245     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
25246 ];
25247
25248 // protocols..
25249 Roo.HtmlEditorCore.pwhite= [
25250         'http',  'https',  'mailto'
25251 ];
25252
25253 // white listed style attributes.
25254 Roo.HtmlEditorCore.cwhite= [
25255       //  'text-align', /// default is to allow most things..
25256       
25257          
25258 //        'font-size'//??
25259 ];
25260
25261 // black listed style attributes.
25262 Roo.HtmlEditorCore.cblack= [
25263       //  'font-size' -- this can be set by the project 
25264 ];
25265
25266
25267 Roo.HtmlEditorCore.swapCodes   =[ 
25268     [    8211, "--" ], 
25269     [    8212, "--" ], 
25270     [    8216,  "'" ],  
25271     [    8217, "'" ],  
25272     [    8220, '"' ],  
25273     [    8221, '"' ],  
25274     [    8226, "*" ],  
25275     [    8230, "..." ]
25276 ]; 
25277
25278     /*
25279  * - LGPL
25280  *
25281  * HtmlEditor
25282  * 
25283  */
25284
25285 /**
25286  * @class Roo.bootstrap.HtmlEditor
25287  * @extends Roo.bootstrap.TextArea
25288  * Bootstrap HtmlEditor class
25289
25290  * @constructor
25291  * Create a new HtmlEditor
25292  * @param {Object} config The config object
25293  */
25294
25295 Roo.bootstrap.HtmlEditor = function(config){
25296     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25297     if (!this.toolbars) {
25298         this.toolbars = [];
25299     }
25300     
25301     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25302     this.addEvents({
25303             /**
25304              * @event initialize
25305              * Fires when the editor is fully initialized (including the iframe)
25306              * @param {HtmlEditor} this
25307              */
25308             initialize: true,
25309             /**
25310              * @event activate
25311              * Fires when the editor is first receives the focus. Any insertion must wait
25312              * until after this event.
25313              * @param {HtmlEditor} this
25314              */
25315             activate: true,
25316              /**
25317              * @event beforesync
25318              * Fires before the textarea is updated with content from the editor iframe. Return false
25319              * to cancel the sync.
25320              * @param {HtmlEditor} this
25321              * @param {String} html
25322              */
25323             beforesync: true,
25324              /**
25325              * @event beforepush
25326              * Fires before the iframe editor is updated with content from the textarea. Return false
25327              * to cancel the push.
25328              * @param {HtmlEditor} this
25329              * @param {String} html
25330              */
25331             beforepush: true,
25332              /**
25333              * @event sync
25334              * Fires when the textarea is updated with content from the editor iframe.
25335              * @param {HtmlEditor} this
25336              * @param {String} html
25337              */
25338             sync: true,
25339              /**
25340              * @event push
25341              * Fires when the iframe editor is updated with content from the textarea.
25342              * @param {HtmlEditor} this
25343              * @param {String} html
25344              */
25345             push: true,
25346              /**
25347              * @event editmodechange
25348              * Fires when the editor switches edit modes
25349              * @param {HtmlEditor} this
25350              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25351              */
25352             editmodechange: true,
25353             /**
25354              * @event editorevent
25355              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25356              * @param {HtmlEditor} this
25357              */
25358             editorevent: true,
25359             /**
25360              * @event firstfocus
25361              * Fires when on first focus - needed by toolbars..
25362              * @param {HtmlEditor} this
25363              */
25364             firstfocus: true,
25365             /**
25366              * @event autosave
25367              * Auto save the htmlEditor value as a file into Events
25368              * @param {HtmlEditor} this
25369              */
25370             autosave: true,
25371             /**
25372              * @event savedpreview
25373              * preview the saved version of htmlEditor
25374              * @param {HtmlEditor} this
25375              */
25376             savedpreview: true
25377         });
25378 };
25379
25380
25381 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
25382     
25383     
25384       /**
25385      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25386      */
25387     toolbars : false,
25388     
25389      /**
25390     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25391     */
25392     btns : [],
25393    
25394      /**
25395      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25396      *                        Roo.resizable.
25397      */
25398     resizable : false,
25399      /**
25400      * @cfg {Number} height (in pixels)
25401      */   
25402     height: 300,
25403    /**
25404      * @cfg {Number} width (in pixels)
25405      */   
25406     width: false,
25407     
25408     /**
25409      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25410      * 
25411      */
25412     stylesheets: false,
25413     
25414     // id of frame..
25415     frameId: false,
25416     
25417     // private properties
25418     validationEvent : false,
25419     deferHeight: true,
25420     initialized : false,
25421     activated : false,
25422     
25423     onFocus : Roo.emptyFn,
25424     iframePad:3,
25425     hideMode:'offsets',
25426     
25427     tbContainer : false,
25428     
25429     bodyCls : '',
25430     
25431     toolbarContainer :function() {
25432         return this.wrap.select('.x-html-editor-tb',true).first();
25433     },
25434
25435     /**
25436      * Protected method that will not generally be called directly. It
25437      * is called when the editor creates its toolbar. Override this method if you need to
25438      * add custom toolbar buttons.
25439      * @param {HtmlEditor} editor
25440      */
25441     createToolbar : function(){
25442         Roo.log('renewing');
25443         Roo.log("create toolbars");
25444         
25445         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25446         this.toolbars[0].render(this.toolbarContainer());
25447         
25448         return;
25449         
25450 //        if (!editor.toolbars || !editor.toolbars.length) {
25451 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25452 //        }
25453 //        
25454 //        for (var i =0 ; i < editor.toolbars.length;i++) {
25455 //            editor.toolbars[i] = Roo.factory(
25456 //                    typeof(editor.toolbars[i]) == 'string' ?
25457 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
25458 //                Roo.bootstrap.HtmlEditor);
25459 //            editor.toolbars[i].init(editor);
25460 //        }
25461     },
25462
25463      
25464     // private
25465     onRender : function(ct, position)
25466     {
25467        // Roo.log("Call onRender: " + this.xtype);
25468         var _t = this;
25469         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25470       
25471         this.wrap = this.inputEl().wrap({
25472             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25473         });
25474         
25475         this.editorcore.onRender(ct, position);
25476          
25477         if (this.resizable) {
25478             this.resizeEl = new Roo.Resizable(this.wrap, {
25479                 pinned : true,
25480                 wrap: true,
25481                 dynamic : true,
25482                 minHeight : this.height,
25483                 height: this.height,
25484                 handles : this.resizable,
25485                 width: this.width,
25486                 listeners : {
25487                     resize : function(r, w, h) {
25488                         _t.onResize(w,h); // -something
25489                     }
25490                 }
25491             });
25492             
25493         }
25494         this.createToolbar(this);
25495        
25496         
25497         if(!this.width && this.resizable){
25498             this.setSize(this.wrap.getSize());
25499         }
25500         if (this.resizeEl) {
25501             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25502             // should trigger onReize..
25503         }
25504         
25505     },
25506
25507     // private
25508     onResize : function(w, h)
25509     {
25510         Roo.log('resize: ' +w + ',' + h );
25511         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
25512         var ew = false;
25513         var eh = false;
25514         
25515         if(this.inputEl() ){
25516             if(typeof w == 'number'){
25517                 var aw = w - this.wrap.getFrameWidth('lr');
25518                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
25519                 ew = aw;
25520             }
25521             if(typeof h == 'number'){
25522                  var tbh = -11;  // fixme it needs to tool bar size!
25523                 for (var i =0; i < this.toolbars.length;i++) {
25524                     // fixme - ask toolbars for heights?
25525                     tbh += this.toolbars[i].el.getHeight();
25526                     //if (this.toolbars[i].footer) {
25527                     //    tbh += this.toolbars[i].footer.el.getHeight();
25528                     //}
25529                 }
25530               
25531                 
25532                 
25533                 
25534                 
25535                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25536                 ah -= 5; // knock a few pixes off for look..
25537                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
25538                 var eh = ah;
25539             }
25540         }
25541         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
25542         this.editorcore.onResize(ew,eh);
25543         
25544     },
25545
25546     /**
25547      * Toggles the editor between standard and source edit mode.
25548      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25549      */
25550     toggleSourceEdit : function(sourceEditMode)
25551     {
25552         this.editorcore.toggleSourceEdit(sourceEditMode);
25553         
25554         if(this.editorcore.sourceEditMode){
25555             Roo.log('editor - showing textarea');
25556             
25557 //            Roo.log('in');
25558 //            Roo.log(this.syncValue());
25559             this.syncValue();
25560             this.inputEl().removeClass(['hide', 'x-hidden']);
25561             this.inputEl().dom.removeAttribute('tabIndex');
25562             this.inputEl().focus();
25563         }else{
25564             Roo.log('editor - hiding textarea');
25565 //            Roo.log('out')
25566 //            Roo.log(this.pushValue()); 
25567             this.pushValue();
25568             
25569             this.inputEl().addClass(['hide', 'x-hidden']);
25570             this.inputEl().dom.setAttribute('tabIndex', -1);
25571             //this.deferFocus();
25572         }
25573          
25574         if(this.resizable){
25575             this.setSize(this.wrap.getSize());
25576         }
25577         
25578         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
25579     },
25580  
25581     // private (for BoxComponent)
25582     adjustSize : Roo.BoxComponent.prototype.adjustSize,
25583
25584     // private (for BoxComponent)
25585     getResizeEl : function(){
25586         return this.wrap;
25587     },
25588
25589     // private (for BoxComponent)
25590     getPositionEl : function(){
25591         return this.wrap;
25592     },
25593
25594     // private
25595     initEvents : function(){
25596         this.originalValue = this.getValue();
25597     },
25598
25599 //    /**
25600 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25601 //     * @method
25602 //     */
25603 //    markInvalid : Roo.emptyFn,
25604 //    /**
25605 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25606 //     * @method
25607 //     */
25608 //    clearInvalid : Roo.emptyFn,
25609
25610     setValue : function(v){
25611         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
25612         this.editorcore.pushValue();
25613     },
25614
25615      
25616     // private
25617     deferFocus : function(){
25618         this.focus.defer(10, this);
25619     },
25620
25621     // doc'ed in Field
25622     focus : function(){
25623         this.editorcore.focus();
25624         
25625     },
25626       
25627
25628     // private
25629     onDestroy : function(){
25630         
25631         
25632         
25633         if(this.rendered){
25634             
25635             for (var i =0; i < this.toolbars.length;i++) {
25636                 // fixme - ask toolbars for heights?
25637                 this.toolbars[i].onDestroy();
25638             }
25639             
25640             this.wrap.dom.innerHTML = '';
25641             this.wrap.remove();
25642         }
25643     },
25644
25645     // private
25646     onFirstFocus : function(){
25647         //Roo.log("onFirstFocus");
25648         this.editorcore.onFirstFocus();
25649          for (var i =0; i < this.toolbars.length;i++) {
25650             this.toolbars[i].onFirstFocus();
25651         }
25652         
25653     },
25654     
25655     // private
25656     syncValue : function()
25657     {   
25658         this.editorcore.syncValue();
25659     },
25660     
25661     pushValue : function()
25662     {   
25663         this.editorcore.pushValue();
25664     }
25665      
25666     
25667     // hide stuff that is not compatible
25668     /**
25669      * @event blur
25670      * @hide
25671      */
25672     /**
25673      * @event change
25674      * @hide
25675      */
25676     /**
25677      * @event focus
25678      * @hide
25679      */
25680     /**
25681      * @event specialkey
25682      * @hide
25683      */
25684     /**
25685      * @cfg {String} fieldClass @hide
25686      */
25687     /**
25688      * @cfg {String} focusClass @hide
25689      */
25690     /**
25691      * @cfg {String} autoCreate @hide
25692      */
25693     /**
25694      * @cfg {String} inputType @hide
25695      */
25696      
25697     /**
25698      * @cfg {String} invalidText @hide
25699      */
25700     /**
25701      * @cfg {String} msgFx @hide
25702      */
25703     /**
25704      * @cfg {String} validateOnBlur @hide
25705      */
25706 });
25707  
25708     
25709    
25710    
25711    
25712       
25713 Roo.namespace('Roo.bootstrap.htmleditor');
25714 /**
25715  * @class Roo.bootstrap.HtmlEditorToolbar1
25716  * Basic Toolbar
25717  * 
25718  * @example
25719  * Usage:
25720  *
25721  new Roo.bootstrap.HtmlEditor({
25722     ....
25723     toolbars : [
25724         new Roo.bootstrap.HtmlEditorToolbar1({
25725             disable : { fonts: 1 , format: 1, ..., ... , ...],
25726             btns : [ .... ]
25727         })
25728     }
25729      
25730  * 
25731  * @cfg {Object} disable List of elements to disable..
25732  * @cfg {Array} btns List of additional buttons.
25733  * 
25734  * 
25735  * NEEDS Extra CSS? 
25736  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
25737  */
25738  
25739 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
25740 {
25741     
25742     Roo.apply(this, config);
25743     
25744     // default disabled, based on 'good practice'..
25745     this.disable = this.disable || {};
25746     Roo.applyIf(this.disable, {
25747         fontSize : true,
25748         colors : true,
25749         specialElements : true
25750     });
25751     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
25752     
25753     this.editor = config.editor;
25754     this.editorcore = config.editor.editorcore;
25755     
25756     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
25757     
25758     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25759     // dont call parent... till later.
25760 }
25761 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
25762      
25763     bar : true,
25764     
25765     editor : false,
25766     editorcore : false,
25767     
25768     
25769     formats : [
25770         "p" ,  
25771         "h1","h2","h3","h4","h5","h6", 
25772         "pre", "code", 
25773         "abbr", "acronym", "address", "cite", "samp", "var",
25774         'div','span'
25775     ],
25776     
25777     onRender : function(ct, position)
25778     {
25779        // Roo.log("Call onRender: " + this.xtype);
25780         
25781        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
25782        Roo.log(this.el);
25783        this.el.dom.style.marginBottom = '0';
25784        var _this = this;
25785        var editorcore = this.editorcore;
25786        var editor= this.editor;
25787        
25788        var children = [];
25789        var btn = function(id,cmd , toggle, handler, html){
25790        
25791             var  event = toggle ? 'toggle' : 'click';
25792        
25793             var a = {
25794                 size : 'sm',
25795                 xtype: 'Button',
25796                 xns: Roo.bootstrap,
25797                 //glyphicon : id,
25798                 fa: id,
25799                 cmd : id || cmd,
25800                 enableToggle:toggle !== false,
25801                 html : html || '',
25802                 pressed : toggle ? false : null,
25803                 listeners : {}
25804             };
25805             a.listeners[toggle ? 'toggle' : 'click'] = function() {
25806                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
25807             };
25808             children.push(a);
25809             return a;
25810        }
25811        
25812     //    var cb_box = function...
25813         
25814         var style = {
25815                 xtype: 'Button',
25816                 size : 'sm',
25817                 xns: Roo.bootstrap,
25818                 fa : 'font',
25819                 //html : 'submit'
25820                 menu : {
25821                     xtype: 'Menu',
25822                     xns: Roo.bootstrap,
25823                     items:  []
25824                 }
25825         };
25826         Roo.each(this.formats, function(f) {
25827             style.menu.items.push({
25828                 xtype :'MenuItem',
25829                 xns: Roo.bootstrap,
25830                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
25831                 tagname : f,
25832                 listeners : {
25833                     click : function()
25834                     {
25835                         editorcore.insertTag(this.tagname);
25836                         editor.focus();
25837                     }
25838                 }
25839                 
25840             });
25841         });
25842         children.push(style);   
25843         
25844         btn('bold',false,true);
25845         btn('italic',false,true);
25846         btn('align-left', 'justifyleft',true);
25847         btn('align-center', 'justifycenter',true);
25848         btn('align-right' , 'justifyright',true);
25849         btn('link', false, false, function(btn) {
25850             //Roo.log("create link?");
25851             var url = prompt(this.createLinkText, this.defaultLinkValue);
25852             if(url && url != 'http:/'+'/'){
25853                 this.editorcore.relayCmd('createlink', url);
25854             }
25855         }),
25856         btn('list','insertunorderedlist',true);
25857         btn('pencil', false,true, function(btn){
25858                 Roo.log(this);
25859                 this.toggleSourceEdit(btn.pressed);
25860         });
25861         
25862         if (this.editor.btns.length > 0) {
25863             for (var i = 0; i<this.editor.btns.length; i++) {
25864                 children.push(this.editor.btns[i]);
25865             }
25866         }
25867         
25868         /*
25869         var cog = {
25870                 xtype: 'Button',
25871                 size : 'sm',
25872                 xns: Roo.bootstrap,
25873                 glyphicon : 'cog',
25874                 //html : 'submit'
25875                 menu : {
25876                     xtype: 'Menu',
25877                     xns: Roo.bootstrap,
25878                     items:  []
25879                 }
25880         };
25881         
25882         cog.menu.items.push({
25883             xtype :'MenuItem',
25884             xns: Roo.bootstrap,
25885             html : Clean styles,
25886             tagname : f,
25887             listeners : {
25888                 click : function()
25889                 {
25890                     editorcore.insertTag(this.tagname);
25891                     editor.focus();
25892                 }
25893             }
25894             
25895         });
25896        */
25897         
25898          
25899        this.xtype = 'NavSimplebar';
25900         
25901         for(var i=0;i< children.length;i++) {
25902             
25903             this.buttons.add(this.addxtypeChild(children[i]));
25904             
25905         }
25906         
25907         editor.on('editorevent', this.updateToolbar, this);
25908     },
25909     onBtnClick : function(id)
25910     {
25911        this.editorcore.relayCmd(id);
25912        this.editorcore.focus();
25913     },
25914     
25915     /**
25916      * Protected method that will not generally be called directly. It triggers
25917      * a toolbar update by reading the markup state of the current selection in the editor.
25918      */
25919     updateToolbar: function(){
25920
25921         if(!this.editorcore.activated){
25922             this.editor.onFirstFocus(); // is this neeed?
25923             return;
25924         }
25925
25926         var btns = this.buttons; 
25927         var doc = this.editorcore.doc;
25928         btns.get('bold').setActive(doc.queryCommandState('bold'));
25929         btns.get('italic').setActive(doc.queryCommandState('italic'));
25930         //btns.get('underline').setActive(doc.queryCommandState('underline'));
25931         
25932         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
25933         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
25934         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
25935         
25936         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
25937         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
25938          /*
25939         
25940         var ans = this.editorcore.getAllAncestors();
25941         if (this.formatCombo) {
25942             
25943             
25944             var store = this.formatCombo.store;
25945             this.formatCombo.setValue("");
25946             for (var i =0; i < ans.length;i++) {
25947                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
25948                     // select it..
25949                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
25950                     break;
25951                 }
25952             }
25953         }
25954         
25955         
25956         
25957         // hides menus... - so this cant be on a menu...
25958         Roo.bootstrap.MenuMgr.hideAll();
25959         */
25960         Roo.bootstrap.MenuMgr.hideAll();
25961         //this.editorsyncValue();
25962     },
25963     onFirstFocus: function() {
25964         this.buttons.each(function(item){
25965            item.enable();
25966         });
25967     },
25968     toggleSourceEdit : function(sourceEditMode){
25969         
25970           
25971         if(sourceEditMode){
25972             Roo.log("disabling buttons");
25973            this.buttons.each( function(item){
25974                 if(item.cmd != 'pencil'){
25975                     item.disable();
25976                 }
25977             });
25978           
25979         }else{
25980             Roo.log("enabling buttons");
25981             if(this.editorcore.initialized){
25982                 this.buttons.each( function(item){
25983                     item.enable();
25984                 });
25985             }
25986             
25987         }
25988         Roo.log("calling toggole on editor");
25989         // tell the editor that it's been pressed..
25990         this.editor.toggleSourceEdit(sourceEditMode);
25991        
25992     }
25993 });
25994
25995
25996
25997
25998
25999 /**
26000  * @class Roo.bootstrap.Table.AbstractSelectionModel
26001  * @extends Roo.util.Observable
26002  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26003  * implemented by descendant classes.  This class should not be directly instantiated.
26004  * @constructor
26005  */
26006 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26007     this.locked = false;
26008     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26009 };
26010
26011
26012 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26013     /** @ignore Called by the grid automatically. Do not call directly. */
26014     init : function(grid){
26015         this.grid = grid;
26016         this.initEvents();
26017     },
26018
26019     /**
26020      * Locks the selections.
26021      */
26022     lock : function(){
26023         this.locked = true;
26024     },
26025
26026     /**
26027      * Unlocks the selections.
26028      */
26029     unlock : function(){
26030         this.locked = false;
26031     },
26032
26033     /**
26034      * Returns true if the selections are locked.
26035      * @return {Boolean}
26036      */
26037     isLocked : function(){
26038         return this.locked;
26039     },
26040     
26041     
26042     initEvents : function ()
26043     {
26044         
26045     }
26046 });
26047 /**
26048  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26049  * @class Roo.bootstrap.Table.RowSelectionModel
26050  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26051  * It supports multiple selections and keyboard selection/navigation. 
26052  * @constructor
26053  * @param {Object} config
26054  */
26055
26056 Roo.bootstrap.Table.RowSelectionModel = function(config){
26057     Roo.apply(this, config);
26058     this.selections = new Roo.util.MixedCollection(false, function(o){
26059         return o.id;
26060     });
26061
26062     this.last = false;
26063     this.lastActive = false;
26064
26065     this.addEvents({
26066         /**
26067              * @event selectionchange
26068              * Fires when the selection changes
26069              * @param {SelectionModel} this
26070              */
26071             "selectionchange" : true,
26072         /**
26073              * @event afterselectionchange
26074              * Fires after the selection changes (eg. by key press or clicking)
26075              * @param {SelectionModel} this
26076              */
26077             "afterselectionchange" : true,
26078         /**
26079              * @event beforerowselect
26080              * Fires when a row is selected being selected, return false to cancel.
26081              * @param {SelectionModel} this
26082              * @param {Number} rowIndex The selected index
26083              * @param {Boolean} keepExisting False if other selections will be cleared
26084              */
26085             "beforerowselect" : true,
26086         /**
26087              * @event rowselect
26088              * Fires when a row is selected.
26089              * @param {SelectionModel} this
26090              * @param {Number} rowIndex The selected index
26091              * @param {Roo.data.Record} r The record
26092              */
26093             "rowselect" : true,
26094         /**
26095              * @event rowdeselect
26096              * Fires when a row is deselected.
26097              * @param {SelectionModel} this
26098              * @param {Number} rowIndex The selected index
26099              */
26100         "rowdeselect" : true
26101     });
26102     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26103     this.locked = false;
26104  };
26105
26106 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
26107     /**
26108      * @cfg {Boolean} singleSelect
26109      * True to allow selection of only one row at a time (defaults to false)
26110      */
26111     singleSelect : false,
26112
26113     // private
26114     initEvents : function()
26115     {
26116
26117         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26118         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
26119         //}else{ // allow click to work like normal
26120          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
26121         //}
26122         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26123         this.grid.on("rowclick", this.handleMouseDown, this);
26124         
26125         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26126             "up" : function(e){
26127                 if(!e.shiftKey){
26128                     this.selectPrevious(e.shiftKey);
26129                 }else if(this.last !== false && this.lastActive !== false){
26130                     var last = this.last;
26131                     this.selectRange(this.last,  this.lastActive-1);
26132                     this.grid.getView().focusRow(this.lastActive);
26133                     if(last !== false){
26134                         this.last = last;
26135                     }
26136                 }else{
26137                     this.selectFirstRow();
26138                 }
26139                 this.fireEvent("afterselectionchange", this);
26140             },
26141             "down" : function(e){
26142                 if(!e.shiftKey){
26143                     this.selectNext(e.shiftKey);
26144                 }else if(this.last !== false && this.lastActive !== false){
26145                     var last = this.last;
26146                     this.selectRange(this.last,  this.lastActive+1);
26147                     this.grid.getView().focusRow(this.lastActive);
26148                     if(last !== false){
26149                         this.last = last;
26150                     }
26151                 }else{
26152                     this.selectFirstRow();
26153                 }
26154                 this.fireEvent("afterselectionchange", this);
26155             },
26156             scope: this
26157         });
26158         this.grid.store.on('load', function(){
26159             this.selections.clear();
26160         },this);
26161         /*
26162         var view = this.grid.view;
26163         view.on("refresh", this.onRefresh, this);
26164         view.on("rowupdated", this.onRowUpdated, this);
26165         view.on("rowremoved", this.onRemove, this);
26166         */
26167     },
26168
26169     // private
26170     onRefresh : function()
26171     {
26172         var ds = this.grid.store, i, v = this.grid.view;
26173         var s = this.selections;
26174         s.each(function(r){
26175             if((i = ds.indexOfId(r.id)) != -1){
26176                 v.onRowSelect(i);
26177             }else{
26178                 s.remove(r);
26179             }
26180         });
26181     },
26182
26183     // private
26184     onRemove : function(v, index, r){
26185         this.selections.remove(r);
26186     },
26187
26188     // private
26189     onRowUpdated : function(v, index, r){
26190         if(this.isSelected(r)){
26191             v.onRowSelect(index);
26192         }
26193     },
26194
26195     /**
26196      * Select records.
26197      * @param {Array} records The records to select
26198      * @param {Boolean} keepExisting (optional) True to keep existing selections
26199      */
26200     selectRecords : function(records, keepExisting)
26201     {
26202         if(!keepExisting){
26203             this.clearSelections();
26204         }
26205             var ds = this.grid.store;
26206         for(var i = 0, len = records.length; i < len; i++){
26207             this.selectRow(ds.indexOf(records[i]), true);
26208         }
26209     },
26210
26211     /**
26212      * Gets the number of selected rows.
26213      * @return {Number}
26214      */
26215     getCount : function(){
26216         return this.selections.length;
26217     },
26218
26219     /**
26220      * Selects the first row in the grid.
26221      */
26222     selectFirstRow : function(){
26223         this.selectRow(0);
26224     },
26225
26226     /**
26227      * Select the last row.
26228      * @param {Boolean} keepExisting (optional) True to keep existing selections
26229      */
26230     selectLastRow : function(keepExisting){
26231         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26232         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26233     },
26234
26235     /**
26236      * Selects the row immediately following the last selected row.
26237      * @param {Boolean} keepExisting (optional) True to keep existing selections
26238      */
26239     selectNext : function(keepExisting)
26240     {
26241             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26242             this.selectRow(this.last+1, keepExisting);
26243             this.grid.getView().focusRow(this.last);
26244         }
26245     },
26246
26247     /**
26248      * Selects the row that precedes the last selected row.
26249      * @param {Boolean} keepExisting (optional) True to keep existing selections
26250      */
26251     selectPrevious : function(keepExisting){
26252         if(this.last){
26253             this.selectRow(this.last-1, keepExisting);
26254             this.grid.getView().focusRow(this.last);
26255         }
26256     },
26257
26258     /**
26259      * Returns the selected records
26260      * @return {Array} Array of selected records
26261      */
26262     getSelections : function(){
26263         return [].concat(this.selections.items);
26264     },
26265
26266     /**
26267      * Returns the first selected record.
26268      * @return {Record}
26269      */
26270     getSelected : function(){
26271         return this.selections.itemAt(0);
26272     },
26273
26274
26275     /**
26276      * Clears all selections.
26277      */
26278     clearSelections : function(fast)
26279     {
26280         if(this.locked) {
26281             return;
26282         }
26283         if(fast !== true){
26284                 var ds = this.grid.store;
26285             var s = this.selections;
26286             s.each(function(r){
26287                 this.deselectRow(ds.indexOfId(r.id));
26288             }, this);
26289             s.clear();
26290         }else{
26291             this.selections.clear();
26292         }
26293         this.last = false;
26294     },
26295
26296
26297     /**
26298      * Selects all rows.
26299      */
26300     selectAll : function(){
26301         if(this.locked) {
26302             return;
26303         }
26304         this.selections.clear();
26305         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26306             this.selectRow(i, true);
26307         }
26308     },
26309
26310     /**
26311      * Returns True if there is a selection.
26312      * @return {Boolean}
26313      */
26314     hasSelection : function(){
26315         return this.selections.length > 0;
26316     },
26317
26318     /**
26319      * Returns True if the specified row is selected.
26320      * @param {Number/Record} record The record or index of the record to check
26321      * @return {Boolean}
26322      */
26323     isSelected : function(index){
26324             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26325         return (r && this.selections.key(r.id) ? true : false);
26326     },
26327
26328     /**
26329      * Returns True if the specified record id is selected.
26330      * @param {String} id The id of record to check
26331      * @return {Boolean}
26332      */
26333     isIdSelected : function(id){
26334         return (this.selections.key(id) ? true : false);
26335     },
26336
26337
26338     // private
26339     handleMouseDBClick : function(e, t){
26340         
26341     },
26342     // private
26343     handleMouseDown : function(e, t)
26344     {
26345             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26346         if(this.isLocked() || rowIndex < 0 ){
26347             return;
26348         };
26349         if(e.shiftKey && this.last !== false){
26350             var last = this.last;
26351             this.selectRange(last, rowIndex, e.ctrlKey);
26352             this.last = last; // reset the last
26353             t.focus();
26354     
26355         }else{
26356             var isSelected = this.isSelected(rowIndex);
26357             //Roo.log("select row:" + rowIndex);
26358             if(isSelected){
26359                 this.deselectRow(rowIndex);
26360             } else {
26361                         this.selectRow(rowIndex, true);
26362             }
26363     
26364             /*
26365                 if(e.button !== 0 && isSelected){
26366                 alert('rowIndex 2: ' + rowIndex);
26367                     view.focusRow(rowIndex);
26368                 }else if(e.ctrlKey && isSelected){
26369                     this.deselectRow(rowIndex);
26370                 }else if(!isSelected){
26371                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26372                     view.focusRow(rowIndex);
26373                 }
26374             */
26375         }
26376         this.fireEvent("afterselectionchange", this);
26377     },
26378     // private
26379     handleDragableRowClick :  function(grid, rowIndex, e) 
26380     {
26381         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26382             this.selectRow(rowIndex, false);
26383             grid.view.focusRow(rowIndex);
26384              this.fireEvent("afterselectionchange", this);
26385         }
26386     },
26387     
26388     /**
26389      * Selects multiple rows.
26390      * @param {Array} rows Array of the indexes of the row to select
26391      * @param {Boolean} keepExisting (optional) True to keep existing selections
26392      */
26393     selectRows : function(rows, keepExisting){
26394         if(!keepExisting){
26395             this.clearSelections();
26396         }
26397         for(var i = 0, len = rows.length; i < len; i++){
26398             this.selectRow(rows[i], true);
26399         }
26400     },
26401
26402     /**
26403      * Selects a range of rows. All rows in between startRow and endRow are also selected.
26404      * @param {Number} startRow The index of the first row in the range
26405      * @param {Number} endRow The index of the last row in the range
26406      * @param {Boolean} keepExisting (optional) True to retain existing selections
26407      */
26408     selectRange : function(startRow, endRow, keepExisting){
26409         if(this.locked) {
26410             return;
26411         }
26412         if(!keepExisting){
26413             this.clearSelections();
26414         }
26415         if(startRow <= endRow){
26416             for(var i = startRow; i <= endRow; i++){
26417                 this.selectRow(i, true);
26418             }
26419         }else{
26420             for(var i = startRow; i >= endRow; i--){
26421                 this.selectRow(i, true);
26422             }
26423         }
26424     },
26425
26426     /**
26427      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
26428      * @param {Number} startRow The index of the first row in the range
26429      * @param {Number} endRow The index of the last row in the range
26430      */
26431     deselectRange : function(startRow, endRow, preventViewNotify){
26432         if(this.locked) {
26433             return;
26434         }
26435         for(var i = startRow; i <= endRow; i++){
26436             this.deselectRow(i, preventViewNotify);
26437         }
26438     },
26439
26440     /**
26441      * Selects a row.
26442      * @param {Number} row The index of the row to select
26443      * @param {Boolean} keepExisting (optional) True to keep existing selections
26444      */
26445     selectRow : function(index, keepExisting, preventViewNotify)
26446     {
26447             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
26448             return;
26449         }
26450         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
26451             if(!keepExisting || this.singleSelect){
26452                 this.clearSelections();
26453             }
26454             
26455             var r = this.grid.store.getAt(index);
26456             //console.log('selectRow - record id :' + r.id);
26457             
26458             this.selections.add(r);
26459             this.last = this.lastActive = index;
26460             if(!preventViewNotify){
26461                 var proxy = new Roo.Element(
26462                                 this.grid.getRowDom(index)
26463                 );
26464                 proxy.addClass('bg-info info');
26465             }
26466             this.fireEvent("rowselect", this, index, r);
26467             this.fireEvent("selectionchange", this);
26468         }
26469     },
26470
26471     /**
26472      * Deselects a row.
26473      * @param {Number} row The index of the row to deselect
26474      */
26475     deselectRow : function(index, preventViewNotify)
26476     {
26477         if(this.locked) {
26478             return;
26479         }
26480         if(this.last == index){
26481             this.last = false;
26482         }
26483         if(this.lastActive == index){
26484             this.lastActive = false;
26485         }
26486         
26487         var r = this.grid.store.getAt(index);
26488         if (!r) {
26489             return;
26490         }
26491         
26492         this.selections.remove(r);
26493         //.console.log('deselectRow - record id :' + r.id);
26494         if(!preventViewNotify){
26495         
26496             var proxy = new Roo.Element(
26497                 this.grid.getRowDom(index)
26498             );
26499             proxy.removeClass('bg-info info');
26500         }
26501         this.fireEvent("rowdeselect", this, index);
26502         this.fireEvent("selectionchange", this);
26503     },
26504
26505     // private
26506     restoreLast : function(){
26507         if(this._last){
26508             this.last = this._last;
26509         }
26510     },
26511
26512     // private
26513     acceptsNav : function(row, col, cm){
26514         return !cm.isHidden(col) && cm.isCellEditable(col, row);
26515     },
26516
26517     // private
26518     onEditorKey : function(field, e){
26519         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
26520         if(k == e.TAB){
26521             e.stopEvent();
26522             ed.completeEdit();
26523             if(e.shiftKey){
26524                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
26525             }else{
26526                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
26527             }
26528         }else if(k == e.ENTER && !e.ctrlKey){
26529             e.stopEvent();
26530             ed.completeEdit();
26531             if(e.shiftKey){
26532                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
26533             }else{
26534                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
26535             }
26536         }else if(k == e.ESC){
26537             ed.cancelEdit();
26538         }
26539         if(newCell){
26540             g.startEditing(newCell[0], newCell[1]);
26541         }
26542     }
26543 });
26544 /*
26545  * Based on:
26546  * Ext JS Library 1.1.1
26547  * Copyright(c) 2006-2007, Ext JS, LLC.
26548  *
26549  * Originally Released Under LGPL - original licence link has changed is not relivant.
26550  *
26551  * Fork - LGPL
26552  * <script type="text/javascript">
26553  */
26554  
26555 /**
26556  * @class Roo.bootstrap.PagingToolbar
26557  * @extends Roo.bootstrap.NavSimplebar
26558  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
26559  * @constructor
26560  * Create a new PagingToolbar
26561  * @param {Object} config The config object
26562  * @param {Roo.data.Store} store
26563  */
26564 Roo.bootstrap.PagingToolbar = function(config)
26565 {
26566     // old args format still supported... - xtype is prefered..
26567         // created from xtype...
26568     
26569     this.ds = config.dataSource;
26570     
26571     if (config.store && !this.ds) {
26572         this.store= Roo.factory(config.store, Roo.data);
26573         this.ds = this.store;
26574         this.ds.xmodule = this.xmodule || false;
26575     }
26576     
26577     this.toolbarItems = [];
26578     if (config.items) {
26579         this.toolbarItems = config.items;
26580     }
26581     
26582     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
26583     
26584     this.cursor = 0;
26585     
26586     if (this.ds) { 
26587         this.bind(this.ds);
26588     }
26589     
26590     if (Roo.bootstrap.version == 4) {
26591         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
26592     } else {
26593         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
26594     }
26595     
26596 };
26597
26598 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
26599     /**
26600      * @cfg {Roo.data.Store} dataSource
26601      * The underlying data store providing the paged data
26602      */
26603     /**
26604      * @cfg {String/HTMLElement/Element} container
26605      * container The id or element that will contain the toolbar
26606      */
26607     /**
26608      * @cfg {Boolean} displayInfo
26609      * True to display the displayMsg (defaults to false)
26610      */
26611     /**
26612      * @cfg {Number} pageSize
26613      * The number of records to display per page (defaults to 20)
26614      */
26615     pageSize: 20,
26616     /**
26617      * @cfg {String} displayMsg
26618      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
26619      */
26620     displayMsg : 'Displaying {0} - {1} of {2}',
26621     /**
26622      * @cfg {String} emptyMsg
26623      * The message to display when no records are found (defaults to "No data to display")
26624      */
26625     emptyMsg : 'No data to display',
26626     /**
26627      * Customizable piece of the default paging text (defaults to "Page")
26628      * @type String
26629      */
26630     beforePageText : "Page",
26631     /**
26632      * Customizable piece of the default paging text (defaults to "of %0")
26633      * @type String
26634      */
26635     afterPageText : "of {0}",
26636     /**
26637      * Customizable piece of the default paging text (defaults to "First Page")
26638      * @type String
26639      */
26640     firstText : "First Page",
26641     /**
26642      * Customizable piece of the default paging text (defaults to "Previous Page")
26643      * @type String
26644      */
26645     prevText : "Previous Page",
26646     /**
26647      * Customizable piece of the default paging text (defaults to "Next Page")
26648      * @type String
26649      */
26650     nextText : "Next Page",
26651     /**
26652      * Customizable piece of the default paging text (defaults to "Last Page")
26653      * @type String
26654      */
26655     lastText : "Last Page",
26656     /**
26657      * Customizable piece of the default paging text (defaults to "Refresh")
26658      * @type String
26659      */
26660     refreshText : "Refresh",
26661
26662     buttons : false,
26663     // private
26664     onRender : function(ct, position) 
26665     {
26666         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
26667         this.navgroup.parentId = this.id;
26668         this.navgroup.onRender(this.el, null);
26669         // add the buttons to the navgroup
26670         
26671         if(this.displayInfo){
26672             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
26673             this.displayEl = this.el.select('.x-paging-info', true).first();
26674 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
26675 //            this.displayEl = navel.el.select('span',true).first();
26676         }
26677         
26678         var _this = this;
26679         
26680         if(this.buttons){
26681             Roo.each(_this.buttons, function(e){ // this might need to use render????
26682                Roo.factory(e).render(_this.el);
26683             });
26684         }
26685             
26686         Roo.each(_this.toolbarItems, function(e) {
26687             _this.navgroup.addItem(e);
26688         });
26689         
26690         
26691         this.first = this.navgroup.addItem({
26692             tooltip: this.firstText,
26693             cls: "prev btn-outline-secondary",
26694             html : ' <i class="fa fa-step-backward"></i>',
26695             disabled: true,
26696             preventDefault: true,
26697             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
26698         });
26699         
26700         this.prev =  this.navgroup.addItem({
26701             tooltip: this.prevText,
26702             cls: "prev btn-outline-secondary",
26703             html : ' <i class="fa fa-backward"></i>',
26704             disabled: true,
26705             preventDefault: true,
26706             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
26707         });
26708     //this.addSeparator();
26709         
26710         
26711         var field = this.navgroup.addItem( {
26712             tagtype : 'span',
26713             cls : 'x-paging-position  btn-outline-secondary',
26714              disabled: true,
26715             html : this.beforePageText  +
26716                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
26717                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
26718          } ); //?? escaped?
26719         
26720         this.field = field.el.select('input', true).first();
26721         this.field.on("keydown", this.onPagingKeydown, this);
26722         this.field.on("focus", function(){this.dom.select();});
26723     
26724     
26725         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
26726         //this.field.setHeight(18);
26727         //this.addSeparator();
26728         this.next = this.navgroup.addItem({
26729             tooltip: this.nextText,
26730             cls: "next btn-outline-secondary",
26731             html : ' <i class="fa fa-forward"></i>',
26732             disabled: true,
26733             preventDefault: true,
26734             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
26735         });
26736         this.last = this.navgroup.addItem({
26737             tooltip: this.lastText,
26738             html : ' <i class="fa fa-step-forward"></i>',
26739             cls: "next btn-outline-secondary",
26740             disabled: true,
26741             preventDefault: true,
26742             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
26743         });
26744     //this.addSeparator();
26745         this.loading = this.navgroup.addItem({
26746             tooltip: this.refreshText,
26747             cls: "btn-outline-secondary",
26748             html : ' <i class="fa fa-refresh"></i>',
26749             preventDefault: true,
26750             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
26751         });
26752         
26753     },
26754
26755     // private
26756     updateInfo : function(){
26757         if(this.displayEl){
26758             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
26759             var msg = count == 0 ?
26760                 this.emptyMsg :
26761                 String.format(
26762                     this.displayMsg,
26763                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
26764                 );
26765             this.displayEl.update(msg);
26766         }
26767     },
26768
26769     // private
26770     onLoad : function(ds, r, o)
26771     {
26772         this.cursor = o.params.start ? o.params.start : 0;
26773         
26774         var d = this.getPageData(),
26775             ap = d.activePage,
26776             ps = d.pages;
26777         
26778         
26779         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
26780         this.field.dom.value = ap;
26781         this.first.setDisabled(ap == 1);
26782         this.prev.setDisabled(ap == 1);
26783         this.next.setDisabled(ap == ps);
26784         this.last.setDisabled(ap == ps);
26785         this.loading.enable();
26786         this.updateInfo();
26787     },
26788
26789     // private
26790     getPageData : function(){
26791         var total = this.ds.getTotalCount();
26792         return {
26793             total : total,
26794             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
26795             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
26796         };
26797     },
26798
26799     // private
26800     onLoadError : function(){
26801         this.loading.enable();
26802     },
26803
26804     // private
26805     onPagingKeydown : function(e){
26806         var k = e.getKey();
26807         var d = this.getPageData();
26808         if(k == e.RETURN){
26809             var v = this.field.dom.value, pageNum;
26810             if(!v || isNaN(pageNum = parseInt(v, 10))){
26811                 this.field.dom.value = d.activePage;
26812                 return;
26813             }
26814             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
26815             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
26816             e.stopEvent();
26817         }
26818         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))
26819         {
26820           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
26821           this.field.dom.value = pageNum;
26822           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
26823           e.stopEvent();
26824         }
26825         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
26826         {
26827           var v = this.field.dom.value, pageNum; 
26828           var increment = (e.shiftKey) ? 10 : 1;
26829           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
26830                 increment *= -1;
26831           }
26832           if(!v || isNaN(pageNum = parseInt(v, 10))) {
26833             this.field.dom.value = d.activePage;
26834             return;
26835           }
26836           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
26837           {
26838             this.field.dom.value = parseInt(v, 10) + increment;
26839             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
26840             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
26841           }
26842           e.stopEvent();
26843         }
26844     },
26845
26846     // private
26847     beforeLoad : function(){
26848         if(this.loading){
26849             this.loading.disable();
26850         }
26851     },
26852
26853     // private
26854     onClick : function(which){
26855         
26856         var ds = this.ds;
26857         if (!ds) {
26858             return;
26859         }
26860         
26861         switch(which){
26862             case "first":
26863                 ds.load({params:{start: 0, limit: this.pageSize}});
26864             break;
26865             case "prev":
26866                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
26867             break;
26868             case "next":
26869                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
26870             break;
26871             case "last":
26872                 var total = ds.getTotalCount();
26873                 var extra = total % this.pageSize;
26874                 var lastStart = extra ? (total - extra) : total-this.pageSize;
26875                 ds.load({params:{start: lastStart, limit: this.pageSize}});
26876             break;
26877             case "refresh":
26878                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
26879             break;
26880         }
26881     },
26882
26883     /**
26884      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
26885      * @param {Roo.data.Store} store The data store to unbind
26886      */
26887     unbind : function(ds){
26888         ds.un("beforeload", this.beforeLoad, this);
26889         ds.un("load", this.onLoad, this);
26890         ds.un("loadexception", this.onLoadError, this);
26891         ds.un("remove", this.updateInfo, this);
26892         ds.un("add", this.updateInfo, this);
26893         this.ds = undefined;
26894     },
26895
26896     /**
26897      * Binds the paging toolbar to the specified {@link Roo.data.Store}
26898      * @param {Roo.data.Store} store The data store to bind
26899      */
26900     bind : function(ds){
26901         ds.on("beforeload", this.beforeLoad, this);
26902         ds.on("load", this.onLoad, this);
26903         ds.on("loadexception", this.onLoadError, this);
26904         ds.on("remove", this.updateInfo, this);
26905         ds.on("add", this.updateInfo, this);
26906         this.ds = ds;
26907     }
26908 });/*
26909  * - LGPL
26910  *
26911  * element
26912  * 
26913  */
26914
26915 /**
26916  * @class Roo.bootstrap.MessageBar
26917  * @extends Roo.bootstrap.Component
26918  * Bootstrap MessageBar class
26919  * @cfg {String} html contents of the MessageBar
26920  * @cfg {String} weight (info | success | warning | danger) default info
26921  * @cfg {String} beforeClass insert the bar before the given class
26922  * @cfg {Boolean} closable (true | false) default false
26923  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
26924  * 
26925  * @constructor
26926  * Create a new Element
26927  * @param {Object} config The config object
26928  */
26929
26930 Roo.bootstrap.MessageBar = function(config){
26931     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
26932 };
26933
26934 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
26935     
26936     html: '',
26937     weight: 'info',
26938     closable: false,
26939     fixed: false,
26940     beforeClass: 'bootstrap-sticky-wrap',
26941     
26942     getAutoCreate : function(){
26943         
26944         var cfg = {
26945             tag: 'div',
26946             cls: 'alert alert-dismissable alert-' + this.weight,
26947             cn: [
26948                 {
26949                     tag: 'span',
26950                     cls: 'message',
26951                     html: this.html || ''
26952                 }
26953             ]
26954         };
26955         
26956         if(this.fixed){
26957             cfg.cls += ' alert-messages-fixed';
26958         }
26959         
26960         if(this.closable){
26961             cfg.cn.push({
26962                 tag: 'button',
26963                 cls: 'close',
26964                 html: 'x'
26965             });
26966         }
26967         
26968         return cfg;
26969     },
26970     
26971     onRender : function(ct, position)
26972     {
26973         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
26974         
26975         if(!this.el){
26976             var cfg = Roo.apply({},  this.getAutoCreate());
26977             cfg.id = Roo.id();
26978             
26979             if (this.cls) {
26980                 cfg.cls += ' ' + this.cls;
26981             }
26982             if (this.style) {
26983                 cfg.style = this.style;
26984             }
26985             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
26986             
26987             this.el.setVisibilityMode(Roo.Element.DISPLAY);
26988         }
26989         
26990         this.el.select('>button.close').on('click', this.hide, this);
26991         
26992     },
26993     
26994     show : function()
26995     {
26996         if (!this.rendered) {
26997             this.render();
26998         }
26999         
27000         this.el.show();
27001         
27002         this.fireEvent('show', this);
27003         
27004     },
27005     
27006     hide : function()
27007     {
27008         if (!this.rendered) {
27009             this.render();
27010         }
27011         
27012         this.el.hide();
27013         
27014         this.fireEvent('hide', this);
27015     },
27016     
27017     update : function()
27018     {
27019 //        var e = this.el.dom.firstChild;
27020 //        
27021 //        if(this.closable){
27022 //            e = e.nextSibling;
27023 //        }
27024 //        
27025 //        e.data = this.html || '';
27026
27027         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27028     }
27029    
27030 });
27031
27032  
27033
27034      /*
27035  * - LGPL
27036  *
27037  * Graph
27038  * 
27039  */
27040
27041
27042 /**
27043  * @class Roo.bootstrap.Graph
27044  * @extends Roo.bootstrap.Component
27045  * Bootstrap Graph class
27046 > Prameters
27047  -sm {number} sm 4
27048  -md {number} md 5
27049  @cfg {String} graphtype  bar | vbar | pie
27050  @cfg {number} g_x coodinator | centre x (pie)
27051  @cfg {number} g_y coodinator | centre y (pie)
27052  @cfg {number} g_r radius (pie)
27053  @cfg {number} g_height height of the chart (respected by all elements in the set)
27054  @cfg {number} g_width width of the chart (respected by all elements in the set)
27055  @cfg {Object} title The title of the chart
27056     
27057  -{Array}  values
27058  -opts (object) options for the chart 
27059      o {
27060      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27061      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27062      o vgutter (number)
27063      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.
27064      o stacked (boolean) whether or not to tread values as in a stacked bar chart
27065      o to
27066      o stretch (boolean)
27067      o }
27068  -opts (object) options for the pie
27069      o{
27070      o cut
27071      o startAngle (number)
27072      o endAngle (number)
27073      } 
27074  *
27075  * @constructor
27076  * Create a new Input
27077  * @param {Object} config The config object
27078  */
27079
27080 Roo.bootstrap.Graph = function(config){
27081     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27082     
27083     this.addEvents({
27084         // img events
27085         /**
27086          * @event click
27087          * The img click event for the img.
27088          * @param {Roo.EventObject} e
27089          */
27090         "click" : true
27091     });
27092 };
27093
27094 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
27095     
27096     sm: 4,
27097     md: 5,
27098     graphtype: 'bar',
27099     g_height: 250,
27100     g_width: 400,
27101     g_x: 50,
27102     g_y: 50,
27103     g_r: 30,
27104     opts:{
27105         //g_colors: this.colors,
27106         g_type: 'soft',
27107         g_gutter: '20%'
27108
27109     },
27110     title : false,
27111
27112     getAutoCreate : function(){
27113         
27114         var cfg = {
27115             tag: 'div',
27116             html : null
27117         };
27118         
27119         
27120         return  cfg;
27121     },
27122
27123     onRender : function(ct,position){
27124         
27125         
27126         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27127         
27128         if (typeof(Raphael) == 'undefined') {
27129             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27130             return;
27131         }
27132         
27133         this.raphael = Raphael(this.el.dom);
27134         
27135                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27136                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27137                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27138                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27139                 /*
27140                 r.text(160, 10, "Single Series Chart").attr(txtattr);
27141                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27142                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27143                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27144                 
27145                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27146                 r.barchart(330, 10, 300, 220, data1);
27147                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27148                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27149                 */
27150                 
27151                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27152                 // r.barchart(30, 30, 560, 250,  xdata, {
27153                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27154                 //     axis : "0 0 1 1",
27155                 //     axisxlabels :  xdata
27156                 //     //yvalues : cols,
27157                    
27158                 // });
27159 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27160 //        
27161 //        this.load(null,xdata,{
27162 //                axis : "0 0 1 1",
27163 //                axisxlabels :  xdata
27164 //                });
27165
27166     },
27167
27168     load : function(graphtype,xdata,opts)
27169     {
27170         this.raphael.clear();
27171         if(!graphtype) {
27172             graphtype = this.graphtype;
27173         }
27174         if(!opts){
27175             opts = this.opts;
27176         }
27177         var r = this.raphael,
27178             fin = function () {
27179                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27180             },
27181             fout = function () {
27182                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27183             },
27184             pfin = function() {
27185                 this.sector.stop();
27186                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27187
27188                 if (this.label) {
27189                     this.label[0].stop();
27190                     this.label[0].attr({ r: 7.5 });
27191                     this.label[1].attr({ "font-weight": 800 });
27192                 }
27193             },
27194             pfout = function() {
27195                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27196
27197                 if (this.label) {
27198                     this.label[0].animate({ r: 5 }, 500, "bounce");
27199                     this.label[1].attr({ "font-weight": 400 });
27200                 }
27201             };
27202
27203         switch(graphtype){
27204             case 'bar':
27205                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27206                 break;
27207             case 'hbar':
27208                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27209                 break;
27210             case 'pie':
27211 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
27212 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27213 //            
27214                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27215                 
27216                 break;
27217
27218         }
27219         
27220         if(this.title){
27221             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27222         }
27223         
27224     },
27225     
27226     setTitle: function(o)
27227     {
27228         this.title = o;
27229     },
27230     
27231     initEvents: function() {
27232         
27233         if(!this.href){
27234             this.el.on('click', this.onClick, this);
27235         }
27236     },
27237     
27238     onClick : function(e)
27239     {
27240         Roo.log('img onclick');
27241         this.fireEvent('click', this, e);
27242     }
27243    
27244 });
27245
27246  
27247 /*
27248  * - LGPL
27249  *
27250  * numberBox
27251  * 
27252  */
27253 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27254
27255 /**
27256  * @class Roo.bootstrap.dash.NumberBox
27257  * @extends Roo.bootstrap.Component
27258  * Bootstrap NumberBox class
27259  * @cfg {String} headline Box headline
27260  * @cfg {String} content Box content
27261  * @cfg {String} icon Box icon
27262  * @cfg {String} footer Footer text
27263  * @cfg {String} fhref Footer href
27264  * 
27265  * @constructor
27266  * Create a new NumberBox
27267  * @param {Object} config The config object
27268  */
27269
27270
27271 Roo.bootstrap.dash.NumberBox = function(config){
27272     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27273     
27274 };
27275
27276 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
27277     
27278     headline : '',
27279     content : '',
27280     icon : '',
27281     footer : '',
27282     fhref : '',
27283     ficon : '',
27284     
27285     getAutoCreate : function(){
27286         
27287         var cfg = {
27288             tag : 'div',
27289             cls : 'small-box ',
27290             cn : [
27291                 {
27292                     tag : 'div',
27293                     cls : 'inner',
27294                     cn :[
27295                         {
27296                             tag : 'h3',
27297                             cls : 'roo-headline',
27298                             html : this.headline
27299                         },
27300                         {
27301                             tag : 'p',
27302                             cls : 'roo-content',
27303                             html : this.content
27304                         }
27305                     ]
27306                 }
27307             ]
27308         };
27309         
27310         if(this.icon){
27311             cfg.cn.push({
27312                 tag : 'div',
27313                 cls : 'icon',
27314                 cn :[
27315                     {
27316                         tag : 'i',
27317                         cls : 'ion ' + this.icon
27318                     }
27319                 ]
27320             });
27321         }
27322         
27323         if(this.footer){
27324             var footer = {
27325                 tag : 'a',
27326                 cls : 'small-box-footer',
27327                 href : this.fhref || '#',
27328                 html : this.footer
27329             };
27330             
27331             cfg.cn.push(footer);
27332             
27333         }
27334         
27335         return  cfg;
27336     },
27337
27338     onRender : function(ct,position){
27339         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27340
27341
27342        
27343                 
27344     },
27345
27346     setHeadline: function (value)
27347     {
27348         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27349     },
27350     
27351     setFooter: function (value, href)
27352     {
27353         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27354         
27355         if(href){
27356             this.el.select('a.small-box-footer',true).first().attr('href', href);
27357         }
27358         
27359     },
27360
27361     setContent: function (value)
27362     {
27363         this.el.select('.roo-content',true).first().dom.innerHTML = value;
27364     },
27365
27366     initEvents: function() 
27367     {   
27368         
27369     }
27370     
27371 });
27372
27373  
27374 /*
27375  * - LGPL
27376  *
27377  * TabBox
27378  * 
27379  */
27380 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27381
27382 /**
27383  * @class Roo.bootstrap.dash.TabBox
27384  * @extends Roo.bootstrap.Component
27385  * Bootstrap TabBox class
27386  * @cfg {String} title Title of the TabBox
27387  * @cfg {String} icon Icon of the TabBox
27388  * @cfg {Boolean} showtabs (true|false) show the tabs default true
27389  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
27390  * 
27391  * @constructor
27392  * Create a new TabBox
27393  * @param {Object} config The config object
27394  */
27395
27396
27397 Roo.bootstrap.dash.TabBox = function(config){
27398     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
27399     this.addEvents({
27400         // raw events
27401         /**
27402          * @event addpane
27403          * When a pane is added
27404          * @param {Roo.bootstrap.dash.TabPane} pane
27405          */
27406         "addpane" : true,
27407         /**
27408          * @event activatepane
27409          * When a pane is activated
27410          * @param {Roo.bootstrap.dash.TabPane} pane
27411          */
27412         "activatepane" : true
27413         
27414          
27415     });
27416     
27417     this.panes = [];
27418 };
27419
27420 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
27421
27422     title : '',
27423     icon : false,
27424     showtabs : true,
27425     tabScrollable : false,
27426     
27427     getChildContainer : function()
27428     {
27429         return this.el.select('.tab-content', true).first();
27430     },
27431     
27432     getAutoCreate : function(){
27433         
27434         var header = {
27435             tag: 'li',
27436             cls: 'pull-left header',
27437             html: this.title,
27438             cn : []
27439         };
27440         
27441         if(this.icon){
27442             header.cn.push({
27443                 tag: 'i',
27444                 cls: 'fa ' + this.icon
27445             });
27446         }
27447         
27448         var h = {
27449             tag: 'ul',
27450             cls: 'nav nav-tabs pull-right',
27451             cn: [
27452                 header
27453             ]
27454         };
27455         
27456         if(this.tabScrollable){
27457             h = {
27458                 tag: 'div',
27459                 cls: 'tab-header',
27460                 cn: [
27461                     {
27462                         tag: 'ul',
27463                         cls: 'nav nav-tabs pull-right',
27464                         cn: [
27465                             header
27466                         ]
27467                     }
27468                 ]
27469             };
27470         }
27471         
27472         var cfg = {
27473             tag: 'div',
27474             cls: 'nav-tabs-custom',
27475             cn: [
27476                 h,
27477                 {
27478                     tag: 'div',
27479                     cls: 'tab-content no-padding',
27480                     cn: []
27481                 }
27482             ]
27483         };
27484
27485         return  cfg;
27486     },
27487     initEvents : function()
27488     {
27489         //Roo.log('add add pane handler');
27490         this.on('addpane', this.onAddPane, this);
27491     },
27492      /**
27493      * Updates the box title
27494      * @param {String} html to set the title to.
27495      */
27496     setTitle : function(value)
27497     {
27498         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
27499     },
27500     onAddPane : function(pane)
27501     {
27502         this.panes.push(pane);
27503         //Roo.log('addpane');
27504         //Roo.log(pane);
27505         // tabs are rendere left to right..
27506         if(!this.showtabs){
27507             return;
27508         }
27509         
27510         var ctr = this.el.select('.nav-tabs', true).first();
27511          
27512          
27513         var existing = ctr.select('.nav-tab',true);
27514         var qty = existing.getCount();;
27515         
27516         
27517         var tab = ctr.createChild({
27518             tag : 'li',
27519             cls : 'nav-tab' + (qty ? '' : ' active'),
27520             cn : [
27521                 {
27522                     tag : 'a',
27523                     href:'#',
27524                     html : pane.title
27525                 }
27526             ]
27527         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
27528         pane.tab = tab;
27529         
27530         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
27531         if (!qty) {
27532             pane.el.addClass('active');
27533         }
27534         
27535                 
27536     },
27537     onTabClick : function(ev,un,ob,pane)
27538     {
27539         //Roo.log('tab - prev default');
27540         ev.preventDefault();
27541         
27542         
27543         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
27544         pane.tab.addClass('active');
27545         //Roo.log(pane.title);
27546         this.getChildContainer().select('.tab-pane',true).removeClass('active');
27547         // technically we should have a deactivate event.. but maybe add later.
27548         // and it should not de-activate the selected tab...
27549         this.fireEvent('activatepane', pane);
27550         pane.el.addClass('active');
27551         pane.fireEvent('activate');
27552         
27553         
27554     },
27555     
27556     getActivePane : function()
27557     {
27558         var r = false;
27559         Roo.each(this.panes, function(p) {
27560             if(p.el.hasClass('active')){
27561                 r = p;
27562                 return false;
27563             }
27564             
27565             return;
27566         });
27567         
27568         return r;
27569     }
27570     
27571     
27572 });
27573
27574  
27575 /*
27576  * - LGPL
27577  *
27578  * Tab pane
27579  * 
27580  */
27581 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27582 /**
27583  * @class Roo.bootstrap.TabPane
27584  * @extends Roo.bootstrap.Component
27585  * Bootstrap TabPane class
27586  * @cfg {Boolean} active (false | true) Default false
27587  * @cfg {String} title title of panel
27588
27589  * 
27590  * @constructor
27591  * Create a new TabPane
27592  * @param {Object} config The config object
27593  */
27594
27595 Roo.bootstrap.dash.TabPane = function(config){
27596     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
27597     
27598     this.addEvents({
27599         // raw events
27600         /**
27601          * @event activate
27602          * When a pane is activated
27603          * @param {Roo.bootstrap.dash.TabPane} pane
27604          */
27605         "activate" : true
27606          
27607     });
27608 };
27609
27610 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
27611     
27612     active : false,
27613     title : '',
27614     
27615     // the tabBox that this is attached to.
27616     tab : false,
27617      
27618     getAutoCreate : function() 
27619     {
27620         var cfg = {
27621             tag: 'div',
27622             cls: 'tab-pane'
27623         };
27624         
27625         if(this.active){
27626             cfg.cls += ' active';
27627         }
27628         
27629         return cfg;
27630     },
27631     initEvents  : function()
27632     {
27633         //Roo.log('trigger add pane handler');
27634         this.parent().fireEvent('addpane', this)
27635     },
27636     
27637      /**
27638      * Updates the tab title 
27639      * @param {String} html to set the title to.
27640      */
27641     setTitle: function(str)
27642     {
27643         if (!this.tab) {
27644             return;
27645         }
27646         this.title = str;
27647         this.tab.select('a', true).first().dom.innerHTML = str;
27648         
27649     }
27650     
27651     
27652     
27653 });
27654
27655  
27656
27657
27658  /*
27659  * - LGPL
27660  *
27661  * menu
27662  * 
27663  */
27664 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
27665
27666 /**
27667  * @class Roo.bootstrap.menu.Menu
27668  * @extends Roo.bootstrap.Component
27669  * Bootstrap Menu class - container for Menu
27670  * @cfg {String} html Text of the menu
27671  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
27672  * @cfg {String} icon Font awesome icon
27673  * @cfg {String} pos Menu align to (top | bottom) default bottom
27674  * 
27675  * 
27676  * @constructor
27677  * Create a new Menu
27678  * @param {Object} config The config object
27679  */
27680
27681
27682 Roo.bootstrap.menu.Menu = function(config){
27683     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
27684     
27685     this.addEvents({
27686         /**
27687          * @event beforeshow
27688          * Fires before this menu is displayed
27689          * @param {Roo.bootstrap.menu.Menu} this
27690          */
27691         beforeshow : true,
27692         /**
27693          * @event beforehide
27694          * Fires before this menu is hidden
27695          * @param {Roo.bootstrap.menu.Menu} this
27696          */
27697         beforehide : true,
27698         /**
27699          * @event show
27700          * Fires after this menu is displayed
27701          * @param {Roo.bootstrap.menu.Menu} this
27702          */
27703         show : true,
27704         /**
27705          * @event hide
27706          * Fires after this menu is hidden
27707          * @param {Roo.bootstrap.menu.Menu} this
27708          */
27709         hide : true,
27710         /**
27711          * @event click
27712          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
27713          * @param {Roo.bootstrap.menu.Menu} this
27714          * @param {Roo.EventObject} e
27715          */
27716         click : true
27717     });
27718     
27719 };
27720
27721 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
27722     
27723     submenu : false,
27724     html : '',
27725     weight : 'default',
27726     icon : false,
27727     pos : 'bottom',
27728     
27729     
27730     getChildContainer : function() {
27731         if(this.isSubMenu){
27732             return this.el;
27733         }
27734         
27735         return this.el.select('ul.dropdown-menu', true).first();  
27736     },
27737     
27738     getAutoCreate : function()
27739     {
27740         var text = [
27741             {
27742                 tag : 'span',
27743                 cls : 'roo-menu-text',
27744                 html : this.html
27745             }
27746         ];
27747         
27748         if(this.icon){
27749             text.unshift({
27750                 tag : 'i',
27751                 cls : 'fa ' + this.icon
27752             })
27753         }
27754         
27755         
27756         var cfg = {
27757             tag : 'div',
27758             cls : 'btn-group',
27759             cn : [
27760                 {
27761                     tag : 'button',
27762                     cls : 'dropdown-button btn btn-' + this.weight,
27763                     cn : text
27764                 },
27765                 {
27766                     tag : 'button',
27767                     cls : 'dropdown-toggle btn btn-' + this.weight,
27768                     cn : [
27769                         {
27770                             tag : 'span',
27771                             cls : 'caret'
27772                         }
27773                     ]
27774                 },
27775                 {
27776                     tag : 'ul',
27777                     cls : 'dropdown-menu'
27778                 }
27779             ]
27780             
27781         };
27782         
27783         if(this.pos == 'top'){
27784             cfg.cls += ' dropup';
27785         }
27786         
27787         if(this.isSubMenu){
27788             cfg = {
27789                 tag : 'ul',
27790                 cls : 'dropdown-menu'
27791             }
27792         }
27793         
27794         return cfg;
27795     },
27796     
27797     onRender : function(ct, position)
27798     {
27799         this.isSubMenu = ct.hasClass('dropdown-submenu');
27800         
27801         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
27802     },
27803     
27804     initEvents : function() 
27805     {
27806         if(this.isSubMenu){
27807             return;
27808         }
27809         
27810         this.hidden = true;
27811         
27812         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
27813         this.triggerEl.on('click', this.onTriggerPress, this);
27814         
27815         this.buttonEl = this.el.select('button.dropdown-button', true).first();
27816         this.buttonEl.on('click', this.onClick, this);
27817         
27818     },
27819     
27820     list : function()
27821     {
27822         if(this.isSubMenu){
27823             return this.el;
27824         }
27825         
27826         return this.el.select('ul.dropdown-menu', true).first();
27827     },
27828     
27829     onClick : function(e)
27830     {
27831         this.fireEvent("click", this, e);
27832     },
27833     
27834     onTriggerPress  : function(e)
27835     {   
27836         if (this.isVisible()) {
27837             this.hide();
27838         } else {
27839             this.show();
27840         }
27841     },
27842     
27843     isVisible : function(){
27844         return !this.hidden;
27845     },
27846     
27847     show : function()
27848     {
27849         this.fireEvent("beforeshow", this);
27850         
27851         this.hidden = false;
27852         this.el.addClass('open');
27853         
27854         Roo.get(document).on("mouseup", this.onMouseUp, this);
27855         
27856         this.fireEvent("show", this);
27857         
27858         
27859     },
27860     
27861     hide : function()
27862     {
27863         this.fireEvent("beforehide", this);
27864         
27865         this.hidden = true;
27866         this.el.removeClass('open');
27867         
27868         Roo.get(document).un("mouseup", this.onMouseUp);
27869         
27870         this.fireEvent("hide", this);
27871     },
27872     
27873     onMouseUp : function()
27874     {
27875         this.hide();
27876     }
27877     
27878 });
27879
27880  
27881  /*
27882  * - LGPL
27883  *
27884  * menu item
27885  * 
27886  */
27887 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
27888
27889 /**
27890  * @class Roo.bootstrap.menu.Item
27891  * @extends Roo.bootstrap.Component
27892  * Bootstrap MenuItem class
27893  * @cfg {Boolean} submenu (true | false) default false
27894  * @cfg {String} html text of the item
27895  * @cfg {String} href the link
27896  * @cfg {Boolean} disable (true | false) default false
27897  * @cfg {Boolean} preventDefault (true | false) default true
27898  * @cfg {String} icon Font awesome icon
27899  * @cfg {String} pos Submenu align to (left | right) default right 
27900  * 
27901  * 
27902  * @constructor
27903  * Create a new Item
27904  * @param {Object} config The config object
27905  */
27906
27907
27908 Roo.bootstrap.menu.Item = function(config){
27909     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
27910     this.addEvents({
27911         /**
27912          * @event mouseover
27913          * Fires when the mouse is hovering over this menu
27914          * @param {Roo.bootstrap.menu.Item} this
27915          * @param {Roo.EventObject} e
27916          */
27917         mouseover : true,
27918         /**
27919          * @event mouseout
27920          * Fires when the mouse exits this menu
27921          * @param {Roo.bootstrap.menu.Item} this
27922          * @param {Roo.EventObject} e
27923          */
27924         mouseout : true,
27925         // raw events
27926         /**
27927          * @event click
27928          * The raw click event for the entire grid.
27929          * @param {Roo.EventObject} e
27930          */
27931         click : true
27932     });
27933 };
27934
27935 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
27936     
27937     submenu : false,
27938     href : '',
27939     html : '',
27940     preventDefault: true,
27941     disable : false,
27942     icon : false,
27943     pos : 'right',
27944     
27945     getAutoCreate : function()
27946     {
27947         var text = [
27948             {
27949                 tag : 'span',
27950                 cls : 'roo-menu-item-text',
27951                 html : this.html
27952             }
27953         ];
27954         
27955         if(this.icon){
27956             text.unshift({
27957                 tag : 'i',
27958                 cls : 'fa ' + this.icon
27959             })
27960         }
27961         
27962         var cfg = {
27963             tag : 'li',
27964             cn : [
27965                 {
27966                     tag : 'a',
27967                     href : this.href || '#',
27968                     cn : text
27969                 }
27970             ]
27971         };
27972         
27973         if(this.disable){
27974             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
27975         }
27976         
27977         if(this.submenu){
27978             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
27979             
27980             if(this.pos == 'left'){
27981                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
27982             }
27983         }
27984         
27985         return cfg;
27986     },
27987     
27988     initEvents : function() 
27989     {
27990         this.el.on('mouseover', this.onMouseOver, this);
27991         this.el.on('mouseout', this.onMouseOut, this);
27992         
27993         this.el.select('a', true).first().on('click', this.onClick, this);
27994         
27995     },
27996     
27997     onClick : function(e)
27998     {
27999         if(this.preventDefault){
28000             e.preventDefault();
28001         }
28002         
28003         this.fireEvent("click", this, e);
28004     },
28005     
28006     onMouseOver : function(e)
28007     {
28008         if(this.submenu && this.pos == 'left'){
28009             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28010         }
28011         
28012         this.fireEvent("mouseover", this, e);
28013     },
28014     
28015     onMouseOut : function(e)
28016     {
28017         this.fireEvent("mouseout", this, e);
28018     }
28019 });
28020
28021  
28022
28023  /*
28024  * - LGPL
28025  *
28026  * menu separator
28027  * 
28028  */
28029 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28030
28031 /**
28032  * @class Roo.bootstrap.menu.Separator
28033  * @extends Roo.bootstrap.Component
28034  * Bootstrap Separator class
28035  * 
28036  * @constructor
28037  * Create a new Separator
28038  * @param {Object} config The config object
28039  */
28040
28041
28042 Roo.bootstrap.menu.Separator = function(config){
28043     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28044 };
28045
28046 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28047     
28048     getAutoCreate : function(){
28049         var cfg = {
28050             tag : 'li',
28051             cls: 'divider'
28052         };
28053         
28054         return cfg;
28055     }
28056    
28057 });
28058
28059  
28060
28061  /*
28062  * - LGPL
28063  *
28064  * Tooltip
28065  * 
28066  */
28067
28068 /**
28069  * @class Roo.bootstrap.Tooltip
28070  * Bootstrap Tooltip class
28071  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28072  * to determine which dom element triggers the tooltip.
28073  * 
28074  * It needs to add support for additional attributes like tooltip-position
28075  * 
28076  * @constructor
28077  * Create a new Toolti
28078  * @param {Object} config The config object
28079  */
28080
28081 Roo.bootstrap.Tooltip = function(config){
28082     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28083     
28084     this.alignment = Roo.bootstrap.Tooltip.alignment;
28085     
28086     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28087         this.alignment = config.alignment;
28088     }
28089     
28090 };
28091
28092 Roo.apply(Roo.bootstrap.Tooltip, {
28093     /**
28094      * @function init initialize tooltip monitoring.
28095      * @static
28096      */
28097     currentEl : false,
28098     currentTip : false,
28099     currentRegion : false,
28100     
28101     //  init : delay?
28102     
28103     init : function()
28104     {
28105         Roo.get(document).on('mouseover', this.enter ,this);
28106         Roo.get(document).on('mouseout', this.leave, this);
28107          
28108         
28109         this.currentTip = new Roo.bootstrap.Tooltip();
28110     },
28111     
28112     enter : function(ev)
28113     {
28114         var dom = ev.getTarget();
28115         
28116         //Roo.log(['enter',dom]);
28117         var el = Roo.fly(dom);
28118         if (this.currentEl) {
28119             //Roo.log(dom);
28120             //Roo.log(this.currentEl);
28121             //Roo.log(this.currentEl.contains(dom));
28122             if (this.currentEl == el) {
28123                 return;
28124             }
28125             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28126                 return;
28127             }
28128
28129         }
28130         
28131         if (this.currentTip.el) {
28132             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28133         }    
28134         //Roo.log(ev);
28135         
28136         if(!el || el.dom == document){
28137             return;
28138         }
28139         
28140         var bindEl = el;
28141         
28142         // you can not look for children, as if el is the body.. then everythign is the child..
28143         if (!el.attr('tooltip')) { //
28144             if (!el.select("[tooltip]").elements.length) {
28145                 return;
28146             }
28147             // is the mouse over this child...?
28148             bindEl = el.select("[tooltip]").first();
28149             var xy = ev.getXY();
28150             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28151                 //Roo.log("not in region.");
28152                 return;
28153             }
28154             //Roo.log("child element over..");
28155             
28156         }
28157         this.currentEl = bindEl;
28158         this.currentTip.bind(bindEl);
28159         this.currentRegion = Roo.lib.Region.getRegion(dom);
28160         this.currentTip.enter();
28161         
28162     },
28163     leave : function(ev)
28164     {
28165         var dom = ev.getTarget();
28166         //Roo.log(['leave',dom]);
28167         if (!this.currentEl) {
28168             return;
28169         }
28170         
28171         
28172         if (dom != this.currentEl.dom) {
28173             return;
28174         }
28175         var xy = ev.getXY();
28176         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
28177             return;
28178         }
28179         // only activate leave if mouse cursor is outside... bounding box..
28180         
28181         
28182         
28183         
28184         if (this.currentTip) {
28185             this.currentTip.leave();
28186         }
28187         //Roo.log('clear currentEl');
28188         this.currentEl = false;
28189         
28190         
28191     },
28192     alignment : {
28193         'left' : ['r-l', [-2,0], 'right'],
28194         'right' : ['l-r', [2,0], 'left'],
28195         'bottom' : ['t-b', [0,2], 'top'],
28196         'top' : [ 'b-t', [0,-2], 'bottom']
28197     }
28198     
28199 });
28200
28201
28202 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
28203     
28204     
28205     bindEl : false,
28206     
28207     delay : null, // can be { show : 300 , hide: 500}
28208     
28209     timeout : null,
28210     
28211     hoverState : null, //???
28212     
28213     placement : 'bottom', 
28214     
28215     alignment : false,
28216     
28217     getAutoCreate : function(){
28218     
28219         var cfg = {
28220            cls : 'tooltip',
28221            role : 'tooltip',
28222            cn : [
28223                 {
28224                     cls : 'tooltip-arrow'
28225                 },
28226                 {
28227                     cls : 'tooltip-inner'
28228                 }
28229            ]
28230         };
28231         
28232         return cfg;
28233     },
28234     bind : function(el)
28235     {
28236         this.bindEl = el;
28237     },
28238       
28239     
28240     enter : function () {
28241        
28242         if (this.timeout != null) {
28243             clearTimeout(this.timeout);
28244         }
28245         
28246         this.hoverState = 'in';
28247          //Roo.log("enter - show");
28248         if (!this.delay || !this.delay.show) {
28249             this.show();
28250             return;
28251         }
28252         var _t = this;
28253         this.timeout = setTimeout(function () {
28254             if (_t.hoverState == 'in') {
28255                 _t.show();
28256             }
28257         }, this.delay.show);
28258     },
28259     leave : function()
28260     {
28261         clearTimeout(this.timeout);
28262     
28263         this.hoverState = 'out';
28264          if (!this.delay || !this.delay.hide) {
28265             this.hide();
28266             return;
28267         }
28268        
28269         var _t = this;
28270         this.timeout = setTimeout(function () {
28271             //Roo.log("leave - timeout");
28272             
28273             if (_t.hoverState == 'out') {
28274                 _t.hide();
28275                 Roo.bootstrap.Tooltip.currentEl = false;
28276             }
28277         }, delay);
28278     },
28279     
28280     show : function (msg)
28281     {
28282         if (!this.el) {
28283             this.render(document.body);
28284         }
28285         // set content.
28286         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28287         
28288         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28289         
28290         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28291         
28292         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
28293         
28294         var placement = typeof this.placement == 'function' ?
28295             this.placement.call(this, this.el, on_el) :
28296             this.placement;
28297             
28298         var autoToken = /\s?auto?\s?/i;
28299         var autoPlace = autoToken.test(placement);
28300         if (autoPlace) {
28301             placement = placement.replace(autoToken, '') || 'top';
28302         }
28303         
28304         //this.el.detach()
28305         //this.el.setXY([0,0]);
28306         this.el.show();
28307         //this.el.dom.style.display='block';
28308         
28309         //this.el.appendTo(on_el);
28310         
28311         var p = this.getPosition();
28312         var box = this.el.getBox();
28313         
28314         if (autoPlace) {
28315             // fixme..
28316         }
28317         
28318         var align = this.alignment[placement];
28319         
28320         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28321         
28322         if(placement == 'top' || placement == 'bottom'){
28323             if(xy[0] < 0){
28324                 placement = 'right';
28325             }
28326             
28327             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28328                 placement = 'left';
28329             }
28330             
28331             var scroll = Roo.select('body', true).first().getScroll();
28332             
28333             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28334                 placement = 'top';
28335             }
28336             
28337             align = this.alignment[placement];
28338         }
28339         
28340         this.el.alignTo(this.bindEl, align[0],align[1]);
28341         //var arrow = this.el.select('.arrow',true).first();
28342         //arrow.set(align[2], 
28343         
28344         this.el.addClass(placement);
28345         
28346         this.el.addClass('in fade');
28347         
28348         this.hoverState = null;
28349         
28350         if (this.el.hasClass('fade')) {
28351             // fade it?
28352         }
28353         
28354     },
28355     hide : function()
28356     {
28357          
28358         if (!this.el) {
28359             return;
28360         }
28361         //this.el.setXY([0,0]);
28362         this.el.removeClass('in');
28363         //this.el.hide();
28364         
28365     }
28366     
28367 });
28368  
28369
28370  /*
28371  * - LGPL
28372  *
28373  * Location Picker
28374  * 
28375  */
28376
28377 /**
28378  * @class Roo.bootstrap.LocationPicker
28379  * @extends Roo.bootstrap.Component
28380  * Bootstrap LocationPicker class
28381  * @cfg {Number} latitude Position when init default 0
28382  * @cfg {Number} longitude Position when init default 0
28383  * @cfg {Number} zoom default 15
28384  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
28385  * @cfg {Boolean} mapTypeControl default false
28386  * @cfg {Boolean} disableDoubleClickZoom default false
28387  * @cfg {Boolean} scrollwheel default true
28388  * @cfg {Boolean} streetViewControl default false
28389  * @cfg {Number} radius default 0
28390  * @cfg {String} locationName
28391  * @cfg {Boolean} draggable default true
28392  * @cfg {Boolean} enableAutocomplete default false
28393  * @cfg {Boolean} enableReverseGeocode default true
28394  * @cfg {String} markerTitle
28395  * 
28396  * @constructor
28397  * Create a new LocationPicker
28398  * @param {Object} config The config object
28399  */
28400
28401
28402 Roo.bootstrap.LocationPicker = function(config){
28403     
28404     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
28405     
28406     this.addEvents({
28407         /**
28408          * @event initial
28409          * Fires when the picker initialized.
28410          * @param {Roo.bootstrap.LocationPicker} this
28411          * @param {Google Location} location
28412          */
28413         initial : true,
28414         /**
28415          * @event positionchanged
28416          * Fires when the picker position changed.
28417          * @param {Roo.bootstrap.LocationPicker} this
28418          * @param {Google Location} location
28419          */
28420         positionchanged : true,
28421         /**
28422          * @event resize
28423          * Fires when the map resize.
28424          * @param {Roo.bootstrap.LocationPicker} this
28425          */
28426         resize : true,
28427         /**
28428          * @event show
28429          * Fires when the map show.
28430          * @param {Roo.bootstrap.LocationPicker} this
28431          */
28432         show : true,
28433         /**
28434          * @event hide
28435          * Fires when the map hide.
28436          * @param {Roo.bootstrap.LocationPicker} this
28437          */
28438         hide : true,
28439         /**
28440          * @event mapClick
28441          * Fires when click the map.
28442          * @param {Roo.bootstrap.LocationPicker} this
28443          * @param {Map event} e
28444          */
28445         mapClick : true,
28446         /**
28447          * @event mapRightClick
28448          * Fires when right click the map.
28449          * @param {Roo.bootstrap.LocationPicker} this
28450          * @param {Map event} e
28451          */
28452         mapRightClick : true,
28453         /**
28454          * @event markerClick
28455          * Fires when click the marker.
28456          * @param {Roo.bootstrap.LocationPicker} this
28457          * @param {Map event} e
28458          */
28459         markerClick : true,
28460         /**
28461          * @event markerRightClick
28462          * Fires when right click the marker.
28463          * @param {Roo.bootstrap.LocationPicker} this
28464          * @param {Map event} e
28465          */
28466         markerRightClick : true,
28467         /**
28468          * @event OverlayViewDraw
28469          * Fires when OverlayView Draw
28470          * @param {Roo.bootstrap.LocationPicker} this
28471          */
28472         OverlayViewDraw : true,
28473         /**
28474          * @event OverlayViewOnAdd
28475          * Fires when OverlayView Draw
28476          * @param {Roo.bootstrap.LocationPicker} this
28477          */
28478         OverlayViewOnAdd : true,
28479         /**
28480          * @event OverlayViewOnRemove
28481          * Fires when OverlayView Draw
28482          * @param {Roo.bootstrap.LocationPicker} this
28483          */
28484         OverlayViewOnRemove : true,
28485         /**
28486          * @event OverlayViewShow
28487          * Fires when OverlayView Draw
28488          * @param {Roo.bootstrap.LocationPicker} this
28489          * @param {Pixel} cpx
28490          */
28491         OverlayViewShow : true,
28492         /**
28493          * @event OverlayViewHide
28494          * Fires when OverlayView Draw
28495          * @param {Roo.bootstrap.LocationPicker} this
28496          */
28497         OverlayViewHide : true,
28498         /**
28499          * @event loadexception
28500          * Fires when load google lib failed.
28501          * @param {Roo.bootstrap.LocationPicker} this
28502          */
28503         loadexception : true
28504     });
28505         
28506 };
28507
28508 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
28509     
28510     gMapContext: false,
28511     
28512     latitude: 0,
28513     longitude: 0,
28514     zoom: 15,
28515     mapTypeId: false,
28516     mapTypeControl: false,
28517     disableDoubleClickZoom: false,
28518     scrollwheel: true,
28519     streetViewControl: false,
28520     radius: 0,
28521     locationName: '',
28522     draggable: true,
28523     enableAutocomplete: false,
28524     enableReverseGeocode: true,
28525     markerTitle: '',
28526     
28527     getAutoCreate: function()
28528     {
28529
28530         var cfg = {
28531             tag: 'div',
28532             cls: 'roo-location-picker'
28533         };
28534         
28535         return cfg
28536     },
28537     
28538     initEvents: function(ct, position)
28539     {       
28540         if(!this.el.getWidth() || this.isApplied()){
28541             return;
28542         }
28543         
28544         this.el.setVisibilityMode(Roo.Element.DISPLAY);
28545         
28546         this.initial();
28547     },
28548     
28549     initial: function()
28550     {
28551         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
28552             this.fireEvent('loadexception', this);
28553             return;
28554         }
28555         
28556         if(!this.mapTypeId){
28557             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
28558         }
28559         
28560         this.gMapContext = this.GMapContext();
28561         
28562         this.initOverlayView();
28563         
28564         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
28565         
28566         var _this = this;
28567                 
28568         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
28569             _this.setPosition(_this.gMapContext.marker.position);
28570         });
28571         
28572         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
28573             _this.fireEvent('mapClick', this, event);
28574             
28575         });
28576
28577         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
28578             _this.fireEvent('mapRightClick', this, event);
28579             
28580         });
28581         
28582         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
28583             _this.fireEvent('markerClick', this, event);
28584             
28585         });
28586
28587         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
28588             _this.fireEvent('markerRightClick', this, event);
28589             
28590         });
28591         
28592         this.setPosition(this.gMapContext.location);
28593         
28594         this.fireEvent('initial', this, this.gMapContext.location);
28595     },
28596     
28597     initOverlayView: function()
28598     {
28599         var _this = this;
28600         
28601         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
28602             
28603             draw: function()
28604             {
28605                 _this.fireEvent('OverlayViewDraw', _this);
28606             },
28607             
28608             onAdd: function()
28609             {
28610                 _this.fireEvent('OverlayViewOnAdd', _this);
28611             },
28612             
28613             onRemove: function()
28614             {
28615                 _this.fireEvent('OverlayViewOnRemove', _this);
28616             },
28617             
28618             show: function(cpx)
28619             {
28620                 _this.fireEvent('OverlayViewShow', _this, cpx);
28621             },
28622             
28623             hide: function()
28624             {
28625                 _this.fireEvent('OverlayViewHide', _this);
28626             }
28627             
28628         });
28629     },
28630     
28631     fromLatLngToContainerPixel: function(event)
28632     {
28633         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
28634     },
28635     
28636     isApplied: function() 
28637     {
28638         return this.getGmapContext() == false ? false : true;
28639     },
28640     
28641     getGmapContext: function() 
28642     {
28643         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
28644     },
28645     
28646     GMapContext: function() 
28647     {
28648         var position = new google.maps.LatLng(this.latitude, this.longitude);
28649         
28650         var _map = new google.maps.Map(this.el.dom, {
28651             center: position,
28652             zoom: this.zoom,
28653             mapTypeId: this.mapTypeId,
28654             mapTypeControl: this.mapTypeControl,
28655             disableDoubleClickZoom: this.disableDoubleClickZoom,
28656             scrollwheel: this.scrollwheel,
28657             streetViewControl: this.streetViewControl,
28658             locationName: this.locationName,
28659             draggable: this.draggable,
28660             enableAutocomplete: this.enableAutocomplete,
28661             enableReverseGeocode: this.enableReverseGeocode
28662         });
28663         
28664         var _marker = new google.maps.Marker({
28665             position: position,
28666             map: _map,
28667             title: this.markerTitle,
28668             draggable: this.draggable
28669         });
28670         
28671         return {
28672             map: _map,
28673             marker: _marker,
28674             circle: null,
28675             location: position,
28676             radius: this.radius,
28677             locationName: this.locationName,
28678             addressComponents: {
28679                 formatted_address: null,
28680                 addressLine1: null,
28681                 addressLine2: null,
28682                 streetName: null,
28683                 streetNumber: null,
28684                 city: null,
28685                 district: null,
28686                 state: null,
28687                 stateOrProvince: null
28688             },
28689             settings: this,
28690             domContainer: this.el.dom,
28691             geodecoder: new google.maps.Geocoder()
28692         };
28693     },
28694     
28695     drawCircle: function(center, radius, options) 
28696     {
28697         if (this.gMapContext.circle != null) {
28698             this.gMapContext.circle.setMap(null);
28699         }
28700         if (radius > 0) {
28701             radius *= 1;
28702             options = Roo.apply({}, options, {
28703                 strokeColor: "#0000FF",
28704                 strokeOpacity: .35,
28705                 strokeWeight: 2,
28706                 fillColor: "#0000FF",
28707                 fillOpacity: .2
28708             });
28709             
28710             options.map = this.gMapContext.map;
28711             options.radius = radius;
28712             options.center = center;
28713             this.gMapContext.circle = new google.maps.Circle(options);
28714             return this.gMapContext.circle;
28715         }
28716         
28717         return null;
28718     },
28719     
28720     setPosition: function(location) 
28721     {
28722         this.gMapContext.location = location;
28723         this.gMapContext.marker.setPosition(location);
28724         this.gMapContext.map.panTo(location);
28725         this.drawCircle(location, this.gMapContext.radius, {});
28726         
28727         var _this = this;
28728         
28729         if (this.gMapContext.settings.enableReverseGeocode) {
28730             this.gMapContext.geodecoder.geocode({
28731                 latLng: this.gMapContext.location
28732             }, function(results, status) {
28733                 
28734                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
28735                     _this.gMapContext.locationName = results[0].formatted_address;
28736                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
28737                     
28738                     _this.fireEvent('positionchanged', this, location);
28739                 }
28740             });
28741             
28742             return;
28743         }
28744         
28745         this.fireEvent('positionchanged', this, location);
28746     },
28747     
28748     resize: function()
28749     {
28750         google.maps.event.trigger(this.gMapContext.map, "resize");
28751         
28752         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
28753         
28754         this.fireEvent('resize', this);
28755     },
28756     
28757     setPositionByLatLng: function(latitude, longitude)
28758     {
28759         this.setPosition(new google.maps.LatLng(latitude, longitude));
28760     },
28761     
28762     getCurrentPosition: function() 
28763     {
28764         return {
28765             latitude: this.gMapContext.location.lat(),
28766             longitude: this.gMapContext.location.lng()
28767         };
28768     },
28769     
28770     getAddressName: function() 
28771     {
28772         return this.gMapContext.locationName;
28773     },
28774     
28775     getAddressComponents: function() 
28776     {
28777         return this.gMapContext.addressComponents;
28778     },
28779     
28780     address_component_from_google_geocode: function(address_components) 
28781     {
28782         var result = {};
28783         
28784         for (var i = 0; i < address_components.length; i++) {
28785             var component = address_components[i];
28786             if (component.types.indexOf("postal_code") >= 0) {
28787                 result.postalCode = component.short_name;
28788             } else if (component.types.indexOf("street_number") >= 0) {
28789                 result.streetNumber = component.short_name;
28790             } else if (component.types.indexOf("route") >= 0) {
28791                 result.streetName = component.short_name;
28792             } else if (component.types.indexOf("neighborhood") >= 0) {
28793                 result.city = component.short_name;
28794             } else if (component.types.indexOf("locality") >= 0) {
28795                 result.city = component.short_name;
28796             } else if (component.types.indexOf("sublocality") >= 0) {
28797                 result.district = component.short_name;
28798             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
28799                 result.stateOrProvince = component.short_name;
28800             } else if (component.types.indexOf("country") >= 0) {
28801                 result.country = component.short_name;
28802             }
28803         }
28804         
28805         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
28806         result.addressLine2 = "";
28807         return result;
28808     },
28809     
28810     setZoomLevel: function(zoom)
28811     {
28812         this.gMapContext.map.setZoom(zoom);
28813     },
28814     
28815     show: function()
28816     {
28817         if(!this.el){
28818             return;
28819         }
28820         
28821         this.el.show();
28822         
28823         this.resize();
28824         
28825         this.fireEvent('show', this);
28826     },
28827     
28828     hide: function()
28829     {
28830         if(!this.el){
28831             return;
28832         }
28833         
28834         this.el.hide();
28835         
28836         this.fireEvent('hide', this);
28837     }
28838     
28839 });
28840
28841 Roo.apply(Roo.bootstrap.LocationPicker, {
28842     
28843     OverlayView : function(map, options)
28844     {
28845         options = options || {};
28846         
28847         this.setMap(map);
28848     }
28849     
28850     
28851 });/**
28852  * @class Roo.bootstrap.Alert
28853  * @extends Roo.bootstrap.Component
28854  * Bootstrap Alert class - shows an alert area box
28855  * eg
28856  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
28857   Enter a valid email address
28858 </div>
28859  * @licence LGPL
28860  * @cfg {String} title The title of alert
28861  * @cfg {String} html The content of alert
28862  * @cfg {String} weight (  success | info | warning | danger )
28863  * @cfg {String} faicon font-awesomeicon
28864  * 
28865  * @constructor
28866  * Create a new alert
28867  * @param {Object} config The config object
28868  */
28869
28870
28871 Roo.bootstrap.Alert = function(config){
28872     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
28873     
28874 };
28875
28876 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
28877     
28878     title: '',
28879     html: '',
28880     weight: false,
28881     faicon: false,
28882     
28883     getAutoCreate : function()
28884     {
28885         
28886         var cfg = {
28887             tag : 'div',
28888             cls : 'alert',
28889             cn : [
28890                 {
28891                     tag : 'i',
28892                     cls : 'roo-alert-icon'
28893                     
28894                 },
28895                 {
28896                     tag : 'b',
28897                     cls : 'roo-alert-title',
28898                     html : this.title
28899                 },
28900                 {
28901                     tag : 'span',
28902                     cls : 'roo-alert-text',
28903                     html : this.html
28904                 }
28905             ]
28906         };
28907         
28908         if(this.faicon){
28909             cfg.cn[0].cls += ' fa ' + this.faicon;
28910         }
28911         
28912         if(this.weight){
28913             cfg.cls += ' alert-' + this.weight;
28914         }
28915         
28916         return cfg;
28917     },
28918     
28919     initEvents: function() 
28920     {
28921         this.el.setVisibilityMode(Roo.Element.DISPLAY);
28922     },
28923     
28924     setTitle : function(str)
28925     {
28926         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
28927     },
28928     
28929     setText : function(str)
28930     {
28931         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
28932     },
28933     
28934     setWeight : function(weight)
28935     {
28936         if(this.weight){
28937             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
28938         }
28939         
28940         this.weight = weight;
28941         
28942         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
28943     },
28944     
28945     setIcon : function(icon)
28946     {
28947         if(this.faicon){
28948             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
28949         }
28950         
28951         this.faicon = icon;
28952         
28953         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
28954     },
28955     
28956     hide: function() 
28957     {
28958         this.el.hide();   
28959     },
28960     
28961     show: function() 
28962     {  
28963         this.el.show();   
28964     }
28965     
28966 });
28967
28968  
28969 /*
28970 * Licence: LGPL
28971 */
28972
28973 /**
28974  * @class Roo.bootstrap.UploadCropbox
28975  * @extends Roo.bootstrap.Component
28976  * Bootstrap UploadCropbox class
28977  * @cfg {String} emptyText show when image has been loaded
28978  * @cfg {String} rotateNotify show when image too small to rotate
28979  * @cfg {Number} errorTimeout default 3000
28980  * @cfg {Number} minWidth default 300
28981  * @cfg {Number} minHeight default 300
28982  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
28983  * @cfg {Boolean} isDocument (true|false) default false
28984  * @cfg {String} url action url
28985  * @cfg {String} paramName default 'imageUpload'
28986  * @cfg {String} method default POST
28987  * @cfg {Boolean} loadMask (true|false) default true
28988  * @cfg {Boolean} loadingText default 'Loading...'
28989  * 
28990  * @constructor
28991  * Create a new UploadCropbox
28992  * @param {Object} config The config object
28993  */
28994
28995 Roo.bootstrap.UploadCropbox = function(config){
28996     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
28997     
28998     this.addEvents({
28999         /**
29000          * @event beforeselectfile
29001          * Fire before select file
29002          * @param {Roo.bootstrap.UploadCropbox} this
29003          */
29004         "beforeselectfile" : true,
29005         /**
29006          * @event initial
29007          * Fire after initEvent
29008          * @param {Roo.bootstrap.UploadCropbox} this
29009          */
29010         "initial" : true,
29011         /**
29012          * @event crop
29013          * Fire after initEvent
29014          * @param {Roo.bootstrap.UploadCropbox} this
29015          * @param {String} data
29016          */
29017         "crop" : true,
29018         /**
29019          * @event prepare
29020          * Fire when preparing the file data
29021          * @param {Roo.bootstrap.UploadCropbox} this
29022          * @param {Object} file
29023          */
29024         "prepare" : true,
29025         /**
29026          * @event exception
29027          * Fire when get exception
29028          * @param {Roo.bootstrap.UploadCropbox} this
29029          * @param {XMLHttpRequest} xhr
29030          */
29031         "exception" : true,
29032         /**
29033          * @event beforeloadcanvas
29034          * Fire before load the canvas
29035          * @param {Roo.bootstrap.UploadCropbox} this
29036          * @param {String} src
29037          */
29038         "beforeloadcanvas" : true,
29039         /**
29040          * @event trash
29041          * Fire when trash image
29042          * @param {Roo.bootstrap.UploadCropbox} this
29043          */
29044         "trash" : true,
29045         /**
29046          * @event download
29047          * Fire when download the image
29048          * @param {Roo.bootstrap.UploadCropbox} this
29049          */
29050         "download" : true,
29051         /**
29052          * @event footerbuttonclick
29053          * Fire when footerbuttonclick
29054          * @param {Roo.bootstrap.UploadCropbox} this
29055          * @param {String} type
29056          */
29057         "footerbuttonclick" : true,
29058         /**
29059          * @event resize
29060          * Fire when resize
29061          * @param {Roo.bootstrap.UploadCropbox} this
29062          */
29063         "resize" : true,
29064         /**
29065          * @event rotate
29066          * Fire when rotate the image
29067          * @param {Roo.bootstrap.UploadCropbox} this
29068          * @param {String} pos
29069          */
29070         "rotate" : true,
29071         /**
29072          * @event inspect
29073          * Fire when inspect the file
29074          * @param {Roo.bootstrap.UploadCropbox} this
29075          * @param {Object} file
29076          */
29077         "inspect" : true,
29078         /**
29079          * @event upload
29080          * Fire when xhr upload the file
29081          * @param {Roo.bootstrap.UploadCropbox} this
29082          * @param {Object} data
29083          */
29084         "upload" : true,
29085         /**
29086          * @event arrange
29087          * Fire when arrange the file data
29088          * @param {Roo.bootstrap.UploadCropbox} this
29089          * @param {Object} formData
29090          */
29091         "arrange" : true
29092     });
29093     
29094     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29095 };
29096
29097 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
29098     
29099     emptyText : 'Click to upload image',
29100     rotateNotify : 'Image is too small to rotate',
29101     errorTimeout : 3000,
29102     scale : 0,
29103     baseScale : 1,
29104     rotate : 0,
29105     dragable : false,
29106     pinching : false,
29107     mouseX : 0,
29108     mouseY : 0,
29109     cropData : false,
29110     minWidth : 300,
29111     minHeight : 300,
29112     file : false,
29113     exif : {},
29114     baseRotate : 1,
29115     cropType : 'image/jpeg',
29116     buttons : false,
29117     canvasLoaded : false,
29118     isDocument : false,
29119     method : 'POST',
29120     paramName : 'imageUpload',
29121     loadMask : true,
29122     loadingText : 'Loading...',
29123     maskEl : false,
29124     
29125     getAutoCreate : function()
29126     {
29127         var cfg = {
29128             tag : 'div',
29129             cls : 'roo-upload-cropbox',
29130             cn : [
29131                 {
29132                     tag : 'input',
29133                     cls : 'roo-upload-cropbox-selector',
29134                     type : 'file'
29135                 },
29136                 {
29137                     tag : 'div',
29138                     cls : 'roo-upload-cropbox-body',
29139                     style : 'cursor:pointer',
29140                     cn : [
29141                         {
29142                             tag : 'div',
29143                             cls : 'roo-upload-cropbox-preview'
29144                         },
29145                         {
29146                             tag : 'div',
29147                             cls : 'roo-upload-cropbox-thumb'
29148                         },
29149                         {
29150                             tag : 'div',
29151                             cls : 'roo-upload-cropbox-empty-notify',
29152                             html : this.emptyText
29153                         },
29154                         {
29155                             tag : 'div',
29156                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29157                             html : this.rotateNotify
29158                         }
29159                     ]
29160                 },
29161                 {
29162                     tag : 'div',
29163                     cls : 'roo-upload-cropbox-footer',
29164                     cn : {
29165                         tag : 'div',
29166                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29167                         cn : []
29168                     }
29169                 }
29170             ]
29171         };
29172         
29173         return cfg;
29174     },
29175     
29176     onRender : function(ct, position)
29177     {
29178         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29179         
29180         if (this.buttons.length) {
29181             
29182             Roo.each(this.buttons, function(bb) {
29183                 
29184                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29185                 
29186                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29187                 
29188             }, this);
29189         }
29190         
29191         if(this.loadMask){
29192             this.maskEl = this.el;
29193         }
29194     },
29195     
29196     initEvents : function()
29197     {
29198         this.urlAPI = (window.createObjectURL && window) || 
29199                                 (window.URL && URL.revokeObjectURL && URL) || 
29200                                 (window.webkitURL && webkitURL);
29201                         
29202         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29203         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29204         
29205         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29206         this.selectorEl.hide();
29207         
29208         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29209         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29210         
29211         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29212         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29213         this.thumbEl.hide();
29214         
29215         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29216         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29217         
29218         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29219         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29220         this.errorEl.hide();
29221         
29222         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29223         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29224         this.footerEl.hide();
29225         
29226         this.setThumbBoxSize();
29227         
29228         this.bind();
29229         
29230         this.resize();
29231         
29232         this.fireEvent('initial', this);
29233     },
29234
29235     bind : function()
29236     {
29237         var _this = this;
29238         
29239         window.addEventListener("resize", function() { _this.resize(); } );
29240         
29241         this.bodyEl.on('click', this.beforeSelectFile, this);
29242         
29243         if(Roo.isTouch){
29244             this.bodyEl.on('touchstart', this.onTouchStart, this);
29245             this.bodyEl.on('touchmove', this.onTouchMove, this);
29246             this.bodyEl.on('touchend', this.onTouchEnd, this);
29247         }
29248         
29249         if(!Roo.isTouch){
29250             this.bodyEl.on('mousedown', this.onMouseDown, this);
29251             this.bodyEl.on('mousemove', this.onMouseMove, this);
29252             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29253             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29254             Roo.get(document).on('mouseup', this.onMouseUp, this);
29255         }
29256         
29257         this.selectorEl.on('change', this.onFileSelected, this);
29258     },
29259     
29260     reset : function()
29261     {    
29262         this.scale = 0;
29263         this.baseScale = 1;
29264         this.rotate = 0;
29265         this.baseRotate = 1;
29266         this.dragable = false;
29267         this.pinching = false;
29268         this.mouseX = 0;
29269         this.mouseY = 0;
29270         this.cropData = false;
29271         this.notifyEl.dom.innerHTML = this.emptyText;
29272         
29273         this.selectorEl.dom.value = '';
29274         
29275     },
29276     
29277     resize : function()
29278     {
29279         if(this.fireEvent('resize', this) != false){
29280             this.setThumbBoxPosition();
29281             this.setCanvasPosition();
29282         }
29283     },
29284     
29285     onFooterButtonClick : function(e, el, o, type)
29286     {
29287         switch (type) {
29288             case 'rotate-left' :
29289                 this.onRotateLeft(e);
29290                 break;
29291             case 'rotate-right' :
29292                 this.onRotateRight(e);
29293                 break;
29294             case 'picture' :
29295                 this.beforeSelectFile(e);
29296                 break;
29297             case 'trash' :
29298                 this.trash(e);
29299                 break;
29300             case 'crop' :
29301                 this.crop(e);
29302                 break;
29303             case 'download' :
29304                 this.download(e);
29305                 break;
29306             default :
29307                 break;
29308         }
29309         
29310         this.fireEvent('footerbuttonclick', this, type);
29311     },
29312     
29313     beforeSelectFile : function(e)
29314     {
29315         e.preventDefault();
29316         
29317         if(this.fireEvent('beforeselectfile', this) != false){
29318             this.selectorEl.dom.click();
29319         }
29320     },
29321     
29322     onFileSelected : function(e)
29323     {
29324         e.preventDefault();
29325         
29326         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29327             return;
29328         }
29329         
29330         var file = this.selectorEl.dom.files[0];
29331         
29332         if(this.fireEvent('inspect', this, file) != false){
29333             this.prepare(file);
29334         }
29335         
29336     },
29337     
29338     trash : function(e)
29339     {
29340         this.fireEvent('trash', this);
29341     },
29342     
29343     download : function(e)
29344     {
29345         this.fireEvent('download', this);
29346     },
29347     
29348     loadCanvas : function(src)
29349     {   
29350         if(this.fireEvent('beforeloadcanvas', this, src) != false){
29351             
29352             this.reset();
29353             
29354             this.imageEl = document.createElement('img');
29355             
29356             var _this = this;
29357             
29358             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29359             
29360             this.imageEl.src = src;
29361         }
29362     },
29363     
29364     onLoadCanvas : function()
29365     {   
29366         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29367         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29368         
29369         this.bodyEl.un('click', this.beforeSelectFile, this);
29370         
29371         this.notifyEl.hide();
29372         this.thumbEl.show();
29373         this.footerEl.show();
29374         
29375         this.baseRotateLevel();
29376         
29377         if(this.isDocument){
29378             this.setThumbBoxSize();
29379         }
29380         
29381         this.setThumbBoxPosition();
29382         
29383         this.baseScaleLevel();
29384         
29385         this.draw();
29386         
29387         this.resize();
29388         
29389         this.canvasLoaded = true;
29390         
29391         if(this.loadMask){
29392             this.maskEl.unmask();
29393         }
29394         
29395     },
29396     
29397     setCanvasPosition : function()
29398     {   
29399         if(!this.canvasEl){
29400             return;
29401         }
29402         
29403         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
29404         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
29405         
29406         this.previewEl.setLeft(pw);
29407         this.previewEl.setTop(ph);
29408         
29409     },
29410     
29411     onMouseDown : function(e)
29412     {   
29413         e.stopEvent();
29414         
29415         this.dragable = true;
29416         this.pinching = false;
29417         
29418         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
29419             this.dragable = false;
29420             return;
29421         }
29422         
29423         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29424         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29425         
29426     },
29427     
29428     onMouseMove : function(e)
29429     {   
29430         e.stopEvent();
29431         
29432         if(!this.canvasLoaded){
29433             return;
29434         }
29435         
29436         if (!this.dragable){
29437             return;
29438         }
29439         
29440         var minX = Math.ceil(this.thumbEl.getLeft(true));
29441         var minY = Math.ceil(this.thumbEl.getTop(true));
29442         
29443         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
29444         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
29445         
29446         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29447         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29448         
29449         x = x - this.mouseX;
29450         y = y - this.mouseY;
29451         
29452         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
29453         var bgY = Math.ceil(y + this.previewEl.getTop(true));
29454         
29455         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
29456         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
29457         
29458         this.previewEl.setLeft(bgX);
29459         this.previewEl.setTop(bgY);
29460         
29461         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29462         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29463     },
29464     
29465     onMouseUp : function(e)
29466     {   
29467         e.stopEvent();
29468         
29469         this.dragable = false;
29470     },
29471     
29472     onMouseWheel : function(e)
29473     {   
29474         e.stopEvent();
29475         
29476         this.startScale = this.scale;
29477         
29478         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
29479         
29480         if(!this.zoomable()){
29481             this.scale = this.startScale;
29482             return;
29483         }
29484         
29485         this.draw();
29486         
29487         return;
29488     },
29489     
29490     zoomable : function()
29491     {
29492         var minScale = this.thumbEl.getWidth() / this.minWidth;
29493         
29494         if(this.minWidth < this.minHeight){
29495             minScale = this.thumbEl.getHeight() / this.minHeight;
29496         }
29497         
29498         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
29499         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
29500         
29501         if(
29502                 this.isDocument &&
29503                 (this.rotate == 0 || this.rotate == 180) && 
29504                 (
29505                     width > this.imageEl.OriginWidth || 
29506                     height > this.imageEl.OriginHeight ||
29507                     (width < this.minWidth && height < this.minHeight)
29508                 )
29509         ){
29510             return false;
29511         }
29512         
29513         if(
29514                 this.isDocument &&
29515                 (this.rotate == 90 || this.rotate == 270) && 
29516                 (
29517                     width > this.imageEl.OriginWidth || 
29518                     height > this.imageEl.OriginHeight ||
29519                     (width < this.minHeight && height < this.minWidth)
29520                 )
29521         ){
29522             return false;
29523         }
29524         
29525         if(
29526                 !this.isDocument &&
29527                 (this.rotate == 0 || this.rotate == 180) && 
29528                 (
29529                     width < this.minWidth || 
29530                     width > this.imageEl.OriginWidth || 
29531                     height < this.minHeight || 
29532                     height > this.imageEl.OriginHeight
29533                 )
29534         ){
29535             return false;
29536         }
29537         
29538         if(
29539                 !this.isDocument &&
29540                 (this.rotate == 90 || this.rotate == 270) && 
29541                 (
29542                     width < this.minHeight || 
29543                     width > this.imageEl.OriginWidth || 
29544                     height < this.minWidth || 
29545                     height > this.imageEl.OriginHeight
29546                 )
29547         ){
29548             return false;
29549         }
29550         
29551         return true;
29552         
29553     },
29554     
29555     onRotateLeft : function(e)
29556     {   
29557         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29558             
29559             var minScale = this.thumbEl.getWidth() / this.minWidth;
29560             
29561             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29562             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29563             
29564             this.startScale = this.scale;
29565             
29566             while (this.getScaleLevel() < minScale){
29567             
29568                 this.scale = this.scale + 1;
29569                 
29570                 if(!this.zoomable()){
29571                     break;
29572                 }
29573                 
29574                 if(
29575                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29576                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29577                 ){
29578                     continue;
29579                 }
29580                 
29581                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29582
29583                 this.draw();
29584                 
29585                 return;
29586             }
29587             
29588             this.scale = this.startScale;
29589             
29590             this.onRotateFail();
29591             
29592             return false;
29593         }
29594         
29595         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29596
29597         if(this.isDocument){
29598             this.setThumbBoxSize();
29599             this.setThumbBoxPosition();
29600             this.setCanvasPosition();
29601         }
29602         
29603         this.draw();
29604         
29605         this.fireEvent('rotate', this, 'left');
29606         
29607     },
29608     
29609     onRotateRight : function(e)
29610     {
29611         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29612             
29613             var minScale = this.thumbEl.getWidth() / this.minWidth;
29614         
29615             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29616             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29617             
29618             this.startScale = this.scale;
29619             
29620             while (this.getScaleLevel() < minScale){
29621             
29622                 this.scale = this.scale + 1;
29623                 
29624                 if(!this.zoomable()){
29625                     break;
29626                 }
29627                 
29628                 if(
29629                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29630                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29631                 ){
29632                     continue;
29633                 }
29634                 
29635                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29636
29637                 this.draw();
29638                 
29639                 return;
29640             }
29641             
29642             this.scale = this.startScale;
29643             
29644             this.onRotateFail();
29645             
29646             return false;
29647         }
29648         
29649         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29650
29651         if(this.isDocument){
29652             this.setThumbBoxSize();
29653             this.setThumbBoxPosition();
29654             this.setCanvasPosition();
29655         }
29656         
29657         this.draw();
29658         
29659         this.fireEvent('rotate', this, 'right');
29660     },
29661     
29662     onRotateFail : function()
29663     {
29664         this.errorEl.show(true);
29665         
29666         var _this = this;
29667         
29668         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
29669     },
29670     
29671     draw : function()
29672     {
29673         this.previewEl.dom.innerHTML = '';
29674         
29675         var canvasEl = document.createElement("canvas");
29676         
29677         var contextEl = canvasEl.getContext("2d");
29678         
29679         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29680         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29681         var center = this.imageEl.OriginWidth / 2;
29682         
29683         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
29684             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29685             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29686             center = this.imageEl.OriginHeight / 2;
29687         }
29688         
29689         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
29690         
29691         contextEl.translate(center, center);
29692         contextEl.rotate(this.rotate * Math.PI / 180);
29693
29694         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
29695         
29696         this.canvasEl = document.createElement("canvas");
29697         
29698         this.contextEl = this.canvasEl.getContext("2d");
29699         
29700         switch (this.rotate) {
29701             case 0 :
29702                 
29703                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29704                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29705                 
29706                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29707                 
29708                 break;
29709             case 90 : 
29710                 
29711                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29712                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29713                 
29714                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29715                     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);
29716                     break;
29717                 }
29718                 
29719                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29720                 
29721                 break;
29722             case 180 :
29723                 
29724                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29725                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29726                 
29727                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29728                     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);
29729                     break;
29730                 }
29731                 
29732                 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);
29733                 
29734                 break;
29735             case 270 :
29736                 
29737                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29738                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29739         
29740                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29741                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29742                     break;
29743                 }
29744                 
29745                 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);
29746                 
29747                 break;
29748             default : 
29749                 break;
29750         }
29751         
29752         this.previewEl.appendChild(this.canvasEl);
29753         
29754         this.setCanvasPosition();
29755     },
29756     
29757     crop : function()
29758     {
29759         if(!this.canvasLoaded){
29760             return;
29761         }
29762         
29763         var imageCanvas = document.createElement("canvas");
29764         
29765         var imageContext = imageCanvas.getContext("2d");
29766         
29767         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
29768         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
29769         
29770         var center = imageCanvas.width / 2;
29771         
29772         imageContext.translate(center, center);
29773         
29774         imageContext.rotate(this.rotate * Math.PI / 180);
29775         
29776         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
29777         
29778         var canvas = document.createElement("canvas");
29779         
29780         var context = canvas.getContext("2d");
29781                 
29782         canvas.width = this.minWidth;
29783         canvas.height = this.minHeight;
29784
29785         switch (this.rotate) {
29786             case 0 :
29787                 
29788                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
29789                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
29790                 
29791                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29792                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29793                 
29794                 var targetWidth = this.minWidth - 2 * x;
29795                 var targetHeight = this.minHeight - 2 * y;
29796                 
29797                 var scale = 1;
29798                 
29799                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29800                     scale = targetWidth / width;
29801                 }
29802                 
29803                 if(x > 0 && y == 0){
29804                     scale = targetHeight / height;
29805                 }
29806                 
29807                 if(x > 0 && y > 0){
29808                     scale = targetWidth / width;
29809                     
29810                     if(width < height){
29811                         scale = targetHeight / height;
29812                     }
29813                 }
29814                 
29815                 context.scale(scale, scale);
29816                 
29817                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29818                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29819
29820                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29821                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29822
29823                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29824                 
29825                 break;
29826             case 90 : 
29827                 
29828                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
29829                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
29830                 
29831                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29832                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29833                 
29834                 var targetWidth = this.minWidth - 2 * x;
29835                 var targetHeight = this.minHeight - 2 * y;
29836                 
29837                 var scale = 1;
29838                 
29839                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29840                     scale = targetWidth / width;
29841                 }
29842                 
29843                 if(x > 0 && y == 0){
29844                     scale = targetHeight / height;
29845                 }
29846                 
29847                 if(x > 0 && y > 0){
29848                     scale = targetWidth / width;
29849                     
29850                     if(width < height){
29851                         scale = targetHeight / height;
29852                     }
29853                 }
29854                 
29855                 context.scale(scale, scale);
29856                 
29857                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29858                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29859
29860                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29861                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29862                 
29863                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
29864                 
29865                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29866                 
29867                 break;
29868             case 180 :
29869                 
29870                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
29871                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
29872                 
29873                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29874                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29875                 
29876                 var targetWidth = this.minWidth - 2 * x;
29877                 var targetHeight = this.minHeight - 2 * y;
29878                 
29879                 var scale = 1;
29880                 
29881                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29882                     scale = targetWidth / width;
29883                 }
29884                 
29885                 if(x > 0 && y == 0){
29886                     scale = targetHeight / height;
29887                 }
29888                 
29889                 if(x > 0 && y > 0){
29890                     scale = targetWidth / width;
29891                     
29892                     if(width < height){
29893                         scale = targetHeight / height;
29894                     }
29895                 }
29896                 
29897                 context.scale(scale, scale);
29898                 
29899                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29900                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29901
29902                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29903                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29904
29905                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
29906                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
29907                 
29908                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29909                 
29910                 break;
29911             case 270 :
29912                 
29913                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
29914                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
29915                 
29916                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29917                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29918                 
29919                 var targetWidth = this.minWidth - 2 * x;
29920                 var targetHeight = this.minHeight - 2 * y;
29921                 
29922                 var scale = 1;
29923                 
29924                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29925                     scale = targetWidth / width;
29926                 }
29927                 
29928                 if(x > 0 && y == 0){
29929                     scale = targetHeight / height;
29930                 }
29931                 
29932                 if(x > 0 && y > 0){
29933                     scale = targetWidth / width;
29934                     
29935                     if(width < height){
29936                         scale = targetHeight / height;
29937                     }
29938                 }
29939                 
29940                 context.scale(scale, scale);
29941                 
29942                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29943                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29944
29945                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29946                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29947                 
29948                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
29949                 
29950                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29951                 
29952                 break;
29953             default : 
29954                 break;
29955         }
29956         
29957         this.cropData = canvas.toDataURL(this.cropType);
29958         
29959         if(this.fireEvent('crop', this, this.cropData) !== false){
29960             this.process(this.file, this.cropData);
29961         }
29962         
29963         return;
29964         
29965     },
29966     
29967     setThumbBoxSize : function()
29968     {
29969         var width, height;
29970         
29971         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
29972             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
29973             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
29974             
29975             this.minWidth = width;
29976             this.minHeight = height;
29977             
29978             if(this.rotate == 90 || this.rotate == 270){
29979                 this.minWidth = height;
29980                 this.minHeight = width;
29981             }
29982         }
29983         
29984         height = 300;
29985         width = Math.ceil(this.minWidth * height / this.minHeight);
29986         
29987         if(this.minWidth > this.minHeight){
29988             width = 300;
29989             height = Math.ceil(this.minHeight * width / this.minWidth);
29990         }
29991         
29992         this.thumbEl.setStyle({
29993             width : width + 'px',
29994             height : height + 'px'
29995         });
29996
29997         return;
29998             
29999     },
30000     
30001     setThumbBoxPosition : function()
30002     {
30003         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30004         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30005         
30006         this.thumbEl.setLeft(x);
30007         this.thumbEl.setTop(y);
30008         
30009     },
30010     
30011     baseRotateLevel : function()
30012     {
30013         this.baseRotate = 1;
30014         
30015         if(
30016                 typeof(this.exif) != 'undefined' &&
30017                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30018                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30019         ){
30020             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30021         }
30022         
30023         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30024         
30025     },
30026     
30027     baseScaleLevel : function()
30028     {
30029         var width, height;
30030         
30031         if(this.isDocument){
30032             
30033             if(this.baseRotate == 6 || this.baseRotate == 8){
30034             
30035                 height = this.thumbEl.getHeight();
30036                 this.baseScale = height / this.imageEl.OriginWidth;
30037
30038                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30039                     width = this.thumbEl.getWidth();
30040                     this.baseScale = width / this.imageEl.OriginHeight;
30041                 }
30042
30043                 return;
30044             }
30045
30046             height = this.thumbEl.getHeight();
30047             this.baseScale = height / this.imageEl.OriginHeight;
30048
30049             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30050                 width = this.thumbEl.getWidth();
30051                 this.baseScale = width / this.imageEl.OriginWidth;
30052             }
30053
30054             return;
30055         }
30056         
30057         if(this.baseRotate == 6 || this.baseRotate == 8){
30058             
30059             width = this.thumbEl.getHeight();
30060             this.baseScale = width / this.imageEl.OriginHeight;
30061             
30062             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30063                 height = this.thumbEl.getWidth();
30064                 this.baseScale = height / this.imageEl.OriginHeight;
30065             }
30066             
30067             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30068                 height = this.thumbEl.getWidth();
30069                 this.baseScale = height / this.imageEl.OriginHeight;
30070                 
30071                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30072                     width = this.thumbEl.getHeight();
30073                     this.baseScale = width / this.imageEl.OriginWidth;
30074                 }
30075             }
30076             
30077             return;
30078         }
30079         
30080         width = this.thumbEl.getWidth();
30081         this.baseScale = width / this.imageEl.OriginWidth;
30082         
30083         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30084             height = this.thumbEl.getHeight();
30085             this.baseScale = height / this.imageEl.OriginHeight;
30086         }
30087         
30088         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30089             
30090             height = this.thumbEl.getHeight();
30091             this.baseScale = height / this.imageEl.OriginHeight;
30092             
30093             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30094                 width = this.thumbEl.getWidth();
30095                 this.baseScale = width / this.imageEl.OriginWidth;
30096             }
30097             
30098         }
30099         
30100         return;
30101     },
30102     
30103     getScaleLevel : function()
30104     {
30105         return this.baseScale * Math.pow(1.1, this.scale);
30106     },
30107     
30108     onTouchStart : function(e)
30109     {
30110         if(!this.canvasLoaded){
30111             this.beforeSelectFile(e);
30112             return;
30113         }
30114         
30115         var touches = e.browserEvent.touches;
30116         
30117         if(!touches){
30118             return;
30119         }
30120         
30121         if(touches.length == 1){
30122             this.onMouseDown(e);
30123             return;
30124         }
30125         
30126         if(touches.length != 2){
30127             return;
30128         }
30129         
30130         var coords = [];
30131         
30132         for(var i = 0, finger; finger = touches[i]; i++){
30133             coords.push(finger.pageX, finger.pageY);
30134         }
30135         
30136         var x = Math.pow(coords[0] - coords[2], 2);
30137         var y = Math.pow(coords[1] - coords[3], 2);
30138         
30139         this.startDistance = Math.sqrt(x + y);
30140         
30141         this.startScale = this.scale;
30142         
30143         this.pinching = true;
30144         this.dragable = false;
30145         
30146     },
30147     
30148     onTouchMove : function(e)
30149     {
30150         if(!this.pinching && !this.dragable){
30151             return;
30152         }
30153         
30154         var touches = e.browserEvent.touches;
30155         
30156         if(!touches){
30157             return;
30158         }
30159         
30160         if(this.dragable){
30161             this.onMouseMove(e);
30162             return;
30163         }
30164         
30165         var coords = [];
30166         
30167         for(var i = 0, finger; finger = touches[i]; i++){
30168             coords.push(finger.pageX, finger.pageY);
30169         }
30170         
30171         var x = Math.pow(coords[0] - coords[2], 2);
30172         var y = Math.pow(coords[1] - coords[3], 2);
30173         
30174         this.endDistance = Math.sqrt(x + y);
30175         
30176         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30177         
30178         if(!this.zoomable()){
30179             this.scale = this.startScale;
30180             return;
30181         }
30182         
30183         this.draw();
30184         
30185     },
30186     
30187     onTouchEnd : function(e)
30188     {
30189         this.pinching = false;
30190         this.dragable = false;
30191         
30192     },
30193     
30194     process : function(file, crop)
30195     {
30196         if(this.loadMask){
30197             this.maskEl.mask(this.loadingText);
30198         }
30199         
30200         this.xhr = new XMLHttpRequest();
30201         
30202         file.xhr = this.xhr;
30203
30204         this.xhr.open(this.method, this.url, true);
30205         
30206         var headers = {
30207             "Accept": "application/json",
30208             "Cache-Control": "no-cache",
30209             "X-Requested-With": "XMLHttpRequest"
30210         };
30211         
30212         for (var headerName in headers) {
30213             var headerValue = headers[headerName];
30214             if (headerValue) {
30215                 this.xhr.setRequestHeader(headerName, headerValue);
30216             }
30217         }
30218         
30219         var _this = this;
30220         
30221         this.xhr.onload = function()
30222         {
30223             _this.xhrOnLoad(_this.xhr);
30224         }
30225         
30226         this.xhr.onerror = function()
30227         {
30228             _this.xhrOnError(_this.xhr);
30229         }
30230         
30231         var formData = new FormData();
30232
30233         formData.append('returnHTML', 'NO');
30234         
30235         if(crop){
30236             formData.append('crop', crop);
30237         }
30238         
30239         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30240             formData.append(this.paramName, file, file.name);
30241         }
30242         
30243         if(typeof(file.filename) != 'undefined'){
30244             formData.append('filename', file.filename);
30245         }
30246         
30247         if(typeof(file.mimetype) != 'undefined'){
30248             formData.append('mimetype', file.mimetype);
30249         }
30250         
30251         if(this.fireEvent('arrange', this, formData) != false){
30252             this.xhr.send(formData);
30253         };
30254     },
30255     
30256     xhrOnLoad : function(xhr)
30257     {
30258         if(this.loadMask){
30259             this.maskEl.unmask();
30260         }
30261         
30262         if (xhr.readyState !== 4) {
30263             this.fireEvent('exception', this, xhr);
30264             return;
30265         }
30266
30267         var response = Roo.decode(xhr.responseText);
30268         
30269         if(!response.success){
30270             this.fireEvent('exception', this, xhr);
30271             return;
30272         }
30273         
30274         var response = Roo.decode(xhr.responseText);
30275         
30276         this.fireEvent('upload', this, response);
30277         
30278     },
30279     
30280     xhrOnError : function()
30281     {
30282         if(this.loadMask){
30283             this.maskEl.unmask();
30284         }
30285         
30286         Roo.log('xhr on error');
30287         
30288         var response = Roo.decode(xhr.responseText);
30289           
30290         Roo.log(response);
30291         
30292     },
30293     
30294     prepare : function(file)
30295     {   
30296         if(this.loadMask){
30297             this.maskEl.mask(this.loadingText);
30298         }
30299         
30300         this.file = false;
30301         this.exif = {};
30302         
30303         if(typeof(file) === 'string'){
30304             this.loadCanvas(file);
30305             return;
30306         }
30307         
30308         if(!file || !this.urlAPI){
30309             return;
30310         }
30311         
30312         this.file = file;
30313         this.cropType = file.type;
30314         
30315         var _this = this;
30316         
30317         if(this.fireEvent('prepare', this, this.file) != false){
30318             
30319             var reader = new FileReader();
30320             
30321             reader.onload = function (e) {
30322                 if (e.target.error) {
30323                     Roo.log(e.target.error);
30324                     return;
30325                 }
30326                 
30327                 var buffer = e.target.result,
30328                     dataView = new DataView(buffer),
30329                     offset = 2,
30330                     maxOffset = dataView.byteLength - 4,
30331                     markerBytes,
30332                     markerLength;
30333                 
30334                 if (dataView.getUint16(0) === 0xffd8) {
30335                     while (offset < maxOffset) {
30336                         markerBytes = dataView.getUint16(offset);
30337                         
30338                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30339                             markerLength = dataView.getUint16(offset + 2) + 2;
30340                             if (offset + markerLength > dataView.byteLength) {
30341                                 Roo.log('Invalid meta data: Invalid segment size.');
30342                                 break;
30343                             }
30344                             
30345                             if(markerBytes == 0xffe1){
30346                                 _this.parseExifData(
30347                                     dataView,
30348                                     offset,
30349                                     markerLength
30350                                 );
30351                             }
30352                             
30353                             offset += markerLength;
30354                             
30355                             continue;
30356                         }
30357                         
30358                         break;
30359                     }
30360                     
30361                 }
30362                 
30363                 var url = _this.urlAPI.createObjectURL(_this.file);
30364                 
30365                 _this.loadCanvas(url);
30366                 
30367                 return;
30368             }
30369             
30370             reader.readAsArrayBuffer(this.file);
30371             
30372         }
30373         
30374     },
30375     
30376     parseExifData : function(dataView, offset, length)
30377     {
30378         var tiffOffset = offset + 10,
30379             littleEndian,
30380             dirOffset;
30381     
30382         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30383             // No Exif data, might be XMP data instead
30384             return;
30385         }
30386         
30387         // Check for the ASCII code for "Exif" (0x45786966):
30388         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30389             // No Exif data, might be XMP data instead
30390             return;
30391         }
30392         if (tiffOffset + 8 > dataView.byteLength) {
30393             Roo.log('Invalid Exif data: Invalid segment size.');
30394             return;
30395         }
30396         // Check for the two null bytes:
30397         if (dataView.getUint16(offset + 8) !== 0x0000) {
30398             Roo.log('Invalid Exif data: Missing byte alignment offset.');
30399             return;
30400         }
30401         // Check the byte alignment:
30402         switch (dataView.getUint16(tiffOffset)) {
30403         case 0x4949:
30404             littleEndian = true;
30405             break;
30406         case 0x4D4D:
30407             littleEndian = false;
30408             break;
30409         default:
30410             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
30411             return;
30412         }
30413         // Check for the TIFF tag marker (0x002A):
30414         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
30415             Roo.log('Invalid Exif data: Missing TIFF marker.');
30416             return;
30417         }
30418         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
30419         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
30420         
30421         this.parseExifTags(
30422             dataView,
30423             tiffOffset,
30424             tiffOffset + dirOffset,
30425             littleEndian
30426         );
30427     },
30428     
30429     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
30430     {
30431         var tagsNumber,
30432             dirEndOffset,
30433             i;
30434         if (dirOffset + 6 > dataView.byteLength) {
30435             Roo.log('Invalid Exif data: Invalid directory offset.');
30436             return;
30437         }
30438         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
30439         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
30440         if (dirEndOffset + 4 > dataView.byteLength) {
30441             Roo.log('Invalid Exif data: Invalid directory size.');
30442             return;
30443         }
30444         for (i = 0; i < tagsNumber; i += 1) {
30445             this.parseExifTag(
30446                 dataView,
30447                 tiffOffset,
30448                 dirOffset + 2 + 12 * i, // tag offset
30449                 littleEndian
30450             );
30451         }
30452         // Return the offset to the next directory:
30453         return dataView.getUint32(dirEndOffset, littleEndian);
30454     },
30455     
30456     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
30457     {
30458         var tag = dataView.getUint16(offset, littleEndian);
30459         
30460         this.exif[tag] = this.getExifValue(
30461             dataView,
30462             tiffOffset,
30463             offset,
30464             dataView.getUint16(offset + 2, littleEndian), // tag type
30465             dataView.getUint32(offset + 4, littleEndian), // tag length
30466             littleEndian
30467         );
30468     },
30469     
30470     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
30471     {
30472         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
30473             tagSize,
30474             dataOffset,
30475             values,
30476             i,
30477             str,
30478             c;
30479     
30480         if (!tagType) {
30481             Roo.log('Invalid Exif data: Invalid tag type.');
30482             return;
30483         }
30484         
30485         tagSize = tagType.size * length;
30486         // Determine if the value is contained in the dataOffset bytes,
30487         // or if the value at the dataOffset is a pointer to the actual data:
30488         dataOffset = tagSize > 4 ?
30489                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
30490         if (dataOffset + tagSize > dataView.byteLength) {
30491             Roo.log('Invalid Exif data: Invalid data offset.');
30492             return;
30493         }
30494         if (length === 1) {
30495             return tagType.getValue(dataView, dataOffset, littleEndian);
30496         }
30497         values = [];
30498         for (i = 0; i < length; i += 1) {
30499             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
30500         }
30501         
30502         if (tagType.ascii) {
30503             str = '';
30504             // Concatenate the chars:
30505             for (i = 0; i < values.length; i += 1) {
30506                 c = values[i];
30507                 // Ignore the terminating NULL byte(s):
30508                 if (c === '\u0000') {
30509                     break;
30510                 }
30511                 str += c;
30512             }
30513             return str;
30514         }
30515         return values;
30516     }
30517     
30518 });
30519
30520 Roo.apply(Roo.bootstrap.UploadCropbox, {
30521     tags : {
30522         'Orientation': 0x0112
30523     },
30524     
30525     Orientation: {
30526             1: 0, //'top-left',
30527 //            2: 'top-right',
30528             3: 180, //'bottom-right',
30529 //            4: 'bottom-left',
30530 //            5: 'left-top',
30531             6: 90, //'right-top',
30532 //            7: 'right-bottom',
30533             8: 270 //'left-bottom'
30534     },
30535     
30536     exifTagTypes : {
30537         // byte, 8-bit unsigned int:
30538         1: {
30539             getValue: function (dataView, dataOffset) {
30540                 return dataView.getUint8(dataOffset);
30541             },
30542             size: 1
30543         },
30544         // ascii, 8-bit byte:
30545         2: {
30546             getValue: function (dataView, dataOffset) {
30547                 return String.fromCharCode(dataView.getUint8(dataOffset));
30548             },
30549             size: 1,
30550             ascii: true
30551         },
30552         // short, 16 bit int:
30553         3: {
30554             getValue: function (dataView, dataOffset, littleEndian) {
30555                 return dataView.getUint16(dataOffset, littleEndian);
30556             },
30557             size: 2
30558         },
30559         // long, 32 bit int:
30560         4: {
30561             getValue: function (dataView, dataOffset, littleEndian) {
30562                 return dataView.getUint32(dataOffset, littleEndian);
30563             },
30564             size: 4
30565         },
30566         // rational = two long values, first is numerator, second is denominator:
30567         5: {
30568             getValue: function (dataView, dataOffset, littleEndian) {
30569                 return dataView.getUint32(dataOffset, littleEndian) /
30570                     dataView.getUint32(dataOffset + 4, littleEndian);
30571             },
30572             size: 8
30573         },
30574         // slong, 32 bit signed int:
30575         9: {
30576             getValue: function (dataView, dataOffset, littleEndian) {
30577                 return dataView.getInt32(dataOffset, littleEndian);
30578             },
30579             size: 4
30580         },
30581         // srational, two slongs, first is numerator, second is denominator:
30582         10: {
30583             getValue: function (dataView, dataOffset, littleEndian) {
30584                 return dataView.getInt32(dataOffset, littleEndian) /
30585                     dataView.getInt32(dataOffset + 4, littleEndian);
30586             },
30587             size: 8
30588         }
30589     },
30590     
30591     footer : {
30592         STANDARD : [
30593             {
30594                 tag : 'div',
30595                 cls : 'btn-group roo-upload-cropbox-rotate-left',
30596                 action : 'rotate-left',
30597                 cn : [
30598                     {
30599                         tag : 'button',
30600                         cls : 'btn btn-default',
30601                         html : '<i class="fa fa-undo"></i>'
30602                     }
30603                 ]
30604             },
30605             {
30606                 tag : 'div',
30607                 cls : 'btn-group roo-upload-cropbox-picture',
30608                 action : 'picture',
30609                 cn : [
30610                     {
30611                         tag : 'button',
30612                         cls : 'btn btn-default',
30613                         html : '<i class="fa fa-picture-o"></i>'
30614                     }
30615                 ]
30616             },
30617             {
30618                 tag : 'div',
30619                 cls : 'btn-group roo-upload-cropbox-rotate-right',
30620                 action : 'rotate-right',
30621                 cn : [
30622                     {
30623                         tag : 'button',
30624                         cls : 'btn btn-default',
30625                         html : '<i class="fa fa-repeat"></i>'
30626                     }
30627                 ]
30628             }
30629         ],
30630         DOCUMENT : [
30631             {
30632                 tag : 'div',
30633                 cls : 'btn-group roo-upload-cropbox-rotate-left',
30634                 action : 'rotate-left',
30635                 cn : [
30636                     {
30637                         tag : 'button',
30638                         cls : 'btn btn-default',
30639                         html : '<i class="fa fa-undo"></i>'
30640                     }
30641                 ]
30642             },
30643             {
30644                 tag : 'div',
30645                 cls : 'btn-group roo-upload-cropbox-download',
30646                 action : 'download',
30647                 cn : [
30648                     {
30649                         tag : 'button',
30650                         cls : 'btn btn-default',
30651                         html : '<i class="fa fa-download"></i>'
30652                     }
30653                 ]
30654             },
30655             {
30656                 tag : 'div',
30657                 cls : 'btn-group roo-upload-cropbox-crop',
30658                 action : 'crop',
30659                 cn : [
30660                     {
30661                         tag : 'button',
30662                         cls : 'btn btn-default',
30663                         html : '<i class="fa fa-crop"></i>'
30664                     }
30665                 ]
30666             },
30667             {
30668                 tag : 'div',
30669                 cls : 'btn-group roo-upload-cropbox-trash',
30670                 action : 'trash',
30671                 cn : [
30672                     {
30673                         tag : 'button',
30674                         cls : 'btn btn-default',
30675                         html : '<i class="fa fa-trash"></i>'
30676                     }
30677                 ]
30678             },
30679             {
30680                 tag : 'div',
30681                 cls : 'btn-group roo-upload-cropbox-rotate-right',
30682                 action : 'rotate-right',
30683                 cn : [
30684                     {
30685                         tag : 'button',
30686                         cls : 'btn btn-default',
30687                         html : '<i class="fa fa-repeat"></i>'
30688                     }
30689                 ]
30690             }
30691         ],
30692         ROTATOR : [
30693             {
30694                 tag : 'div',
30695                 cls : 'btn-group roo-upload-cropbox-rotate-left',
30696                 action : 'rotate-left',
30697                 cn : [
30698                     {
30699                         tag : 'button',
30700                         cls : 'btn btn-default',
30701                         html : '<i class="fa fa-undo"></i>'
30702                     }
30703                 ]
30704             },
30705             {
30706                 tag : 'div',
30707                 cls : 'btn-group roo-upload-cropbox-rotate-right',
30708                 action : 'rotate-right',
30709                 cn : [
30710                     {
30711                         tag : 'button',
30712                         cls : 'btn btn-default',
30713                         html : '<i class="fa fa-repeat"></i>'
30714                     }
30715                 ]
30716             }
30717         ]
30718     }
30719 });
30720
30721 /*
30722 * Licence: LGPL
30723 */
30724
30725 /**
30726  * @class Roo.bootstrap.DocumentManager
30727  * @extends Roo.bootstrap.Component
30728  * Bootstrap DocumentManager class
30729  * @cfg {String} paramName default 'imageUpload'
30730  * @cfg {String} toolTipName default 'filename'
30731  * @cfg {String} method default POST
30732  * @cfg {String} url action url
30733  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
30734  * @cfg {Boolean} multiple multiple upload default true
30735  * @cfg {Number} thumbSize default 300
30736  * @cfg {String} fieldLabel
30737  * @cfg {Number} labelWidth default 4
30738  * @cfg {String} labelAlign (left|top) default left
30739  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
30740 * @cfg {Number} labellg set the width of label (1-12)
30741  * @cfg {Number} labelmd set the width of label (1-12)
30742  * @cfg {Number} labelsm set the width of label (1-12)
30743  * @cfg {Number} labelxs set the width of label (1-12)
30744  * 
30745  * @constructor
30746  * Create a new DocumentManager
30747  * @param {Object} config The config object
30748  */
30749
30750 Roo.bootstrap.DocumentManager = function(config){
30751     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
30752     
30753     this.files = [];
30754     this.delegates = [];
30755     
30756     this.addEvents({
30757         /**
30758          * @event initial
30759          * Fire when initial the DocumentManager
30760          * @param {Roo.bootstrap.DocumentManager} this
30761          */
30762         "initial" : true,
30763         /**
30764          * @event inspect
30765          * inspect selected file
30766          * @param {Roo.bootstrap.DocumentManager} this
30767          * @param {File} file
30768          */
30769         "inspect" : true,
30770         /**
30771          * @event exception
30772          * Fire when xhr load exception
30773          * @param {Roo.bootstrap.DocumentManager} this
30774          * @param {XMLHttpRequest} xhr
30775          */
30776         "exception" : true,
30777         /**
30778          * @event afterupload
30779          * Fire when xhr load exception
30780          * @param {Roo.bootstrap.DocumentManager} this
30781          * @param {XMLHttpRequest} xhr
30782          */
30783         "afterupload" : true,
30784         /**
30785          * @event prepare
30786          * prepare the form data
30787          * @param {Roo.bootstrap.DocumentManager} this
30788          * @param {Object} formData
30789          */
30790         "prepare" : true,
30791         /**
30792          * @event remove
30793          * Fire when remove the file
30794          * @param {Roo.bootstrap.DocumentManager} this
30795          * @param {Object} file
30796          */
30797         "remove" : true,
30798         /**
30799          * @event refresh
30800          * Fire after refresh the file
30801          * @param {Roo.bootstrap.DocumentManager} this
30802          */
30803         "refresh" : true,
30804         /**
30805          * @event click
30806          * Fire after click the image
30807          * @param {Roo.bootstrap.DocumentManager} this
30808          * @param {Object} file
30809          */
30810         "click" : true,
30811         /**
30812          * @event edit
30813          * Fire when upload a image and editable set to true
30814          * @param {Roo.bootstrap.DocumentManager} this
30815          * @param {Object} file
30816          */
30817         "edit" : true,
30818         /**
30819          * @event beforeselectfile
30820          * Fire before select file
30821          * @param {Roo.bootstrap.DocumentManager} this
30822          */
30823         "beforeselectfile" : true,
30824         /**
30825          * @event process
30826          * Fire before process file
30827          * @param {Roo.bootstrap.DocumentManager} this
30828          * @param {Object} file
30829          */
30830         "process" : true,
30831         /**
30832          * @event previewrendered
30833          * Fire when preview rendered
30834          * @param {Roo.bootstrap.DocumentManager} this
30835          * @param {Object} file
30836          */
30837         "previewrendered" : true,
30838         /**
30839          */
30840         "previewResize" : true
30841         
30842     });
30843 };
30844
30845 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
30846     
30847     boxes : 0,
30848     inputName : '',
30849     thumbSize : 300,
30850     multiple : true,
30851     files : false,
30852     method : 'POST',
30853     url : '',
30854     paramName : 'imageUpload',
30855     toolTipName : 'filename',
30856     fieldLabel : '',
30857     labelWidth : 4,
30858     labelAlign : 'left',
30859     editable : true,
30860     delegates : false,
30861     xhr : false, 
30862     
30863     labellg : 0,
30864     labelmd : 0,
30865     labelsm : 0,
30866     labelxs : 0,
30867     
30868     getAutoCreate : function()
30869     {   
30870         var managerWidget = {
30871             tag : 'div',
30872             cls : 'roo-document-manager',
30873             cn : [
30874                 {
30875                     tag : 'input',
30876                     cls : 'roo-document-manager-selector',
30877                     type : 'file'
30878                 },
30879                 {
30880                     tag : 'div',
30881                     cls : 'roo-document-manager-uploader',
30882                     cn : [
30883                         {
30884                             tag : 'div',
30885                             cls : 'roo-document-manager-upload-btn',
30886                             html : '<i class="fa fa-plus"></i>'
30887                         }
30888                     ]
30889                     
30890                 }
30891             ]
30892         };
30893         
30894         var content = [
30895             {
30896                 tag : 'div',
30897                 cls : 'column col-md-12',
30898                 cn : managerWidget
30899             }
30900         ];
30901         
30902         if(this.fieldLabel.length){
30903             
30904             content = [
30905                 {
30906                     tag : 'div',
30907                     cls : 'column col-md-12',
30908                     html : this.fieldLabel
30909                 },
30910                 {
30911                     tag : 'div',
30912                     cls : 'column col-md-12',
30913                     cn : managerWidget
30914                 }
30915             ];
30916
30917             if(this.labelAlign == 'left'){
30918                 content = [
30919                     {
30920                         tag : 'div',
30921                         cls : 'column',
30922                         html : this.fieldLabel
30923                     },
30924                     {
30925                         tag : 'div',
30926                         cls : 'column',
30927                         cn : managerWidget
30928                     }
30929                 ];
30930                 
30931                 if(this.labelWidth > 12){
30932                     content[0].style = "width: " + this.labelWidth + 'px';
30933                 }
30934
30935                 if(this.labelWidth < 13 && this.labelmd == 0){
30936                     this.labelmd = this.labelWidth;
30937                 }
30938
30939                 if(this.labellg > 0){
30940                     content[0].cls += ' col-lg-' + this.labellg;
30941                     content[1].cls += ' col-lg-' + (12 - this.labellg);
30942                 }
30943
30944                 if(this.labelmd > 0){
30945                     content[0].cls += ' col-md-' + this.labelmd;
30946                     content[1].cls += ' col-md-' + (12 - this.labelmd);
30947                 }
30948
30949                 if(this.labelsm > 0){
30950                     content[0].cls += ' col-sm-' + this.labelsm;
30951                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
30952                 }
30953
30954                 if(this.labelxs > 0){
30955                     content[0].cls += ' col-xs-' + this.labelxs;
30956                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
30957                 }
30958                 
30959             }
30960         }
30961         
30962         var cfg = {
30963             tag : 'div',
30964             cls : 'row clearfix',
30965             cn : content
30966         };
30967         
30968         return cfg;
30969         
30970     },
30971     
30972     initEvents : function()
30973     {
30974         this.managerEl = this.el.select('.roo-document-manager', true).first();
30975         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30976         
30977         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
30978         this.selectorEl.hide();
30979         
30980         if(this.multiple){
30981             this.selectorEl.attr('multiple', 'multiple');
30982         }
30983         
30984         this.selectorEl.on('change', this.onFileSelected, this);
30985         
30986         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
30987         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30988         
30989         this.uploader.on('click', this.onUploaderClick, this);
30990         
30991         this.renderProgressDialog();
30992         
30993         var _this = this;
30994         
30995         window.addEventListener("resize", function() { _this.refresh(); } );
30996         
30997         this.fireEvent('initial', this);
30998     },
30999     
31000     renderProgressDialog : function()
31001     {
31002         var _this = this;
31003         
31004         this.progressDialog = new Roo.bootstrap.Modal({
31005             cls : 'roo-document-manager-progress-dialog',
31006             allow_close : false,
31007             animate : false,
31008             title : '',
31009             buttons : [
31010                 {
31011                     name  :'cancel',
31012                     weight : 'danger',
31013                     html : 'Cancel'
31014                 }
31015             ], 
31016             listeners : { 
31017                 btnclick : function() {
31018                     _this.uploadCancel();
31019                     this.hide();
31020                 }
31021             }
31022         });
31023          
31024         this.progressDialog.render(Roo.get(document.body));
31025          
31026         this.progress = new Roo.bootstrap.Progress({
31027             cls : 'roo-document-manager-progress',
31028             active : true,
31029             striped : true
31030         });
31031         
31032         this.progress.render(this.progressDialog.getChildContainer());
31033         
31034         this.progressBar = new Roo.bootstrap.ProgressBar({
31035             cls : 'roo-document-manager-progress-bar',
31036             aria_valuenow : 0,
31037             aria_valuemin : 0,
31038             aria_valuemax : 12,
31039             panel : 'success'
31040         });
31041         
31042         this.progressBar.render(this.progress.getChildContainer());
31043     },
31044     
31045     onUploaderClick : function(e)
31046     {
31047         e.preventDefault();
31048      
31049         if(this.fireEvent('beforeselectfile', this) != false){
31050             this.selectorEl.dom.click();
31051         }
31052         
31053     },
31054     
31055     onFileSelected : function(e)
31056     {
31057         e.preventDefault();
31058         
31059         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31060             return;
31061         }
31062         
31063         Roo.each(this.selectorEl.dom.files, function(file){
31064             if(this.fireEvent('inspect', this, file) != false){
31065                 this.files.push(file);
31066             }
31067         }, this);
31068         
31069         this.queue();
31070         
31071     },
31072     
31073     queue : function()
31074     {
31075         this.selectorEl.dom.value = '';
31076         
31077         if(!this.files || !this.files.length){
31078             return;
31079         }
31080         
31081         if(this.boxes > 0 && this.files.length > this.boxes){
31082             this.files = this.files.slice(0, this.boxes);
31083         }
31084         
31085         this.uploader.show();
31086         
31087         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31088             this.uploader.hide();
31089         }
31090         
31091         var _this = this;
31092         
31093         var files = [];
31094         
31095         var docs = [];
31096         
31097         Roo.each(this.files, function(file){
31098             
31099             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31100                 var f = this.renderPreview(file);
31101                 files.push(f);
31102                 return;
31103             }
31104             
31105             if(file.type.indexOf('image') != -1){
31106                 this.delegates.push(
31107                     (function(){
31108                         _this.process(file);
31109                     }).createDelegate(this)
31110                 );
31111         
31112                 return;
31113             }
31114             
31115             docs.push(
31116                 (function(){
31117                     _this.process(file);
31118                 }).createDelegate(this)
31119             );
31120             
31121         }, this);
31122         
31123         this.files = files;
31124         
31125         this.delegates = this.delegates.concat(docs);
31126         
31127         if(!this.delegates.length){
31128             this.refresh();
31129             return;
31130         }
31131         
31132         this.progressBar.aria_valuemax = this.delegates.length;
31133         
31134         this.arrange();
31135         
31136         return;
31137     },
31138     
31139     arrange : function()
31140     {
31141         if(!this.delegates.length){
31142             this.progressDialog.hide();
31143             this.refresh();
31144             return;
31145         }
31146         
31147         var delegate = this.delegates.shift();
31148         
31149         this.progressDialog.show();
31150         
31151         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31152         
31153         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31154         
31155         delegate();
31156     },
31157     
31158     refresh : function()
31159     {
31160         this.uploader.show();
31161         
31162         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31163             this.uploader.hide();
31164         }
31165         
31166         Roo.isTouch ? this.closable(false) : this.closable(true);
31167         
31168         this.fireEvent('refresh', this);
31169     },
31170     
31171     onRemove : function(e, el, o)
31172     {
31173         e.preventDefault();
31174         
31175         this.fireEvent('remove', this, o);
31176         
31177     },
31178     
31179     remove : function(o)
31180     {
31181         var files = [];
31182         
31183         Roo.each(this.files, function(file){
31184             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31185                 files.push(file);
31186                 return;
31187             }
31188
31189             o.target.remove();
31190
31191         }, this);
31192         
31193         this.files = files;
31194         
31195         this.refresh();
31196     },
31197     
31198     clear : function()
31199     {
31200         Roo.each(this.files, function(file){
31201             if(!file.target){
31202                 return;
31203             }
31204             
31205             file.target.remove();
31206
31207         }, this);
31208         
31209         this.files = [];
31210         
31211         this.refresh();
31212     },
31213     
31214     onClick : function(e, el, o)
31215     {
31216         e.preventDefault();
31217         
31218         this.fireEvent('click', this, o);
31219         
31220     },
31221     
31222     closable : function(closable)
31223     {
31224         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31225             
31226             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31227             
31228             if(closable){
31229                 el.show();
31230                 return;
31231             }
31232             
31233             el.hide();
31234             
31235         }, this);
31236     },
31237     
31238     xhrOnLoad : function(xhr)
31239     {
31240         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31241             el.remove();
31242         }, this);
31243         
31244         if (xhr.readyState !== 4) {
31245             this.arrange();
31246             this.fireEvent('exception', this, xhr);
31247             return;
31248         }
31249
31250         var response = Roo.decode(xhr.responseText);
31251         
31252         if(!response.success){
31253             this.arrange();
31254             this.fireEvent('exception', this, xhr);
31255             return;
31256         }
31257         
31258         var file = this.renderPreview(response.data);
31259         
31260         this.files.push(file);
31261         
31262         this.arrange();
31263         
31264         this.fireEvent('afterupload', this, xhr);
31265         
31266     },
31267     
31268     xhrOnError : function(xhr)
31269     {
31270         Roo.log('xhr on error');
31271         
31272         var response = Roo.decode(xhr.responseText);
31273           
31274         Roo.log(response);
31275         
31276         this.arrange();
31277     },
31278     
31279     process : function(file)
31280     {
31281         if(this.fireEvent('process', this, file) !== false){
31282             if(this.editable && file.type.indexOf('image') != -1){
31283                 this.fireEvent('edit', this, file);
31284                 return;
31285             }
31286
31287             this.uploadStart(file, false);
31288
31289             return;
31290         }
31291         
31292     },
31293     
31294     uploadStart : function(file, crop)
31295     {
31296         this.xhr = new XMLHttpRequest();
31297         
31298         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31299             this.arrange();
31300             return;
31301         }
31302         
31303         file.xhr = this.xhr;
31304             
31305         this.managerEl.createChild({
31306             tag : 'div',
31307             cls : 'roo-document-manager-loading',
31308             cn : [
31309                 {
31310                     tag : 'div',
31311                     tooltip : file.name,
31312                     cls : 'roo-document-manager-thumb',
31313                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31314                 }
31315             ]
31316
31317         });
31318
31319         this.xhr.open(this.method, this.url, true);
31320         
31321         var headers = {
31322             "Accept": "application/json",
31323             "Cache-Control": "no-cache",
31324             "X-Requested-With": "XMLHttpRequest"
31325         };
31326         
31327         for (var headerName in headers) {
31328             var headerValue = headers[headerName];
31329             if (headerValue) {
31330                 this.xhr.setRequestHeader(headerName, headerValue);
31331             }
31332         }
31333         
31334         var _this = this;
31335         
31336         this.xhr.onload = function()
31337         {
31338             _this.xhrOnLoad(_this.xhr);
31339         }
31340         
31341         this.xhr.onerror = function()
31342         {
31343             _this.xhrOnError(_this.xhr);
31344         }
31345         
31346         var formData = new FormData();
31347
31348         formData.append('returnHTML', 'NO');
31349         
31350         if(crop){
31351             formData.append('crop', crop);
31352         }
31353         
31354         formData.append(this.paramName, file, file.name);
31355         
31356         var options = {
31357             file : file, 
31358             manually : false
31359         };
31360         
31361         if(this.fireEvent('prepare', this, formData, options) != false){
31362             
31363             if(options.manually){
31364                 return;
31365             }
31366             
31367             this.xhr.send(formData);
31368             return;
31369         };
31370         
31371         this.uploadCancel();
31372     },
31373     
31374     uploadCancel : function()
31375     {
31376         if (this.xhr) {
31377             this.xhr.abort();
31378         }
31379         
31380         this.delegates = [];
31381         
31382         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31383             el.remove();
31384         }, this);
31385         
31386         this.arrange();
31387     },
31388     
31389     renderPreview : function(file)
31390     {
31391         if(typeof(file.target) != 'undefined' && file.target){
31392             return file;
31393         }
31394         
31395         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
31396         
31397         var previewEl = this.managerEl.createChild({
31398             tag : 'div',
31399             cls : 'roo-document-manager-preview',
31400             cn : [
31401                 {
31402                     tag : 'div',
31403                     tooltip : file[this.toolTipName],
31404                     cls : 'roo-document-manager-thumb',
31405                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
31406                 },
31407                 {
31408                     tag : 'button',
31409                     cls : 'close',
31410                     html : '<i class="fa fa-times-circle"></i>'
31411                 }
31412             ]
31413         });
31414
31415         var close = previewEl.select('button.close', true).first();
31416
31417         close.on('click', this.onRemove, this, file);
31418
31419         file.target = previewEl;
31420
31421         var image = previewEl.select('img', true).first();
31422         
31423         var _this = this;
31424         
31425         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
31426         
31427         image.on('click', this.onClick, this, file);
31428         
31429         this.fireEvent('previewrendered', this, file);
31430         
31431         return file;
31432         
31433     },
31434     
31435     onPreviewLoad : function(file, image)
31436     {
31437         if(typeof(file.target) == 'undefined' || !file.target){
31438             return;
31439         }
31440         
31441         var width = image.dom.naturalWidth || image.dom.width;
31442         var height = image.dom.naturalHeight || image.dom.height;
31443         
31444         if(!this.previewResize) {
31445             return;
31446         }
31447         
31448         if(width > height){
31449             file.target.addClass('wide');
31450             return;
31451         }
31452         
31453         file.target.addClass('tall');
31454         return;
31455         
31456     },
31457     
31458     uploadFromSource : function(file, crop)
31459     {
31460         this.xhr = new XMLHttpRequest();
31461         
31462         this.managerEl.createChild({
31463             tag : 'div',
31464             cls : 'roo-document-manager-loading',
31465             cn : [
31466                 {
31467                     tag : 'div',
31468                     tooltip : file.name,
31469                     cls : 'roo-document-manager-thumb',
31470                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31471                 }
31472             ]
31473
31474         });
31475
31476         this.xhr.open(this.method, this.url, true);
31477         
31478         var headers = {
31479             "Accept": "application/json",
31480             "Cache-Control": "no-cache",
31481             "X-Requested-With": "XMLHttpRequest"
31482         };
31483         
31484         for (var headerName in headers) {
31485             var headerValue = headers[headerName];
31486             if (headerValue) {
31487                 this.xhr.setRequestHeader(headerName, headerValue);
31488             }
31489         }
31490         
31491         var _this = this;
31492         
31493         this.xhr.onload = function()
31494         {
31495             _this.xhrOnLoad(_this.xhr);
31496         }
31497         
31498         this.xhr.onerror = function()
31499         {
31500             _this.xhrOnError(_this.xhr);
31501         }
31502         
31503         var formData = new FormData();
31504
31505         formData.append('returnHTML', 'NO');
31506         
31507         formData.append('crop', crop);
31508         
31509         if(typeof(file.filename) != 'undefined'){
31510             formData.append('filename', file.filename);
31511         }
31512         
31513         if(typeof(file.mimetype) != 'undefined'){
31514             formData.append('mimetype', file.mimetype);
31515         }
31516         
31517         Roo.log(formData);
31518         
31519         if(this.fireEvent('prepare', this, formData) != false){
31520             this.xhr.send(formData);
31521         };
31522     }
31523 });
31524
31525 /*
31526 * Licence: LGPL
31527 */
31528
31529 /**
31530  * @class Roo.bootstrap.DocumentViewer
31531  * @extends Roo.bootstrap.Component
31532  * Bootstrap DocumentViewer class
31533  * @cfg {Boolean} showDownload (true|false) show download button (default true)
31534  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
31535  * 
31536  * @constructor
31537  * Create a new DocumentViewer
31538  * @param {Object} config The config object
31539  */
31540
31541 Roo.bootstrap.DocumentViewer = function(config){
31542     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
31543     
31544     this.addEvents({
31545         /**
31546          * @event initial
31547          * Fire after initEvent
31548          * @param {Roo.bootstrap.DocumentViewer} this
31549          */
31550         "initial" : true,
31551         /**
31552          * @event click
31553          * Fire after click
31554          * @param {Roo.bootstrap.DocumentViewer} this
31555          */
31556         "click" : true,
31557         /**
31558          * @event download
31559          * Fire after download button
31560          * @param {Roo.bootstrap.DocumentViewer} this
31561          */
31562         "download" : true,
31563         /**
31564          * @event trash
31565          * Fire after trash button
31566          * @param {Roo.bootstrap.DocumentViewer} this
31567          */
31568         "trash" : true
31569         
31570     });
31571 };
31572
31573 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
31574     
31575     showDownload : true,
31576     
31577     showTrash : true,
31578     
31579     getAutoCreate : function()
31580     {
31581         var cfg = {
31582             tag : 'div',
31583             cls : 'roo-document-viewer',
31584             cn : [
31585                 {
31586                     tag : 'div',
31587                     cls : 'roo-document-viewer-body',
31588                     cn : [
31589                         {
31590                             tag : 'div',
31591                             cls : 'roo-document-viewer-thumb',
31592                             cn : [
31593                                 {
31594                                     tag : 'img',
31595                                     cls : 'roo-document-viewer-image'
31596                                 }
31597                             ]
31598                         }
31599                     ]
31600                 },
31601                 {
31602                     tag : 'div',
31603                     cls : 'roo-document-viewer-footer',
31604                     cn : {
31605                         tag : 'div',
31606                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
31607                         cn : [
31608                             {
31609                                 tag : 'div',
31610                                 cls : 'btn-group roo-document-viewer-download',
31611                                 cn : [
31612                                     {
31613                                         tag : 'button',
31614                                         cls : 'btn btn-default',
31615                                         html : '<i class="fa fa-download"></i>'
31616                                     }
31617                                 ]
31618                             },
31619                             {
31620                                 tag : 'div',
31621                                 cls : 'btn-group roo-document-viewer-trash',
31622                                 cn : [
31623                                     {
31624                                         tag : 'button',
31625                                         cls : 'btn btn-default',
31626                                         html : '<i class="fa fa-trash"></i>'
31627                                     }
31628                                 ]
31629                             }
31630                         ]
31631                     }
31632                 }
31633             ]
31634         };
31635         
31636         return cfg;
31637     },
31638     
31639     initEvents : function()
31640     {
31641         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
31642         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
31643         
31644         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
31645         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
31646         
31647         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
31648         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
31649         
31650         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
31651         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
31652         
31653         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
31654         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
31655         
31656         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
31657         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
31658         
31659         this.bodyEl.on('click', this.onClick, this);
31660         this.downloadBtn.on('click', this.onDownload, this);
31661         this.trashBtn.on('click', this.onTrash, this);
31662         
31663         this.downloadBtn.hide();
31664         this.trashBtn.hide();
31665         
31666         if(this.showDownload){
31667             this.downloadBtn.show();
31668         }
31669         
31670         if(this.showTrash){
31671             this.trashBtn.show();
31672         }
31673         
31674         if(!this.showDownload && !this.showTrash) {
31675             this.footerEl.hide();
31676         }
31677         
31678     },
31679     
31680     initial : function()
31681     {
31682         this.fireEvent('initial', this);
31683         
31684     },
31685     
31686     onClick : function(e)
31687     {
31688         e.preventDefault();
31689         
31690         this.fireEvent('click', this);
31691     },
31692     
31693     onDownload : function(e)
31694     {
31695         e.preventDefault();
31696         
31697         this.fireEvent('download', this);
31698     },
31699     
31700     onTrash : function(e)
31701     {
31702         e.preventDefault();
31703         
31704         this.fireEvent('trash', this);
31705     }
31706     
31707 });
31708 /*
31709  * - LGPL
31710  *
31711  * nav progress bar
31712  * 
31713  */
31714
31715 /**
31716  * @class Roo.bootstrap.NavProgressBar
31717  * @extends Roo.bootstrap.Component
31718  * Bootstrap NavProgressBar class
31719  * 
31720  * @constructor
31721  * Create a new nav progress bar
31722  * @param {Object} config The config object
31723  */
31724
31725 Roo.bootstrap.NavProgressBar = function(config){
31726     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
31727
31728     this.bullets = this.bullets || [];
31729    
31730 //    Roo.bootstrap.NavProgressBar.register(this);
31731      this.addEvents({
31732         /**
31733              * @event changed
31734              * Fires when the active item changes
31735              * @param {Roo.bootstrap.NavProgressBar} this
31736              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
31737              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
31738          */
31739         'changed': true
31740      });
31741     
31742 };
31743
31744 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
31745     
31746     bullets : [],
31747     barItems : [],
31748     
31749     getAutoCreate : function()
31750     {
31751         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
31752         
31753         cfg = {
31754             tag : 'div',
31755             cls : 'roo-navigation-bar-group',
31756             cn : [
31757                 {
31758                     tag : 'div',
31759                     cls : 'roo-navigation-top-bar'
31760                 },
31761                 {
31762                     tag : 'div',
31763                     cls : 'roo-navigation-bullets-bar',
31764                     cn : [
31765                         {
31766                             tag : 'ul',
31767                             cls : 'roo-navigation-bar'
31768                         }
31769                     ]
31770                 },
31771                 
31772                 {
31773                     tag : 'div',
31774                     cls : 'roo-navigation-bottom-bar'
31775                 }
31776             ]
31777             
31778         };
31779         
31780         return cfg;
31781         
31782     },
31783     
31784     initEvents: function() 
31785     {
31786         
31787     },
31788     
31789     onRender : function(ct, position) 
31790     {
31791         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
31792         
31793         if(this.bullets.length){
31794             Roo.each(this.bullets, function(b){
31795                this.addItem(b);
31796             }, this);
31797         }
31798         
31799         this.format();
31800         
31801     },
31802     
31803     addItem : function(cfg)
31804     {
31805         var item = new Roo.bootstrap.NavProgressItem(cfg);
31806         
31807         item.parentId = this.id;
31808         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
31809         
31810         if(cfg.html){
31811             var top = new Roo.bootstrap.Element({
31812                 tag : 'div',
31813                 cls : 'roo-navigation-bar-text'
31814             });
31815             
31816             var bottom = new Roo.bootstrap.Element({
31817                 tag : 'div',
31818                 cls : 'roo-navigation-bar-text'
31819             });
31820             
31821             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
31822             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
31823             
31824             var topText = new Roo.bootstrap.Element({
31825                 tag : 'span',
31826                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
31827             });
31828             
31829             var bottomText = new Roo.bootstrap.Element({
31830                 tag : 'span',
31831                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
31832             });
31833             
31834             topText.onRender(top.el, null);
31835             bottomText.onRender(bottom.el, null);
31836             
31837             item.topEl = top;
31838             item.bottomEl = bottom;
31839         }
31840         
31841         this.barItems.push(item);
31842         
31843         return item;
31844     },
31845     
31846     getActive : function()
31847     {
31848         var active = false;
31849         
31850         Roo.each(this.barItems, function(v){
31851             
31852             if (!v.isActive()) {
31853                 return;
31854             }
31855             
31856             active = v;
31857             return false;
31858             
31859         });
31860         
31861         return active;
31862     },
31863     
31864     setActiveItem : function(item)
31865     {
31866         var prev = false;
31867         
31868         Roo.each(this.barItems, function(v){
31869             if (v.rid == item.rid) {
31870                 return ;
31871             }
31872             
31873             if (v.isActive()) {
31874                 v.setActive(false);
31875                 prev = v;
31876             }
31877         });
31878
31879         item.setActive(true);
31880         
31881         this.fireEvent('changed', this, item, prev);
31882     },
31883     
31884     getBarItem: function(rid)
31885     {
31886         var ret = false;
31887         
31888         Roo.each(this.barItems, function(e) {
31889             if (e.rid != rid) {
31890                 return;
31891             }
31892             
31893             ret =  e;
31894             return false;
31895         });
31896         
31897         return ret;
31898     },
31899     
31900     indexOfItem : function(item)
31901     {
31902         var index = false;
31903         
31904         Roo.each(this.barItems, function(v, i){
31905             
31906             if (v.rid != item.rid) {
31907                 return;
31908             }
31909             
31910             index = i;
31911             return false
31912         });
31913         
31914         return index;
31915     },
31916     
31917     setActiveNext : function()
31918     {
31919         var i = this.indexOfItem(this.getActive());
31920         
31921         if (i > this.barItems.length) {
31922             return;
31923         }
31924         
31925         this.setActiveItem(this.barItems[i+1]);
31926     },
31927     
31928     setActivePrev : function()
31929     {
31930         var i = this.indexOfItem(this.getActive());
31931         
31932         if (i  < 1) {
31933             return;
31934         }
31935         
31936         this.setActiveItem(this.barItems[i-1]);
31937     },
31938     
31939     format : function()
31940     {
31941         if(!this.barItems.length){
31942             return;
31943         }
31944      
31945         var width = 100 / this.barItems.length;
31946         
31947         Roo.each(this.barItems, function(i){
31948             i.el.setStyle('width', width + '%');
31949             i.topEl.el.setStyle('width', width + '%');
31950             i.bottomEl.el.setStyle('width', width + '%');
31951         }, this);
31952         
31953     }
31954     
31955 });
31956 /*
31957  * - LGPL
31958  *
31959  * Nav Progress Item
31960  * 
31961  */
31962
31963 /**
31964  * @class Roo.bootstrap.NavProgressItem
31965  * @extends Roo.bootstrap.Component
31966  * Bootstrap NavProgressItem class
31967  * @cfg {String} rid the reference id
31968  * @cfg {Boolean} active (true|false) Is item active default false
31969  * @cfg {Boolean} disabled (true|false) Is item active default false
31970  * @cfg {String} html
31971  * @cfg {String} position (top|bottom) text position default bottom
31972  * @cfg {String} icon show icon instead of number
31973  * 
31974  * @constructor
31975  * Create a new NavProgressItem
31976  * @param {Object} config The config object
31977  */
31978 Roo.bootstrap.NavProgressItem = function(config){
31979     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
31980     this.addEvents({
31981         // raw events
31982         /**
31983          * @event click
31984          * The raw click event for the entire grid.
31985          * @param {Roo.bootstrap.NavProgressItem} this
31986          * @param {Roo.EventObject} e
31987          */
31988         "click" : true
31989     });
31990    
31991 };
31992
31993 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
31994     
31995     rid : '',
31996     active : false,
31997     disabled : false,
31998     html : '',
31999     position : 'bottom',
32000     icon : false,
32001     
32002     getAutoCreate : function()
32003     {
32004         var iconCls = 'roo-navigation-bar-item-icon';
32005         
32006         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32007         
32008         var cfg = {
32009             tag: 'li',
32010             cls: 'roo-navigation-bar-item',
32011             cn : [
32012                 {
32013                     tag : 'i',
32014                     cls : iconCls
32015                 }
32016             ]
32017         };
32018         
32019         if(this.active){
32020             cfg.cls += ' active';
32021         }
32022         if(this.disabled){
32023             cfg.cls += ' disabled';
32024         }
32025         
32026         return cfg;
32027     },
32028     
32029     disable : function()
32030     {
32031         this.setDisabled(true);
32032     },
32033     
32034     enable : function()
32035     {
32036         this.setDisabled(false);
32037     },
32038     
32039     initEvents: function() 
32040     {
32041         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32042         
32043         this.iconEl.on('click', this.onClick, this);
32044     },
32045     
32046     onClick : function(e)
32047     {
32048         e.preventDefault();
32049         
32050         if(this.disabled){
32051             return;
32052         }
32053         
32054         if(this.fireEvent('click', this, e) === false){
32055             return;
32056         };
32057         
32058         this.parent().setActiveItem(this);
32059     },
32060     
32061     isActive: function () 
32062     {
32063         return this.active;
32064     },
32065     
32066     setActive : function(state)
32067     {
32068         if(this.active == state){
32069             return;
32070         }
32071         
32072         this.active = state;
32073         
32074         if (state) {
32075             this.el.addClass('active');
32076             return;
32077         }
32078         
32079         this.el.removeClass('active');
32080         
32081         return;
32082     },
32083     
32084     setDisabled : function(state)
32085     {
32086         if(this.disabled == state){
32087             return;
32088         }
32089         
32090         this.disabled = state;
32091         
32092         if (state) {
32093             this.el.addClass('disabled');
32094             return;
32095         }
32096         
32097         this.el.removeClass('disabled');
32098     },
32099     
32100     tooltipEl : function()
32101     {
32102         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32103     }
32104 });
32105  
32106
32107  /*
32108  * - LGPL
32109  *
32110  * FieldLabel
32111  * 
32112  */
32113
32114 /**
32115  * @class Roo.bootstrap.FieldLabel
32116  * @extends Roo.bootstrap.Component
32117  * Bootstrap FieldLabel class
32118  * @cfg {String} html contents of the element
32119  * @cfg {String} tag tag of the element default label
32120  * @cfg {String} cls class of the element
32121  * @cfg {String} target label target 
32122  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32123  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32124  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32125  * @cfg {String} iconTooltip default "This field is required"
32126  * @cfg {String} indicatorpos (left|right) default left
32127  * 
32128  * @constructor
32129  * Create a new FieldLabel
32130  * @param {Object} config The config object
32131  */
32132
32133 Roo.bootstrap.FieldLabel = function(config){
32134     Roo.bootstrap.Element.superclass.constructor.call(this, config);
32135     
32136     this.addEvents({
32137             /**
32138              * @event invalid
32139              * Fires after the field has been marked as invalid.
32140              * @param {Roo.form.FieldLabel} this
32141              * @param {String} msg The validation message
32142              */
32143             invalid : true,
32144             /**
32145              * @event valid
32146              * Fires after the field has been validated with no errors.
32147              * @param {Roo.form.FieldLabel} this
32148              */
32149             valid : true
32150         });
32151 };
32152
32153 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
32154     
32155     tag: 'label',
32156     cls: '',
32157     html: '',
32158     target: '',
32159     allowBlank : true,
32160     invalidClass : 'has-warning',
32161     validClass : 'has-success',
32162     iconTooltip : 'This field is required',
32163     indicatorpos : 'left',
32164     
32165     getAutoCreate : function(){
32166         
32167         var cls = "";
32168         if (!this.allowBlank) {
32169             cls  = "visible";
32170         }
32171         
32172         var cfg = {
32173             tag : this.tag,
32174             cls : 'roo-bootstrap-field-label ' + this.cls,
32175             for : this.target,
32176             cn : [
32177                 {
32178                     tag : 'i',
32179                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32180                     tooltip : this.iconTooltip
32181                 },
32182                 {
32183                     tag : 'span',
32184                     html : this.html
32185                 }
32186             ] 
32187         };
32188         
32189         if(this.indicatorpos == 'right'){
32190             var cfg = {
32191                 tag : this.tag,
32192                 cls : 'roo-bootstrap-field-label ' + this.cls,
32193                 for : this.target,
32194                 cn : [
32195                     {
32196                         tag : 'span',
32197                         html : this.html
32198                     },
32199                     {
32200                         tag : 'i',
32201                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32202                         tooltip : this.iconTooltip
32203                     }
32204                 ] 
32205             };
32206         }
32207         
32208         return cfg;
32209     },
32210     
32211     initEvents: function() 
32212     {
32213         Roo.bootstrap.Element.superclass.initEvents.call(this);
32214         
32215         this.indicator = this.indicatorEl();
32216         
32217         if(this.indicator){
32218             this.indicator.removeClass('visible');
32219             this.indicator.addClass('invisible');
32220         }
32221         
32222         Roo.bootstrap.FieldLabel.register(this);
32223     },
32224     
32225     indicatorEl : function()
32226     {
32227         var indicator = this.el.select('i.roo-required-indicator',true).first();
32228         
32229         if(!indicator){
32230             return false;
32231         }
32232         
32233         return indicator;
32234         
32235     },
32236     
32237     /**
32238      * Mark this field as valid
32239      */
32240     markValid : function()
32241     {
32242         if(this.indicator){
32243             this.indicator.removeClass('visible');
32244             this.indicator.addClass('invisible');
32245         }
32246         if (Roo.bootstrap.version == 3) {
32247             this.el.removeClass(this.invalidClass);
32248             this.el.addClass(this.validClass);
32249         } else {
32250             this.el.removeClass('is-invalid');
32251             this.el.addClass('is-valid');
32252         }
32253         
32254         
32255         this.fireEvent('valid', this);
32256     },
32257     
32258     /**
32259      * Mark this field as invalid
32260      * @param {String} msg The validation message
32261      */
32262     markInvalid : function(msg)
32263     {
32264         if(this.indicator){
32265             this.indicator.removeClass('invisible');
32266             this.indicator.addClass('visible');
32267         }
32268           if (Roo.bootstrap.version == 3) {
32269             this.el.removeClass(this.validClass);
32270             this.el.addClass(this.invalidClass);
32271         } else {
32272             this.el.removeClass('is-valid');
32273             this.el.addClass('is-invalid');
32274         }
32275         
32276         
32277         this.fireEvent('invalid', this, msg);
32278     }
32279     
32280    
32281 });
32282
32283 Roo.apply(Roo.bootstrap.FieldLabel, {
32284     
32285     groups: {},
32286     
32287      /**
32288     * register a FieldLabel Group
32289     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32290     */
32291     register : function(label)
32292     {
32293         if(this.groups.hasOwnProperty(label.target)){
32294             return;
32295         }
32296      
32297         this.groups[label.target] = label;
32298         
32299     },
32300     /**
32301     * fetch a FieldLabel Group based on the target
32302     * @param {string} target
32303     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32304     */
32305     get: function(target) {
32306         if (typeof(this.groups[target]) == 'undefined') {
32307             return false;
32308         }
32309         
32310         return this.groups[target] ;
32311     }
32312 });
32313
32314  
32315
32316  /*
32317  * - LGPL
32318  *
32319  * page DateSplitField.
32320  * 
32321  */
32322
32323
32324 /**
32325  * @class Roo.bootstrap.DateSplitField
32326  * @extends Roo.bootstrap.Component
32327  * Bootstrap DateSplitField class
32328  * @cfg {string} fieldLabel - the label associated
32329  * @cfg {Number} labelWidth set the width of label (0-12)
32330  * @cfg {String} labelAlign (top|left)
32331  * @cfg {Boolean} dayAllowBlank (true|false) default false
32332  * @cfg {Boolean} monthAllowBlank (true|false) default false
32333  * @cfg {Boolean} yearAllowBlank (true|false) default false
32334  * @cfg {string} dayPlaceholder 
32335  * @cfg {string} monthPlaceholder
32336  * @cfg {string} yearPlaceholder
32337  * @cfg {string} dayFormat default 'd'
32338  * @cfg {string} monthFormat default 'm'
32339  * @cfg {string} yearFormat default 'Y'
32340  * @cfg {Number} labellg set the width of label (1-12)
32341  * @cfg {Number} labelmd set the width of label (1-12)
32342  * @cfg {Number} labelsm set the width of label (1-12)
32343  * @cfg {Number} labelxs set the width of label (1-12)
32344
32345  *     
32346  * @constructor
32347  * Create a new DateSplitField
32348  * @param {Object} config The config object
32349  */
32350
32351 Roo.bootstrap.DateSplitField = function(config){
32352     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32353     
32354     this.addEvents({
32355         // raw events
32356          /**
32357          * @event years
32358          * getting the data of years
32359          * @param {Roo.bootstrap.DateSplitField} this
32360          * @param {Object} years
32361          */
32362         "years" : true,
32363         /**
32364          * @event days
32365          * getting the data of days
32366          * @param {Roo.bootstrap.DateSplitField} this
32367          * @param {Object} days
32368          */
32369         "days" : true,
32370         /**
32371          * @event invalid
32372          * Fires after the field has been marked as invalid.
32373          * @param {Roo.form.Field} this
32374          * @param {String} msg The validation message
32375          */
32376         invalid : true,
32377        /**
32378          * @event valid
32379          * Fires after the field has been validated with no errors.
32380          * @param {Roo.form.Field} this
32381          */
32382         valid : true
32383     });
32384 };
32385
32386 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
32387     
32388     fieldLabel : '',
32389     labelAlign : 'top',
32390     labelWidth : 3,
32391     dayAllowBlank : false,
32392     monthAllowBlank : false,
32393     yearAllowBlank : false,
32394     dayPlaceholder : '',
32395     monthPlaceholder : '',
32396     yearPlaceholder : '',
32397     dayFormat : 'd',
32398     monthFormat : 'm',
32399     yearFormat : 'Y',
32400     isFormField : true,
32401     labellg : 0,
32402     labelmd : 0,
32403     labelsm : 0,
32404     labelxs : 0,
32405     
32406     getAutoCreate : function()
32407     {
32408         var cfg = {
32409             tag : 'div',
32410             cls : 'row roo-date-split-field-group',
32411             cn : [
32412                 {
32413                     tag : 'input',
32414                     type : 'hidden',
32415                     cls : 'form-hidden-field roo-date-split-field-group-value',
32416                     name : this.name
32417                 }
32418             ]
32419         };
32420         
32421         var labelCls = 'col-md-12';
32422         var contentCls = 'col-md-4';
32423         
32424         if(this.fieldLabel){
32425             
32426             var label = {
32427                 tag : 'div',
32428                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
32429                 cn : [
32430                     {
32431                         tag : 'label',
32432                         html : this.fieldLabel
32433                     }
32434                 ]
32435             };
32436             
32437             if(this.labelAlign == 'left'){
32438             
32439                 if(this.labelWidth > 12){
32440                     label.style = "width: " + this.labelWidth + 'px';
32441                 }
32442
32443                 if(this.labelWidth < 13 && this.labelmd == 0){
32444                     this.labelmd = this.labelWidth;
32445                 }
32446
32447                 if(this.labellg > 0){
32448                     labelCls = ' col-lg-' + this.labellg;
32449                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
32450                 }
32451
32452                 if(this.labelmd > 0){
32453                     labelCls = ' col-md-' + this.labelmd;
32454                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
32455                 }
32456
32457                 if(this.labelsm > 0){
32458                     labelCls = ' col-sm-' + this.labelsm;
32459                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
32460                 }
32461
32462                 if(this.labelxs > 0){
32463                     labelCls = ' col-xs-' + this.labelxs;
32464                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
32465                 }
32466             }
32467             
32468             label.cls += ' ' + labelCls;
32469             
32470             cfg.cn.push(label);
32471         }
32472         
32473         Roo.each(['day', 'month', 'year'], function(t){
32474             cfg.cn.push({
32475                 tag : 'div',
32476                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
32477             });
32478         }, this);
32479         
32480         return cfg;
32481     },
32482     
32483     inputEl: function ()
32484     {
32485         return this.el.select('.roo-date-split-field-group-value', true).first();
32486     },
32487     
32488     onRender : function(ct, position) 
32489     {
32490         var _this = this;
32491         
32492         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32493         
32494         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
32495         
32496         this.dayField = new Roo.bootstrap.ComboBox({
32497             allowBlank : this.dayAllowBlank,
32498             alwaysQuery : true,
32499             displayField : 'value',
32500             editable : false,
32501             fieldLabel : '',
32502             forceSelection : true,
32503             mode : 'local',
32504             placeholder : this.dayPlaceholder,
32505             selectOnFocus : true,
32506             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32507             triggerAction : 'all',
32508             typeAhead : true,
32509             valueField : 'value',
32510             store : new Roo.data.SimpleStore({
32511                 data : (function() {    
32512                     var days = [];
32513                     _this.fireEvent('days', _this, days);
32514                     return days;
32515                 })(),
32516                 fields : [ 'value' ]
32517             }),
32518             listeners : {
32519                 select : function (_self, record, index)
32520                 {
32521                     _this.setValue(_this.getValue());
32522                 }
32523             }
32524         });
32525
32526         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
32527         
32528         this.monthField = new Roo.bootstrap.MonthField({
32529             after : '<i class=\"fa fa-calendar\"></i>',
32530             allowBlank : this.monthAllowBlank,
32531             placeholder : this.monthPlaceholder,
32532             readOnly : true,
32533             listeners : {
32534                 render : function (_self)
32535                 {
32536                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
32537                         e.preventDefault();
32538                         _self.focus();
32539                     });
32540                 },
32541                 select : function (_self, oldvalue, newvalue)
32542                 {
32543                     _this.setValue(_this.getValue());
32544                 }
32545             }
32546         });
32547         
32548         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
32549         
32550         this.yearField = new Roo.bootstrap.ComboBox({
32551             allowBlank : this.yearAllowBlank,
32552             alwaysQuery : true,
32553             displayField : 'value',
32554             editable : false,
32555             fieldLabel : '',
32556             forceSelection : true,
32557             mode : 'local',
32558             placeholder : this.yearPlaceholder,
32559             selectOnFocus : true,
32560             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32561             triggerAction : 'all',
32562             typeAhead : true,
32563             valueField : 'value',
32564             store : new Roo.data.SimpleStore({
32565                 data : (function() {
32566                     var years = [];
32567                     _this.fireEvent('years', _this, years);
32568                     return years;
32569                 })(),
32570                 fields : [ 'value' ]
32571             }),
32572             listeners : {
32573                 select : function (_self, record, index)
32574                 {
32575                     _this.setValue(_this.getValue());
32576                 }
32577             }
32578         });
32579
32580         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
32581     },
32582     
32583     setValue : function(v, format)
32584     {
32585         this.inputEl.dom.value = v;
32586         
32587         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
32588         
32589         var d = Date.parseDate(v, f);
32590         
32591         if(!d){
32592             this.validate();
32593             return;
32594         }
32595         
32596         this.setDay(d.format(this.dayFormat));
32597         this.setMonth(d.format(this.monthFormat));
32598         this.setYear(d.format(this.yearFormat));
32599         
32600         this.validate();
32601         
32602         return;
32603     },
32604     
32605     setDay : function(v)
32606     {
32607         this.dayField.setValue(v);
32608         this.inputEl.dom.value = this.getValue();
32609         this.validate();
32610         return;
32611     },
32612     
32613     setMonth : function(v)
32614     {
32615         this.monthField.setValue(v, true);
32616         this.inputEl.dom.value = this.getValue();
32617         this.validate();
32618         return;
32619     },
32620     
32621     setYear : function(v)
32622     {
32623         this.yearField.setValue(v);
32624         this.inputEl.dom.value = this.getValue();
32625         this.validate();
32626         return;
32627     },
32628     
32629     getDay : function()
32630     {
32631         return this.dayField.getValue();
32632     },
32633     
32634     getMonth : function()
32635     {
32636         return this.monthField.getValue();
32637     },
32638     
32639     getYear : function()
32640     {
32641         return this.yearField.getValue();
32642     },
32643     
32644     getValue : function()
32645     {
32646         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
32647         
32648         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
32649         
32650         return date;
32651     },
32652     
32653     reset : function()
32654     {
32655         this.setDay('');
32656         this.setMonth('');
32657         this.setYear('');
32658         this.inputEl.dom.value = '';
32659         this.validate();
32660         return;
32661     },
32662     
32663     validate : function()
32664     {
32665         var d = this.dayField.validate();
32666         var m = this.monthField.validate();
32667         var y = this.yearField.validate();
32668         
32669         var valid = true;
32670         
32671         if(
32672                 (!this.dayAllowBlank && !d) ||
32673                 (!this.monthAllowBlank && !m) ||
32674                 (!this.yearAllowBlank && !y)
32675         ){
32676             valid = false;
32677         }
32678         
32679         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
32680             return valid;
32681         }
32682         
32683         if(valid){
32684             this.markValid();
32685             return valid;
32686         }
32687         
32688         this.markInvalid();
32689         
32690         return valid;
32691     },
32692     
32693     markValid : function()
32694     {
32695         
32696         var label = this.el.select('label', true).first();
32697         var icon = this.el.select('i.fa-star', true).first();
32698
32699         if(label && icon){
32700             icon.remove();
32701         }
32702         
32703         this.fireEvent('valid', this);
32704     },
32705     
32706      /**
32707      * Mark this field as invalid
32708      * @param {String} msg The validation message
32709      */
32710     markInvalid : function(msg)
32711     {
32712         
32713         var label = this.el.select('label', true).first();
32714         var icon = this.el.select('i.fa-star', true).first();
32715
32716         if(label && !icon){
32717             this.el.select('.roo-date-split-field-label', true).createChild({
32718                 tag : 'i',
32719                 cls : 'text-danger fa fa-lg fa-star',
32720                 tooltip : 'This field is required',
32721                 style : 'margin-right:5px;'
32722             }, label, true);
32723         }
32724         
32725         this.fireEvent('invalid', this, msg);
32726     },
32727     
32728     clearInvalid : function()
32729     {
32730         var label = this.el.select('label', true).first();
32731         var icon = this.el.select('i.fa-star', true).first();
32732
32733         if(label && icon){
32734             icon.remove();
32735         }
32736         
32737         this.fireEvent('valid', this);
32738     },
32739     
32740     getName: function()
32741     {
32742         return this.name;
32743     }
32744     
32745 });
32746
32747  /**
32748  *
32749  * This is based on 
32750  * http://masonry.desandro.com
32751  *
32752  * The idea is to render all the bricks based on vertical width...
32753  *
32754  * The original code extends 'outlayer' - we might need to use that....
32755  * 
32756  */
32757
32758
32759 /**
32760  * @class Roo.bootstrap.LayoutMasonry
32761  * @extends Roo.bootstrap.Component
32762  * Bootstrap Layout Masonry class
32763  * 
32764  * @constructor
32765  * Create a new Element
32766  * @param {Object} config The config object
32767  */
32768
32769 Roo.bootstrap.LayoutMasonry = function(config){
32770     
32771     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
32772     
32773     this.bricks = [];
32774     
32775     Roo.bootstrap.LayoutMasonry.register(this);
32776     
32777     this.addEvents({
32778         // raw events
32779         /**
32780          * @event layout
32781          * Fire after layout the items
32782          * @param {Roo.bootstrap.LayoutMasonry} this
32783          * @param {Roo.EventObject} e
32784          */
32785         "layout" : true
32786     });
32787     
32788 };
32789
32790 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
32791     
32792     /**
32793      * @cfg {Boolean} isLayoutInstant = no animation?
32794      */   
32795     isLayoutInstant : false, // needed?
32796    
32797     /**
32798      * @cfg {Number} boxWidth  width of the columns
32799      */   
32800     boxWidth : 450,
32801     
32802       /**
32803      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
32804      */   
32805     boxHeight : 0,
32806     
32807     /**
32808      * @cfg {Number} padWidth padding below box..
32809      */   
32810     padWidth : 10, 
32811     
32812     /**
32813      * @cfg {Number} gutter gutter width..
32814      */   
32815     gutter : 10,
32816     
32817      /**
32818      * @cfg {Number} maxCols maximum number of columns
32819      */   
32820     
32821     maxCols: 0,
32822     
32823     /**
32824      * @cfg {Boolean} isAutoInitial defalut true
32825      */   
32826     isAutoInitial : true, 
32827     
32828     containerWidth: 0,
32829     
32830     /**
32831      * @cfg {Boolean} isHorizontal defalut false
32832      */   
32833     isHorizontal : false, 
32834
32835     currentSize : null,
32836     
32837     tag: 'div',
32838     
32839     cls: '',
32840     
32841     bricks: null, //CompositeElement
32842     
32843     cols : 1,
32844     
32845     _isLayoutInited : false,
32846     
32847 //    isAlternative : false, // only use for vertical layout...
32848     
32849     /**
32850      * @cfg {Number} alternativePadWidth padding below box..
32851      */   
32852     alternativePadWidth : 50,
32853     
32854     selectedBrick : [],
32855     
32856     getAutoCreate : function(){
32857         
32858         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
32859         
32860         var cfg = {
32861             tag: this.tag,
32862             cls: 'blog-masonary-wrapper ' + this.cls,
32863             cn : {
32864                 cls : 'mas-boxes masonary'
32865             }
32866         };
32867         
32868         return cfg;
32869     },
32870     
32871     getChildContainer: function( )
32872     {
32873         if (this.boxesEl) {
32874             return this.boxesEl;
32875         }
32876         
32877         this.boxesEl = this.el.select('.mas-boxes').first();
32878         
32879         return this.boxesEl;
32880     },
32881     
32882     
32883     initEvents : function()
32884     {
32885         var _this = this;
32886         
32887         if(this.isAutoInitial){
32888             Roo.log('hook children rendered');
32889             this.on('childrenrendered', function() {
32890                 Roo.log('children rendered');
32891                 _this.initial();
32892             } ,this);
32893         }
32894     },
32895     
32896     initial : function()
32897     {
32898         this.selectedBrick = [];
32899         
32900         this.currentSize = this.el.getBox(true);
32901         
32902         Roo.EventManager.onWindowResize(this.resize, this); 
32903
32904         if(!this.isAutoInitial){
32905             this.layout();
32906             return;
32907         }
32908         
32909         this.layout();
32910         
32911         return;
32912         //this.layout.defer(500,this);
32913         
32914     },
32915     
32916     resize : function()
32917     {
32918         var cs = this.el.getBox(true);
32919         
32920         if (
32921                 this.currentSize.width == cs.width && 
32922                 this.currentSize.x == cs.x && 
32923                 this.currentSize.height == cs.height && 
32924                 this.currentSize.y == cs.y 
32925         ) {
32926             Roo.log("no change in with or X or Y");
32927             return;
32928         }
32929         
32930         this.currentSize = cs;
32931         
32932         this.layout();
32933         
32934     },
32935     
32936     layout : function()
32937     {   
32938         this._resetLayout();
32939         
32940         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32941         
32942         this.layoutItems( isInstant );
32943       
32944         this._isLayoutInited = true;
32945         
32946         this.fireEvent('layout', this);
32947         
32948     },
32949     
32950     _resetLayout : function()
32951     {
32952         if(this.isHorizontal){
32953             this.horizontalMeasureColumns();
32954             return;
32955         }
32956         
32957         this.verticalMeasureColumns();
32958         
32959     },
32960     
32961     verticalMeasureColumns : function()
32962     {
32963         this.getContainerWidth();
32964         
32965 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
32966 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
32967 //            return;
32968 //        }
32969         
32970         var boxWidth = this.boxWidth + this.padWidth;
32971         
32972         if(this.containerWidth < this.boxWidth){
32973             boxWidth = this.containerWidth
32974         }
32975         
32976         var containerWidth = this.containerWidth;
32977         
32978         var cols = Math.floor(containerWidth / boxWidth);
32979         
32980         this.cols = Math.max( cols, 1 );
32981         
32982         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32983         
32984         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
32985         
32986         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
32987         
32988         this.colWidth = boxWidth + avail - this.padWidth;
32989         
32990         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
32991         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
32992     },
32993     
32994     horizontalMeasureColumns : function()
32995     {
32996         this.getContainerWidth();
32997         
32998         var boxWidth = this.boxWidth;
32999         
33000         if(this.containerWidth < boxWidth){
33001             boxWidth = this.containerWidth;
33002         }
33003         
33004         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33005         
33006         this.el.setHeight(boxWidth);
33007         
33008     },
33009     
33010     getContainerWidth : function()
33011     {
33012         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33013     },
33014     
33015     layoutItems : function( isInstant )
33016     {
33017         Roo.log(this.bricks);
33018         
33019         var items = Roo.apply([], this.bricks);
33020         
33021         if(this.isHorizontal){
33022             this._horizontalLayoutItems( items , isInstant );
33023             return;
33024         }
33025         
33026 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33027 //            this._verticalAlternativeLayoutItems( items , isInstant );
33028 //            return;
33029 //        }
33030         
33031         this._verticalLayoutItems( items , isInstant );
33032         
33033     },
33034     
33035     _verticalLayoutItems : function ( items , isInstant)
33036     {
33037         if ( !items || !items.length ) {
33038             return;
33039         }
33040         
33041         var standard = [
33042             ['xs', 'xs', 'xs', 'tall'],
33043             ['xs', 'xs', 'tall'],
33044             ['xs', 'xs', 'sm'],
33045             ['xs', 'xs', 'xs'],
33046             ['xs', 'tall'],
33047             ['xs', 'sm'],
33048             ['xs', 'xs'],
33049             ['xs'],
33050             
33051             ['sm', 'xs', 'xs'],
33052             ['sm', 'xs'],
33053             ['sm'],
33054             
33055             ['tall', 'xs', 'xs', 'xs'],
33056             ['tall', 'xs', 'xs'],
33057             ['tall', 'xs'],
33058             ['tall']
33059             
33060         ];
33061         
33062         var queue = [];
33063         
33064         var boxes = [];
33065         
33066         var box = [];
33067         
33068         Roo.each(items, function(item, k){
33069             
33070             switch (item.size) {
33071                 // these layouts take up a full box,
33072                 case 'md' :
33073                 case 'md-left' :
33074                 case 'md-right' :
33075                 case 'wide' :
33076                     
33077                     if(box.length){
33078                         boxes.push(box);
33079                         box = [];
33080                     }
33081                     
33082                     boxes.push([item]);
33083                     
33084                     break;
33085                     
33086                 case 'xs' :
33087                 case 'sm' :
33088                 case 'tall' :
33089                     
33090                     box.push(item);
33091                     
33092                     break;
33093                 default :
33094                     break;
33095                     
33096             }
33097             
33098         }, this);
33099         
33100         if(box.length){
33101             boxes.push(box);
33102             box = [];
33103         }
33104         
33105         var filterPattern = function(box, length)
33106         {
33107             if(!box.length){
33108                 return;
33109             }
33110             
33111             var match = false;
33112             
33113             var pattern = box.slice(0, length);
33114             
33115             var format = [];
33116             
33117             Roo.each(pattern, function(i){
33118                 format.push(i.size);
33119             }, this);
33120             
33121             Roo.each(standard, function(s){
33122                 
33123                 if(String(s) != String(format)){
33124                     return;
33125                 }
33126                 
33127                 match = true;
33128                 return false;
33129                 
33130             }, this);
33131             
33132             if(!match && length == 1){
33133                 return;
33134             }
33135             
33136             if(!match){
33137                 filterPattern(box, length - 1);
33138                 return;
33139             }
33140                 
33141             queue.push(pattern);
33142
33143             box = box.slice(length, box.length);
33144
33145             filterPattern(box, 4);
33146
33147             return;
33148             
33149         }
33150         
33151         Roo.each(boxes, function(box, k){
33152             
33153             if(!box.length){
33154                 return;
33155             }
33156             
33157             if(box.length == 1){
33158                 queue.push(box);
33159                 return;
33160             }
33161             
33162             filterPattern(box, 4);
33163             
33164         }, this);
33165         
33166         this._processVerticalLayoutQueue( queue, isInstant );
33167         
33168     },
33169     
33170 //    _verticalAlternativeLayoutItems : function( items , isInstant )
33171 //    {
33172 //        if ( !items || !items.length ) {
33173 //            return;
33174 //        }
33175 //
33176 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
33177 //        
33178 //    },
33179     
33180     _horizontalLayoutItems : function ( items , isInstant)
33181     {
33182         if ( !items || !items.length || items.length < 3) {
33183             return;
33184         }
33185         
33186         items.reverse();
33187         
33188         var eItems = items.slice(0, 3);
33189         
33190         items = items.slice(3, items.length);
33191         
33192         var standard = [
33193             ['xs', 'xs', 'xs', 'wide'],
33194             ['xs', 'xs', 'wide'],
33195             ['xs', 'xs', 'sm'],
33196             ['xs', 'xs', 'xs'],
33197             ['xs', 'wide'],
33198             ['xs', 'sm'],
33199             ['xs', 'xs'],
33200             ['xs'],
33201             
33202             ['sm', 'xs', 'xs'],
33203             ['sm', 'xs'],
33204             ['sm'],
33205             
33206             ['wide', 'xs', 'xs', 'xs'],
33207             ['wide', 'xs', 'xs'],
33208             ['wide', 'xs'],
33209             ['wide'],
33210             
33211             ['wide-thin']
33212         ];
33213         
33214         var queue = [];
33215         
33216         var boxes = [];
33217         
33218         var box = [];
33219         
33220         Roo.each(items, function(item, k){
33221             
33222             switch (item.size) {
33223                 case 'md' :
33224                 case 'md-left' :
33225                 case 'md-right' :
33226                 case 'tall' :
33227                     
33228                     if(box.length){
33229                         boxes.push(box);
33230                         box = [];
33231                     }
33232                     
33233                     boxes.push([item]);
33234                     
33235                     break;
33236                     
33237                 case 'xs' :
33238                 case 'sm' :
33239                 case 'wide' :
33240                 case 'wide-thin' :
33241                     
33242                     box.push(item);
33243                     
33244                     break;
33245                 default :
33246                     break;
33247                     
33248             }
33249             
33250         }, this);
33251         
33252         if(box.length){
33253             boxes.push(box);
33254             box = [];
33255         }
33256         
33257         var filterPattern = function(box, length)
33258         {
33259             if(!box.length){
33260                 return;
33261             }
33262             
33263             var match = false;
33264             
33265             var pattern = box.slice(0, length);
33266             
33267             var format = [];
33268             
33269             Roo.each(pattern, function(i){
33270                 format.push(i.size);
33271             }, this);
33272             
33273             Roo.each(standard, function(s){
33274                 
33275                 if(String(s) != String(format)){
33276                     return;
33277                 }
33278                 
33279                 match = true;
33280                 return false;
33281                 
33282             }, this);
33283             
33284             if(!match && length == 1){
33285                 return;
33286             }
33287             
33288             if(!match){
33289                 filterPattern(box, length - 1);
33290                 return;
33291             }
33292                 
33293             queue.push(pattern);
33294
33295             box = box.slice(length, box.length);
33296
33297             filterPattern(box, 4);
33298
33299             return;
33300             
33301         }
33302         
33303         Roo.each(boxes, function(box, k){
33304             
33305             if(!box.length){
33306                 return;
33307             }
33308             
33309             if(box.length == 1){
33310                 queue.push(box);
33311                 return;
33312             }
33313             
33314             filterPattern(box, 4);
33315             
33316         }, this);
33317         
33318         
33319         var prune = [];
33320         
33321         var pos = this.el.getBox(true);
33322         
33323         var minX = pos.x;
33324         
33325         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33326         
33327         var hit_end = false;
33328         
33329         Roo.each(queue, function(box){
33330             
33331             if(hit_end){
33332                 
33333                 Roo.each(box, function(b){
33334                 
33335                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33336                     b.el.hide();
33337
33338                 }, this);
33339
33340                 return;
33341             }
33342             
33343             var mx = 0;
33344             
33345             Roo.each(box, function(b){
33346                 
33347                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33348                 b.el.show();
33349
33350                 mx = Math.max(mx, b.x);
33351                 
33352             }, this);
33353             
33354             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33355             
33356             if(maxX < minX){
33357                 
33358                 Roo.each(box, function(b){
33359                 
33360                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33361                     b.el.hide();
33362                     
33363                 }, this);
33364                 
33365                 hit_end = true;
33366                 
33367                 return;
33368             }
33369             
33370             prune.push(box);
33371             
33372         }, this);
33373         
33374         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
33375     },
33376     
33377     /** Sets position of item in DOM
33378     * @param {Element} item
33379     * @param {Number} x - horizontal position
33380     * @param {Number} y - vertical position
33381     * @param {Boolean} isInstant - disables transitions
33382     */
33383     _processVerticalLayoutQueue : function( queue, isInstant )
33384     {
33385         var pos = this.el.getBox(true);
33386         var x = pos.x;
33387         var y = pos.y;
33388         var maxY = [];
33389         
33390         for (var i = 0; i < this.cols; i++){
33391             maxY[i] = pos.y;
33392         }
33393         
33394         Roo.each(queue, function(box, k){
33395             
33396             var col = k % this.cols;
33397             
33398             Roo.each(box, function(b,kk){
33399                 
33400                 b.el.position('absolute');
33401                 
33402                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33403                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33404                 
33405                 if(b.size == 'md-left' || b.size == 'md-right'){
33406                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33407                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33408                 }
33409                 
33410                 b.el.setWidth(width);
33411                 b.el.setHeight(height);
33412                 // iframe?
33413                 b.el.select('iframe',true).setSize(width,height);
33414                 
33415             }, this);
33416             
33417             for (var i = 0; i < this.cols; i++){
33418                 
33419                 if(maxY[i] < maxY[col]){
33420                     col = i;
33421                     continue;
33422                 }
33423                 
33424                 col = Math.min(col, i);
33425                 
33426             }
33427             
33428             x = pos.x + col * (this.colWidth + this.padWidth);
33429             
33430             y = maxY[col];
33431             
33432             var positions = [];
33433             
33434             switch (box.length){
33435                 case 1 :
33436                     positions = this.getVerticalOneBoxColPositions(x, y, box);
33437                     break;
33438                 case 2 :
33439                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
33440                     break;
33441                 case 3 :
33442                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
33443                     break;
33444                 case 4 :
33445                     positions = this.getVerticalFourBoxColPositions(x, y, box);
33446                     break;
33447                 default :
33448                     break;
33449             }
33450             
33451             Roo.each(box, function(b,kk){
33452                 
33453                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33454                 
33455                 var sz = b.el.getSize();
33456                 
33457                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
33458                 
33459             }, this);
33460             
33461         }, this);
33462         
33463         var mY = 0;
33464         
33465         for (var i = 0; i < this.cols; i++){
33466             mY = Math.max(mY, maxY[i]);
33467         }
33468         
33469         this.el.setHeight(mY - pos.y);
33470         
33471     },
33472     
33473 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
33474 //    {
33475 //        var pos = this.el.getBox(true);
33476 //        var x = pos.x;
33477 //        var y = pos.y;
33478 //        var maxX = pos.right;
33479 //        
33480 //        var maxHeight = 0;
33481 //        
33482 //        Roo.each(items, function(item, k){
33483 //            
33484 //            var c = k % 2;
33485 //            
33486 //            item.el.position('absolute');
33487 //                
33488 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
33489 //
33490 //            item.el.setWidth(width);
33491 //
33492 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
33493 //
33494 //            item.el.setHeight(height);
33495 //            
33496 //            if(c == 0){
33497 //                item.el.setXY([x, y], isInstant ? false : true);
33498 //            } else {
33499 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
33500 //            }
33501 //            
33502 //            y = y + height + this.alternativePadWidth;
33503 //            
33504 //            maxHeight = maxHeight + height + this.alternativePadWidth;
33505 //            
33506 //        }, this);
33507 //        
33508 //        this.el.setHeight(maxHeight);
33509 //        
33510 //    },
33511     
33512     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
33513     {
33514         var pos = this.el.getBox(true);
33515         
33516         var minX = pos.x;
33517         var minY = pos.y;
33518         
33519         var maxX = pos.right;
33520         
33521         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
33522         
33523         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33524         
33525         Roo.each(queue, function(box, k){
33526             
33527             Roo.each(box, function(b, kk){
33528                 
33529                 b.el.position('absolute');
33530                 
33531                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33532                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33533                 
33534                 if(b.size == 'md-left' || b.size == 'md-right'){
33535                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33536                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33537                 }
33538                 
33539                 b.el.setWidth(width);
33540                 b.el.setHeight(height);
33541                 
33542             }, this);
33543             
33544             if(!box.length){
33545                 return;
33546             }
33547             
33548             var positions = [];
33549             
33550             switch (box.length){
33551                 case 1 :
33552                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
33553                     break;
33554                 case 2 :
33555                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
33556                     break;
33557                 case 3 :
33558                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
33559                     break;
33560                 case 4 :
33561                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
33562                     break;
33563                 default :
33564                     break;
33565             }
33566             
33567             Roo.each(box, function(b,kk){
33568                 
33569                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33570                 
33571                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
33572                 
33573             }, this);
33574             
33575         }, this);
33576         
33577     },
33578     
33579     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
33580     {
33581         Roo.each(eItems, function(b,k){
33582             
33583             b.size = (k == 0) ? 'sm' : 'xs';
33584             b.x = (k == 0) ? 2 : 1;
33585             b.y = (k == 0) ? 2 : 1;
33586             
33587             b.el.position('absolute');
33588             
33589             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33590                 
33591             b.el.setWidth(width);
33592             
33593             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33594             
33595             b.el.setHeight(height);
33596             
33597         }, this);
33598
33599         var positions = [];
33600         
33601         positions.push({
33602             x : maxX - this.unitWidth * 2 - this.gutter,
33603             y : minY
33604         });
33605         
33606         positions.push({
33607             x : maxX - this.unitWidth,
33608             y : minY + (this.unitWidth + this.gutter) * 2
33609         });
33610         
33611         positions.push({
33612             x : maxX - this.unitWidth * 3 - this.gutter * 2,
33613             y : minY
33614         });
33615         
33616         Roo.each(eItems, function(b,k){
33617             
33618             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
33619
33620         }, this);
33621         
33622     },
33623     
33624     getVerticalOneBoxColPositions : function(x, y, box)
33625     {
33626         var pos = [];
33627         
33628         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
33629         
33630         if(box[0].size == 'md-left'){
33631             rand = 0;
33632         }
33633         
33634         if(box[0].size == 'md-right'){
33635             rand = 1;
33636         }
33637         
33638         pos.push({
33639             x : x + (this.unitWidth + this.gutter) * rand,
33640             y : y
33641         });
33642         
33643         return pos;
33644     },
33645     
33646     getVerticalTwoBoxColPositions : function(x, y, box)
33647     {
33648         var pos = [];
33649         
33650         if(box[0].size == 'xs'){
33651             
33652             pos.push({
33653                 x : x,
33654                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
33655             });
33656
33657             pos.push({
33658                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
33659                 y : y
33660             });
33661             
33662             return pos;
33663             
33664         }
33665         
33666         pos.push({
33667             x : x,
33668             y : y
33669         });
33670
33671         pos.push({
33672             x : x + (this.unitWidth + this.gutter) * 2,
33673             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
33674         });
33675         
33676         return pos;
33677         
33678     },
33679     
33680     getVerticalThreeBoxColPositions : function(x, y, box)
33681     {
33682         var pos = [];
33683         
33684         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
33685             
33686             pos.push({
33687                 x : x,
33688                 y : y
33689             });
33690
33691             pos.push({
33692                 x : x + (this.unitWidth + this.gutter) * 1,
33693                 y : y
33694             });
33695             
33696             pos.push({
33697                 x : x + (this.unitWidth + this.gutter) * 2,
33698                 y : y
33699             });
33700             
33701             return pos;
33702             
33703         }
33704         
33705         if(box[0].size == 'xs' && box[1].size == 'xs'){
33706             
33707             pos.push({
33708                 x : x,
33709                 y : y
33710             });
33711
33712             pos.push({
33713                 x : x,
33714                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
33715             });
33716             
33717             pos.push({
33718                 x : x + (this.unitWidth + this.gutter) * 1,
33719                 y : y
33720             });
33721             
33722             return pos;
33723             
33724         }
33725         
33726         pos.push({
33727             x : x,
33728             y : y
33729         });
33730
33731         pos.push({
33732             x : x + (this.unitWidth + this.gutter) * 2,
33733             y : y
33734         });
33735
33736         pos.push({
33737             x : x + (this.unitWidth + this.gutter) * 2,
33738             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
33739         });
33740             
33741         return pos;
33742         
33743     },
33744     
33745     getVerticalFourBoxColPositions : function(x, y, box)
33746     {
33747         var pos = [];
33748         
33749         if(box[0].size == 'xs'){
33750             
33751             pos.push({
33752                 x : x,
33753                 y : y
33754             });
33755
33756             pos.push({
33757                 x : x,
33758                 y : y + (this.unitHeight + this.gutter) * 1
33759             });
33760             
33761             pos.push({
33762                 x : x,
33763                 y : y + (this.unitHeight + this.gutter) * 2
33764             });
33765             
33766             pos.push({
33767                 x : x + (this.unitWidth + this.gutter) * 1,
33768                 y : y
33769             });
33770             
33771             return pos;
33772             
33773         }
33774         
33775         pos.push({
33776             x : x,
33777             y : y
33778         });
33779
33780         pos.push({
33781             x : x + (this.unitWidth + this.gutter) * 2,
33782             y : y
33783         });
33784
33785         pos.push({
33786             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
33787             y : y + (this.unitHeight + this.gutter) * 1
33788         });
33789
33790         pos.push({
33791             x : x + (this.unitWidth + this.gutter) * 2,
33792             y : y + (this.unitWidth + this.gutter) * 2
33793         });
33794
33795         return pos;
33796         
33797     },
33798     
33799     getHorizontalOneBoxColPositions : function(maxX, minY, box)
33800     {
33801         var pos = [];
33802         
33803         if(box[0].size == 'md-left'){
33804             pos.push({
33805                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
33806                 y : minY
33807             });
33808             
33809             return pos;
33810         }
33811         
33812         if(box[0].size == 'md-right'){
33813             pos.push({
33814                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
33815                 y : minY + (this.unitWidth + this.gutter) * 1
33816             });
33817             
33818             return pos;
33819         }
33820         
33821         var rand = Math.floor(Math.random() * (4 - box[0].y));
33822         
33823         pos.push({
33824             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33825             y : minY + (this.unitWidth + this.gutter) * rand
33826         });
33827         
33828         return pos;
33829         
33830     },
33831     
33832     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
33833     {
33834         var pos = [];
33835         
33836         if(box[0].size == 'xs'){
33837             
33838             pos.push({
33839                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33840                 y : minY
33841             });
33842
33843             pos.push({
33844                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33845                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
33846             });
33847             
33848             return pos;
33849             
33850         }
33851         
33852         pos.push({
33853             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33854             y : minY
33855         });
33856
33857         pos.push({
33858             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33859             y : minY + (this.unitWidth + this.gutter) * 2
33860         });
33861         
33862         return pos;
33863         
33864     },
33865     
33866     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
33867     {
33868         var pos = [];
33869         
33870         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
33871             
33872             pos.push({
33873                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33874                 y : minY
33875             });
33876
33877             pos.push({
33878                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33879                 y : minY + (this.unitWidth + this.gutter) * 1
33880             });
33881             
33882             pos.push({
33883                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33884                 y : minY + (this.unitWidth + this.gutter) * 2
33885             });
33886             
33887             return pos;
33888             
33889         }
33890         
33891         if(box[0].size == 'xs' && box[1].size == 'xs'){
33892             
33893             pos.push({
33894                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33895                 y : minY
33896             });
33897
33898             pos.push({
33899                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33900                 y : minY
33901             });
33902             
33903             pos.push({
33904                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33905                 y : minY + (this.unitWidth + this.gutter) * 1
33906             });
33907             
33908             return pos;
33909             
33910         }
33911         
33912         pos.push({
33913             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33914             y : minY
33915         });
33916
33917         pos.push({
33918             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33919             y : minY + (this.unitWidth + this.gutter) * 2
33920         });
33921
33922         pos.push({
33923             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33924             y : minY + (this.unitWidth + this.gutter) * 2
33925         });
33926             
33927         return pos;
33928         
33929     },
33930     
33931     getHorizontalFourBoxColPositions : function(maxX, minY, box)
33932     {
33933         var pos = [];
33934         
33935         if(box[0].size == 'xs'){
33936             
33937             pos.push({
33938                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33939                 y : minY
33940             });
33941
33942             pos.push({
33943                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33944                 y : minY
33945             });
33946             
33947             pos.push({
33948                 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),
33949                 y : minY
33950             });
33951             
33952             pos.push({
33953                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
33954                 y : minY + (this.unitWidth + this.gutter) * 1
33955             });
33956             
33957             return pos;
33958             
33959         }
33960         
33961         pos.push({
33962             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33963             y : minY
33964         });
33965         
33966         pos.push({
33967             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33968             y : minY + (this.unitWidth + this.gutter) * 2
33969         });
33970         
33971         pos.push({
33972             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33973             y : minY + (this.unitWidth + this.gutter) * 2
33974         });
33975         
33976         pos.push({
33977             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),
33978             y : minY + (this.unitWidth + this.gutter) * 2
33979         });
33980
33981         return pos;
33982         
33983     },
33984     
33985     /**
33986     * remove a Masonry Brick
33987     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
33988     */
33989     removeBrick : function(brick_id)
33990     {
33991         if (!brick_id) {
33992             return;
33993         }
33994         
33995         for (var i = 0; i<this.bricks.length; i++) {
33996             if (this.bricks[i].id == brick_id) {
33997                 this.bricks.splice(i,1);
33998                 this.el.dom.removeChild(Roo.get(brick_id).dom);
33999                 this.initial();
34000             }
34001         }
34002     },
34003     
34004     /**
34005     * adds a Masonry Brick
34006     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34007     */
34008     addBrick : function(cfg)
34009     {
34010         var cn = new Roo.bootstrap.MasonryBrick(cfg);
34011         //this.register(cn);
34012         cn.parentId = this.id;
34013         cn.render(this.el);
34014         return cn;
34015     },
34016     
34017     /**
34018     * register a Masonry Brick
34019     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34020     */
34021     
34022     register : function(brick)
34023     {
34024         this.bricks.push(brick);
34025         brick.masonryId = this.id;
34026     },
34027     
34028     /**
34029     * clear all the Masonry Brick
34030     */
34031     clearAll : function()
34032     {
34033         this.bricks = [];
34034         //this.getChildContainer().dom.innerHTML = "";
34035         this.el.dom.innerHTML = '';
34036     },
34037     
34038     getSelected : function()
34039     {
34040         if (!this.selectedBrick) {
34041             return false;
34042         }
34043         
34044         return this.selectedBrick;
34045     }
34046 });
34047
34048 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34049     
34050     groups: {},
34051      /**
34052     * register a Masonry Layout
34053     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34054     */
34055     
34056     register : function(layout)
34057     {
34058         this.groups[layout.id] = layout;
34059     },
34060     /**
34061     * fetch a  Masonry Layout based on the masonry layout ID
34062     * @param {string} the masonry layout to add
34063     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34064     */
34065     
34066     get: function(layout_id) {
34067         if (typeof(this.groups[layout_id]) == 'undefined') {
34068             return false;
34069         }
34070         return this.groups[layout_id] ;
34071     }
34072     
34073     
34074     
34075 });
34076
34077  
34078
34079  /**
34080  *
34081  * This is based on 
34082  * http://masonry.desandro.com
34083  *
34084  * The idea is to render all the bricks based on vertical width...
34085  *
34086  * The original code extends 'outlayer' - we might need to use that....
34087  * 
34088  */
34089
34090
34091 /**
34092  * @class Roo.bootstrap.LayoutMasonryAuto
34093  * @extends Roo.bootstrap.Component
34094  * Bootstrap Layout Masonry class
34095  * 
34096  * @constructor
34097  * Create a new Element
34098  * @param {Object} config The config object
34099  */
34100
34101 Roo.bootstrap.LayoutMasonryAuto = function(config){
34102     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34103 };
34104
34105 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
34106     
34107       /**
34108      * @cfg {Boolean} isFitWidth  - resize the width..
34109      */   
34110     isFitWidth : false,  // options..
34111     /**
34112      * @cfg {Boolean} isOriginLeft = left align?
34113      */   
34114     isOriginLeft : true,
34115     /**
34116      * @cfg {Boolean} isOriginTop = top align?
34117      */   
34118     isOriginTop : false,
34119     /**
34120      * @cfg {Boolean} isLayoutInstant = no animation?
34121      */   
34122     isLayoutInstant : false, // needed?
34123     /**
34124      * @cfg {Boolean} isResizingContainer = not sure if this is used..
34125      */   
34126     isResizingContainer : true,
34127     /**
34128      * @cfg {Number} columnWidth  width of the columns 
34129      */   
34130     
34131     columnWidth : 0,
34132     
34133     /**
34134      * @cfg {Number} maxCols maximum number of columns
34135      */   
34136     
34137     maxCols: 0,
34138     /**
34139      * @cfg {Number} padHeight padding below box..
34140      */   
34141     
34142     padHeight : 10, 
34143     
34144     /**
34145      * @cfg {Boolean} isAutoInitial defalut true
34146      */   
34147     
34148     isAutoInitial : true, 
34149     
34150     // private?
34151     gutter : 0,
34152     
34153     containerWidth: 0,
34154     initialColumnWidth : 0,
34155     currentSize : null,
34156     
34157     colYs : null, // array.
34158     maxY : 0,
34159     padWidth: 10,
34160     
34161     
34162     tag: 'div',
34163     cls: '',
34164     bricks: null, //CompositeElement
34165     cols : 0, // array?
34166     // element : null, // wrapped now this.el
34167     _isLayoutInited : null, 
34168     
34169     
34170     getAutoCreate : function(){
34171         
34172         var cfg = {
34173             tag: this.tag,
34174             cls: 'blog-masonary-wrapper ' + this.cls,
34175             cn : {
34176                 cls : 'mas-boxes masonary'
34177             }
34178         };
34179         
34180         return cfg;
34181     },
34182     
34183     getChildContainer: function( )
34184     {
34185         if (this.boxesEl) {
34186             return this.boxesEl;
34187         }
34188         
34189         this.boxesEl = this.el.select('.mas-boxes').first();
34190         
34191         return this.boxesEl;
34192     },
34193     
34194     
34195     initEvents : function()
34196     {
34197         var _this = this;
34198         
34199         if(this.isAutoInitial){
34200             Roo.log('hook children rendered');
34201             this.on('childrenrendered', function() {
34202                 Roo.log('children rendered');
34203                 _this.initial();
34204             } ,this);
34205         }
34206         
34207     },
34208     
34209     initial : function()
34210     {
34211         this.reloadItems();
34212
34213         this.currentSize = this.el.getBox(true);
34214
34215         /// was window resize... - let's see if this works..
34216         Roo.EventManager.onWindowResize(this.resize, this); 
34217
34218         if(!this.isAutoInitial){
34219             this.layout();
34220             return;
34221         }
34222         
34223         this.layout.defer(500,this);
34224     },
34225     
34226     reloadItems: function()
34227     {
34228         this.bricks = this.el.select('.masonry-brick', true);
34229         
34230         this.bricks.each(function(b) {
34231             //Roo.log(b.getSize());
34232             if (!b.attr('originalwidth')) {
34233                 b.attr('originalwidth',  b.getSize().width);
34234             }
34235             
34236         });
34237         
34238         Roo.log(this.bricks.elements.length);
34239     },
34240     
34241     resize : function()
34242     {
34243         Roo.log('resize');
34244         var cs = this.el.getBox(true);
34245         
34246         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34247             Roo.log("no change in with or X");
34248             return;
34249         }
34250         this.currentSize = cs;
34251         this.layout();
34252     },
34253     
34254     layout : function()
34255     {
34256          Roo.log('layout');
34257         this._resetLayout();
34258         //this._manageStamps();
34259       
34260         // don't animate first layout
34261         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34262         this.layoutItems( isInstant );
34263       
34264         // flag for initalized
34265         this._isLayoutInited = true;
34266     },
34267     
34268     layoutItems : function( isInstant )
34269     {
34270         //var items = this._getItemsForLayout( this.items );
34271         // original code supports filtering layout items.. we just ignore it..
34272         
34273         this._layoutItems( this.bricks , isInstant );
34274       
34275         this._postLayout();
34276     },
34277     _layoutItems : function ( items , isInstant)
34278     {
34279        //this.fireEvent( 'layout', this, items );
34280     
34281
34282         if ( !items || !items.elements.length ) {
34283           // no items, emit event with empty array
34284             return;
34285         }
34286
34287         var queue = [];
34288         items.each(function(item) {
34289             Roo.log("layout item");
34290             Roo.log(item);
34291             // get x/y object from method
34292             var position = this._getItemLayoutPosition( item );
34293             // enqueue
34294             position.item = item;
34295             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34296             queue.push( position );
34297         }, this);
34298       
34299         this._processLayoutQueue( queue );
34300     },
34301     /** Sets position of item in DOM
34302     * @param {Element} item
34303     * @param {Number} x - horizontal position
34304     * @param {Number} y - vertical position
34305     * @param {Boolean} isInstant - disables transitions
34306     */
34307     _processLayoutQueue : function( queue )
34308     {
34309         for ( var i=0, len = queue.length; i < len; i++ ) {
34310             var obj = queue[i];
34311             obj.item.position('absolute');
34312             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34313         }
34314     },
34315       
34316     
34317     /**
34318     * Any logic you want to do after each layout,
34319     * i.e. size the container
34320     */
34321     _postLayout : function()
34322     {
34323         this.resizeContainer();
34324     },
34325     
34326     resizeContainer : function()
34327     {
34328         if ( !this.isResizingContainer ) {
34329             return;
34330         }
34331         var size = this._getContainerSize();
34332         if ( size ) {
34333             this.el.setSize(size.width,size.height);
34334             this.boxesEl.setSize(size.width,size.height);
34335         }
34336     },
34337     
34338     
34339     
34340     _resetLayout : function()
34341     {
34342         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34343         this.colWidth = this.el.getWidth();
34344         //this.gutter = this.el.getWidth(); 
34345         
34346         this.measureColumns();
34347
34348         // reset column Y
34349         var i = this.cols;
34350         this.colYs = [];
34351         while (i--) {
34352             this.colYs.push( 0 );
34353         }
34354     
34355         this.maxY = 0;
34356     },
34357
34358     measureColumns : function()
34359     {
34360         this.getContainerWidth();
34361       // if columnWidth is 0, default to outerWidth of first item
34362         if ( !this.columnWidth ) {
34363             var firstItem = this.bricks.first();
34364             Roo.log(firstItem);
34365             this.columnWidth  = this.containerWidth;
34366             if (firstItem && firstItem.attr('originalwidth') ) {
34367                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34368             }
34369             // columnWidth fall back to item of first element
34370             Roo.log("set column width?");
34371                         this.initialColumnWidth = this.columnWidth  ;
34372
34373             // if first elem has no width, default to size of container
34374             
34375         }
34376         
34377         
34378         if (this.initialColumnWidth) {
34379             this.columnWidth = this.initialColumnWidth;
34380         }
34381         
34382         
34383             
34384         // column width is fixed at the top - however if container width get's smaller we should
34385         // reduce it...
34386         
34387         // this bit calcs how man columns..
34388             
34389         var columnWidth = this.columnWidth += this.gutter;
34390       
34391         // calculate columns
34392         var containerWidth = this.containerWidth + this.gutter;
34393         
34394         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
34395         // fix rounding errors, typically with gutters
34396         var excess = columnWidth - containerWidth % columnWidth;
34397         
34398         
34399         // if overshoot is less than a pixel, round up, otherwise floor it
34400         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
34401         cols = Math[ mathMethod ]( cols );
34402         this.cols = Math.max( cols, 1 );
34403         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34404         
34405          // padding positioning..
34406         var totalColWidth = this.cols * this.columnWidth;
34407         var padavail = this.containerWidth - totalColWidth;
34408         // so for 2 columns - we need 3 'pads'
34409         
34410         var padNeeded = (1+this.cols) * this.padWidth;
34411         
34412         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
34413         
34414         this.columnWidth += padExtra
34415         //this.padWidth = Math.floor(padavail /  ( this.cols));
34416         
34417         // adjust colum width so that padding is fixed??
34418         
34419         // we have 3 columns ... total = width * 3
34420         // we have X left over... that should be used by 
34421         
34422         //if (this.expandC) {
34423             
34424         //}
34425         
34426         
34427         
34428     },
34429     
34430     getContainerWidth : function()
34431     {
34432        /* // container is parent if fit width
34433         var container = this.isFitWidth ? this.element.parentNode : this.element;
34434         // check that this.size and size are there
34435         // IE8 triggers resize on body size change, so they might not be
34436         
34437         var size = getSize( container );  //FIXME
34438         this.containerWidth = size && size.innerWidth; //FIXME
34439         */
34440          
34441         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34442         
34443     },
34444     
34445     _getItemLayoutPosition : function( item )  // what is item?
34446     {
34447         // we resize the item to our columnWidth..
34448       
34449         item.setWidth(this.columnWidth);
34450         item.autoBoxAdjust  = false;
34451         
34452         var sz = item.getSize();
34453  
34454         // how many columns does this brick span
34455         var remainder = this.containerWidth % this.columnWidth;
34456         
34457         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
34458         // round if off by 1 pixel, otherwise use ceil
34459         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
34460         colSpan = Math.min( colSpan, this.cols );
34461         
34462         // normally this should be '1' as we dont' currently allow multi width columns..
34463         
34464         var colGroup = this._getColGroup( colSpan );
34465         // get the minimum Y value from the columns
34466         var minimumY = Math.min.apply( Math, colGroup );
34467         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
34468         
34469         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
34470          
34471         // position the brick
34472         var position = {
34473             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
34474             y: this.currentSize.y + minimumY + this.padHeight
34475         };
34476         
34477         Roo.log(position);
34478         // apply setHeight to necessary columns
34479         var setHeight = minimumY + sz.height + this.padHeight;
34480         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
34481         
34482         var setSpan = this.cols + 1 - colGroup.length;
34483         for ( var i = 0; i < setSpan; i++ ) {
34484           this.colYs[ shortColIndex + i ] = setHeight ;
34485         }
34486       
34487         return position;
34488     },
34489     
34490     /**
34491      * @param {Number} colSpan - number of columns the element spans
34492      * @returns {Array} colGroup
34493      */
34494     _getColGroup : function( colSpan )
34495     {
34496         if ( colSpan < 2 ) {
34497           // if brick spans only one column, use all the column Ys
34498           return this.colYs;
34499         }
34500       
34501         var colGroup = [];
34502         // how many different places could this brick fit horizontally
34503         var groupCount = this.cols + 1 - colSpan;
34504         // for each group potential horizontal position
34505         for ( var i = 0; i < groupCount; i++ ) {
34506           // make an array of colY values for that one group
34507           var groupColYs = this.colYs.slice( i, i + colSpan );
34508           // and get the max value of the array
34509           colGroup[i] = Math.max.apply( Math, groupColYs );
34510         }
34511         return colGroup;
34512     },
34513     /*
34514     _manageStamp : function( stamp )
34515     {
34516         var stampSize =  stamp.getSize();
34517         var offset = stamp.getBox();
34518         // get the columns that this stamp affects
34519         var firstX = this.isOriginLeft ? offset.x : offset.right;
34520         var lastX = firstX + stampSize.width;
34521         var firstCol = Math.floor( firstX / this.columnWidth );
34522         firstCol = Math.max( 0, firstCol );
34523         
34524         var lastCol = Math.floor( lastX / this.columnWidth );
34525         // lastCol should not go over if multiple of columnWidth #425
34526         lastCol -= lastX % this.columnWidth ? 0 : 1;
34527         lastCol = Math.min( this.cols - 1, lastCol );
34528         
34529         // set colYs to bottom of the stamp
34530         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
34531             stampSize.height;
34532             
34533         for ( var i = firstCol; i <= lastCol; i++ ) {
34534           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
34535         }
34536     },
34537     */
34538     
34539     _getContainerSize : function()
34540     {
34541         this.maxY = Math.max.apply( Math, this.colYs );
34542         var size = {
34543             height: this.maxY
34544         };
34545       
34546         if ( this.isFitWidth ) {
34547             size.width = this._getContainerFitWidth();
34548         }
34549       
34550         return size;
34551     },
34552     
34553     _getContainerFitWidth : function()
34554     {
34555         var unusedCols = 0;
34556         // count unused columns
34557         var i = this.cols;
34558         while ( --i ) {
34559           if ( this.colYs[i] !== 0 ) {
34560             break;
34561           }
34562           unusedCols++;
34563         }
34564         // fit container to columns that have been used
34565         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
34566     },
34567     
34568     needsResizeLayout : function()
34569     {
34570         var previousWidth = this.containerWidth;
34571         this.getContainerWidth();
34572         return previousWidth !== this.containerWidth;
34573     }
34574  
34575 });
34576
34577  
34578
34579  /*
34580  * - LGPL
34581  *
34582  * element
34583  * 
34584  */
34585
34586 /**
34587  * @class Roo.bootstrap.MasonryBrick
34588  * @extends Roo.bootstrap.Component
34589  * Bootstrap MasonryBrick class
34590  * 
34591  * @constructor
34592  * Create a new MasonryBrick
34593  * @param {Object} config The config object
34594  */
34595
34596 Roo.bootstrap.MasonryBrick = function(config){
34597     
34598     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
34599     
34600     Roo.bootstrap.MasonryBrick.register(this);
34601     
34602     this.addEvents({
34603         // raw events
34604         /**
34605          * @event click
34606          * When a MasonryBrick is clcik
34607          * @param {Roo.bootstrap.MasonryBrick} this
34608          * @param {Roo.EventObject} e
34609          */
34610         "click" : true
34611     });
34612 };
34613
34614 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
34615     
34616     /**
34617      * @cfg {String} title
34618      */   
34619     title : '',
34620     /**
34621      * @cfg {String} html
34622      */   
34623     html : '',
34624     /**
34625      * @cfg {String} bgimage
34626      */   
34627     bgimage : '',
34628     /**
34629      * @cfg {String} videourl
34630      */   
34631     videourl : '',
34632     /**
34633      * @cfg {String} cls
34634      */   
34635     cls : '',
34636     /**
34637      * @cfg {String} href
34638      */   
34639     href : '',
34640     /**
34641      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
34642      */   
34643     size : 'xs',
34644     
34645     /**
34646      * @cfg {String} placetitle (center|bottom)
34647      */   
34648     placetitle : '',
34649     
34650     /**
34651      * @cfg {Boolean} isFitContainer defalut true
34652      */   
34653     isFitContainer : true, 
34654     
34655     /**
34656      * @cfg {Boolean} preventDefault defalut false
34657      */   
34658     preventDefault : false, 
34659     
34660     /**
34661      * @cfg {Boolean} inverse defalut false
34662      */   
34663     maskInverse : false, 
34664     
34665     getAutoCreate : function()
34666     {
34667         if(!this.isFitContainer){
34668             return this.getSplitAutoCreate();
34669         }
34670         
34671         var cls = 'masonry-brick masonry-brick-full';
34672         
34673         if(this.href.length){
34674             cls += ' masonry-brick-link';
34675         }
34676         
34677         if(this.bgimage.length){
34678             cls += ' masonry-brick-image';
34679         }
34680         
34681         if(this.maskInverse){
34682             cls += ' mask-inverse';
34683         }
34684         
34685         if(!this.html.length && !this.maskInverse && !this.videourl.length){
34686             cls += ' enable-mask';
34687         }
34688         
34689         if(this.size){
34690             cls += ' masonry-' + this.size + '-brick';
34691         }
34692         
34693         if(this.placetitle.length){
34694             
34695             switch (this.placetitle) {
34696                 case 'center' :
34697                     cls += ' masonry-center-title';
34698                     break;
34699                 case 'bottom' :
34700                     cls += ' masonry-bottom-title';
34701                     break;
34702                 default:
34703                     break;
34704             }
34705             
34706         } else {
34707             if(!this.html.length && !this.bgimage.length){
34708                 cls += ' masonry-center-title';
34709             }
34710
34711             if(!this.html.length && this.bgimage.length){
34712                 cls += ' masonry-bottom-title';
34713             }
34714         }
34715         
34716         if(this.cls){
34717             cls += ' ' + this.cls;
34718         }
34719         
34720         var cfg = {
34721             tag: (this.href.length) ? 'a' : 'div',
34722             cls: cls,
34723             cn: [
34724                 {
34725                     tag: 'div',
34726                     cls: 'masonry-brick-mask'
34727                 },
34728                 {
34729                     tag: 'div',
34730                     cls: 'masonry-brick-paragraph',
34731                     cn: []
34732                 }
34733             ]
34734         };
34735         
34736         if(this.href.length){
34737             cfg.href = this.href;
34738         }
34739         
34740         var cn = cfg.cn[1].cn;
34741         
34742         if(this.title.length){
34743             cn.push({
34744                 tag: 'h4',
34745                 cls: 'masonry-brick-title',
34746                 html: this.title
34747             });
34748         }
34749         
34750         if(this.html.length){
34751             cn.push({
34752                 tag: 'p',
34753                 cls: 'masonry-brick-text',
34754                 html: this.html
34755             });
34756         }
34757         
34758         if (!this.title.length && !this.html.length) {
34759             cfg.cn[1].cls += ' hide';
34760         }
34761         
34762         if(this.bgimage.length){
34763             cfg.cn.push({
34764                 tag: 'img',
34765                 cls: 'masonry-brick-image-view',
34766                 src: this.bgimage
34767             });
34768         }
34769         
34770         if(this.videourl.length){
34771             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
34772             // youtube support only?
34773             cfg.cn.push({
34774                 tag: 'iframe',
34775                 cls: 'masonry-brick-image-view',
34776                 src: vurl,
34777                 frameborder : 0,
34778                 allowfullscreen : true
34779             });
34780         }
34781         
34782         return cfg;
34783         
34784     },
34785     
34786     getSplitAutoCreate : function()
34787     {
34788         var cls = 'masonry-brick masonry-brick-split';
34789         
34790         if(this.href.length){
34791             cls += ' masonry-brick-link';
34792         }
34793         
34794         if(this.bgimage.length){
34795             cls += ' masonry-brick-image';
34796         }
34797         
34798         if(this.size){
34799             cls += ' masonry-' + this.size + '-brick';
34800         }
34801         
34802         switch (this.placetitle) {
34803             case 'center' :
34804                 cls += ' masonry-center-title';
34805                 break;
34806             case 'bottom' :
34807                 cls += ' masonry-bottom-title';
34808                 break;
34809             default:
34810                 if(!this.bgimage.length){
34811                     cls += ' masonry-center-title';
34812                 }
34813
34814                 if(this.bgimage.length){
34815                     cls += ' masonry-bottom-title';
34816                 }
34817                 break;
34818         }
34819         
34820         if(this.cls){
34821             cls += ' ' + this.cls;
34822         }
34823         
34824         var cfg = {
34825             tag: (this.href.length) ? 'a' : 'div',
34826             cls: cls,
34827             cn: [
34828                 {
34829                     tag: 'div',
34830                     cls: 'masonry-brick-split-head',
34831                     cn: [
34832                         {
34833                             tag: 'div',
34834                             cls: 'masonry-brick-paragraph',
34835                             cn: []
34836                         }
34837                     ]
34838                 },
34839                 {
34840                     tag: 'div',
34841                     cls: 'masonry-brick-split-body',
34842                     cn: []
34843                 }
34844             ]
34845         };
34846         
34847         if(this.href.length){
34848             cfg.href = this.href;
34849         }
34850         
34851         if(this.title.length){
34852             cfg.cn[0].cn[0].cn.push({
34853                 tag: 'h4',
34854                 cls: 'masonry-brick-title',
34855                 html: this.title
34856             });
34857         }
34858         
34859         if(this.html.length){
34860             cfg.cn[1].cn.push({
34861                 tag: 'p',
34862                 cls: 'masonry-brick-text',
34863                 html: this.html
34864             });
34865         }
34866
34867         if(this.bgimage.length){
34868             cfg.cn[0].cn.push({
34869                 tag: 'img',
34870                 cls: 'masonry-brick-image-view',
34871                 src: this.bgimage
34872             });
34873         }
34874         
34875         if(this.videourl.length){
34876             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
34877             // youtube support only?
34878             cfg.cn[0].cn.cn.push({
34879                 tag: 'iframe',
34880                 cls: 'masonry-brick-image-view',
34881                 src: vurl,
34882                 frameborder : 0,
34883                 allowfullscreen : true
34884             });
34885         }
34886         
34887         return cfg;
34888     },
34889     
34890     initEvents: function() 
34891     {
34892         switch (this.size) {
34893             case 'xs' :
34894                 this.x = 1;
34895                 this.y = 1;
34896                 break;
34897             case 'sm' :
34898                 this.x = 2;
34899                 this.y = 2;
34900                 break;
34901             case 'md' :
34902             case 'md-left' :
34903             case 'md-right' :
34904                 this.x = 3;
34905                 this.y = 3;
34906                 break;
34907             case 'tall' :
34908                 this.x = 2;
34909                 this.y = 3;
34910                 break;
34911             case 'wide' :
34912                 this.x = 3;
34913                 this.y = 2;
34914                 break;
34915             case 'wide-thin' :
34916                 this.x = 3;
34917                 this.y = 1;
34918                 break;
34919                         
34920             default :
34921                 break;
34922         }
34923         
34924         if(Roo.isTouch){
34925             this.el.on('touchstart', this.onTouchStart, this);
34926             this.el.on('touchmove', this.onTouchMove, this);
34927             this.el.on('touchend', this.onTouchEnd, this);
34928             this.el.on('contextmenu', this.onContextMenu, this);
34929         } else {
34930             this.el.on('mouseenter'  ,this.enter, this);
34931             this.el.on('mouseleave', this.leave, this);
34932             this.el.on('click', this.onClick, this);
34933         }
34934         
34935         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
34936             this.parent().bricks.push(this);   
34937         }
34938         
34939     },
34940     
34941     onClick: function(e, el)
34942     {
34943         var time = this.endTimer - this.startTimer;
34944         // Roo.log(e.preventDefault());
34945         if(Roo.isTouch){
34946             if(time > 1000){
34947                 e.preventDefault();
34948                 return;
34949             }
34950         }
34951         
34952         if(!this.preventDefault){
34953             return;
34954         }
34955         
34956         e.preventDefault();
34957         
34958         if (this.activeClass != '') {
34959             this.selectBrick();
34960         }
34961         
34962         this.fireEvent('click', this, e);
34963     },
34964     
34965     enter: function(e, el)
34966     {
34967         e.preventDefault();
34968         
34969         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
34970             return;
34971         }
34972         
34973         if(this.bgimage.length && this.html.length){
34974             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
34975         }
34976     },
34977     
34978     leave: function(e, el)
34979     {
34980         e.preventDefault();
34981         
34982         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
34983             return;
34984         }
34985         
34986         if(this.bgimage.length && this.html.length){
34987             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
34988         }
34989     },
34990     
34991     onTouchStart: function(e, el)
34992     {
34993 //        e.preventDefault();
34994         
34995         this.touchmoved = false;
34996         
34997         if(!this.isFitContainer){
34998             return;
34999         }
35000         
35001         if(!this.bgimage.length || !this.html.length){
35002             return;
35003         }
35004         
35005         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35006         
35007         this.timer = new Date().getTime();
35008         
35009     },
35010     
35011     onTouchMove: function(e, el)
35012     {
35013         this.touchmoved = true;
35014     },
35015     
35016     onContextMenu : function(e,el)
35017     {
35018         e.preventDefault();
35019         e.stopPropagation();
35020         return false;
35021     },
35022     
35023     onTouchEnd: function(e, el)
35024     {
35025 //        e.preventDefault();
35026         
35027         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35028         
35029             this.leave(e,el);
35030             
35031             return;
35032         }
35033         
35034         if(!this.bgimage.length || !this.html.length){
35035             
35036             if(this.href.length){
35037                 window.location.href = this.href;
35038             }
35039             
35040             return;
35041         }
35042         
35043         if(!this.isFitContainer){
35044             return;
35045         }
35046         
35047         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35048         
35049         window.location.href = this.href;
35050     },
35051     
35052     //selection on single brick only
35053     selectBrick : function() {
35054         
35055         if (!this.parentId) {
35056             return;
35057         }
35058         
35059         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35060         var index = m.selectedBrick.indexOf(this.id);
35061         
35062         if ( index > -1) {
35063             m.selectedBrick.splice(index,1);
35064             this.el.removeClass(this.activeClass);
35065             return;
35066         }
35067         
35068         for(var i = 0; i < m.selectedBrick.length; i++) {
35069             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35070             b.el.removeClass(b.activeClass);
35071         }
35072         
35073         m.selectedBrick = [];
35074         
35075         m.selectedBrick.push(this.id);
35076         this.el.addClass(this.activeClass);
35077         return;
35078     },
35079     
35080     isSelected : function(){
35081         return this.el.hasClass(this.activeClass);
35082         
35083     }
35084 });
35085
35086 Roo.apply(Roo.bootstrap.MasonryBrick, {
35087     
35088     //groups: {},
35089     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35090      /**
35091     * register a Masonry Brick
35092     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35093     */
35094     
35095     register : function(brick)
35096     {
35097         //this.groups[brick.id] = brick;
35098         this.groups.add(brick.id, brick);
35099     },
35100     /**
35101     * fetch a  masonry brick based on the masonry brick ID
35102     * @param {string} the masonry brick to add
35103     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35104     */
35105     
35106     get: function(brick_id) 
35107     {
35108         // if (typeof(this.groups[brick_id]) == 'undefined') {
35109         //     return false;
35110         // }
35111         // return this.groups[brick_id] ;
35112         
35113         if(this.groups.key(brick_id)) {
35114             return this.groups.key(brick_id);
35115         }
35116         
35117         return false;
35118     }
35119     
35120     
35121     
35122 });
35123
35124  /*
35125  * - LGPL
35126  *
35127  * element
35128  * 
35129  */
35130
35131 /**
35132  * @class Roo.bootstrap.Brick
35133  * @extends Roo.bootstrap.Component
35134  * Bootstrap Brick class
35135  * 
35136  * @constructor
35137  * Create a new Brick
35138  * @param {Object} config The config object
35139  */
35140
35141 Roo.bootstrap.Brick = function(config){
35142     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35143     
35144     this.addEvents({
35145         // raw events
35146         /**
35147          * @event click
35148          * When a Brick is click
35149          * @param {Roo.bootstrap.Brick} this
35150          * @param {Roo.EventObject} e
35151          */
35152         "click" : true
35153     });
35154 };
35155
35156 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
35157     
35158     /**
35159      * @cfg {String} title
35160      */   
35161     title : '',
35162     /**
35163      * @cfg {String} html
35164      */   
35165     html : '',
35166     /**
35167      * @cfg {String} bgimage
35168      */   
35169     bgimage : '',
35170     /**
35171      * @cfg {String} cls
35172      */   
35173     cls : '',
35174     /**
35175      * @cfg {String} href
35176      */   
35177     href : '',
35178     /**
35179      * @cfg {String} video
35180      */   
35181     video : '',
35182     /**
35183      * @cfg {Boolean} square
35184      */   
35185     square : true,
35186     
35187     getAutoCreate : function()
35188     {
35189         var cls = 'roo-brick';
35190         
35191         if(this.href.length){
35192             cls += ' roo-brick-link';
35193         }
35194         
35195         if(this.bgimage.length){
35196             cls += ' roo-brick-image';
35197         }
35198         
35199         if(!this.html.length && !this.bgimage.length){
35200             cls += ' roo-brick-center-title';
35201         }
35202         
35203         if(!this.html.length && this.bgimage.length){
35204             cls += ' roo-brick-bottom-title';
35205         }
35206         
35207         if(this.cls){
35208             cls += ' ' + this.cls;
35209         }
35210         
35211         var cfg = {
35212             tag: (this.href.length) ? 'a' : 'div',
35213             cls: cls,
35214             cn: [
35215                 {
35216                     tag: 'div',
35217                     cls: 'roo-brick-paragraph',
35218                     cn: []
35219                 }
35220             ]
35221         };
35222         
35223         if(this.href.length){
35224             cfg.href = this.href;
35225         }
35226         
35227         var cn = cfg.cn[0].cn;
35228         
35229         if(this.title.length){
35230             cn.push({
35231                 tag: 'h4',
35232                 cls: 'roo-brick-title',
35233                 html: this.title
35234             });
35235         }
35236         
35237         if(this.html.length){
35238             cn.push({
35239                 tag: 'p',
35240                 cls: 'roo-brick-text',
35241                 html: this.html
35242             });
35243         } else {
35244             cn.cls += ' hide';
35245         }
35246         
35247         if(this.bgimage.length){
35248             cfg.cn.push({
35249                 tag: 'img',
35250                 cls: 'roo-brick-image-view',
35251                 src: this.bgimage
35252             });
35253         }
35254         
35255         return cfg;
35256     },
35257     
35258     initEvents: function() 
35259     {
35260         if(this.title.length || this.html.length){
35261             this.el.on('mouseenter'  ,this.enter, this);
35262             this.el.on('mouseleave', this.leave, this);
35263         }
35264         
35265         Roo.EventManager.onWindowResize(this.resize, this); 
35266         
35267         if(this.bgimage.length){
35268             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35269             this.imageEl.on('load', this.onImageLoad, this);
35270             return;
35271         }
35272         
35273         this.resize();
35274     },
35275     
35276     onImageLoad : function()
35277     {
35278         this.resize();
35279     },
35280     
35281     resize : function()
35282     {
35283         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35284         
35285         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35286         
35287         if(this.bgimage.length){
35288             var image = this.el.select('.roo-brick-image-view', true).first();
35289             
35290             image.setWidth(paragraph.getWidth());
35291             
35292             if(this.square){
35293                 image.setHeight(paragraph.getWidth());
35294             }
35295             
35296             this.el.setHeight(image.getHeight());
35297             paragraph.setHeight(image.getHeight());
35298             
35299         }
35300         
35301     },
35302     
35303     enter: function(e, el)
35304     {
35305         e.preventDefault();
35306         
35307         if(this.bgimage.length){
35308             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35309             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35310         }
35311     },
35312     
35313     leave: function(e, el)
35314     {
35315         e.preventDefault();
35316         
35317         if(this.bgimage.length){
35318             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35319             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35320         }
35321     }
35322     
35323 });
35324
35325  
35326
35327  /*
35328  * - LGPL
35329  *
35330  * Number field 
35331  */
35332
35333 /**
35334  * @class Roo.bootstrap.NumberField
35335  * @extends Roo.bootstrap.Input
35336  * Bootstrap NumberField class
35337  * 
35338  * 
35339  * 
35340  * 
35341  * @constructor
35342  * Create a new NumberField
35343  * @param {Object} config The config object
35344  */
35345
35346 Roo.bootstrap.NumberField = function(config){
35347     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35348 };
35349
35350 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35351     
35352     /**
35353      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35354      */
35355     allowDecimals : true,
35356     /**
35357      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35358      */
35359     decimalSeparator : ".",
35360     /**
35361      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35362      */
35363     decimalPrecision : 2,
35364     /**
35365      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35366      */
35367     allowNegative : true,
35368     
35369     /**
35370      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
35371      */
35372     allowZero: true,
35373     /**
35374      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
35375      */
35376     minValue : Number.NEGATIVE_INFINITY,
35377     /**
35378      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
35379      */
35380     maxValue : Number.MAX_VALUE,
35381     /**
35382      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
35383      */
35384     minText : "The minimum value for this field is {0}",
35385     /**
35386      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
35387      */
35388     maxText : "The maximum value for this field is {0}",
35389     /**
35390      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
35391      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
35392      */
35393     nanText : "{0} is not a valid number",
35394     /**
35395      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
35396      */
35397     thousandsDelimiter : false,
35398     /**
35399      * @cfg {String} valueAlign alignment of value
35400      */
35401     valueAlign : "left",
35402
35403     getAutoCreate : function()
35404     {
35405         var hiddenInput = {
35406             tag: 'input',
35407             type: 'hidden',
35408             id: Roo.id(),
35409             cls: 'hidden-number-input'
35410         };
35411         
35412         if (this.name) {
35413             hiddenInput.name = this.name;
35414         }
35415         
35416         this.name = '';
35417         
35418         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
35419         
35420         this.name = hiddenInput.name;
35421         
35422         if(cfg.cn.length > 0) {
35423             cfg.cn.push(hiddenInput);
35424         }
35425         
35426         return cfg;
35427     },
35428
35429     // private
35430     initEvents : function()
35431     {   
35432         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
35433         
35434         var allowed = "0123456789";
35435         
35436         if(this.allowDecimals){
35437             allowed += this.decimalSeparator;
35438         }
35439         
35440         if(this.allowNegative){
35441             allowed += "-";
35442         }
35443         
35444         if(this.thousandsDelimiter) {
35445             allowed += ",";
35446         }
35447         
35448         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
35449         
35450         var keyPress = function(e){
35451             
35452             var k = e.getKey();
35453             
35454             var c = e.getCharCode();
35455             
35456             if(
35457                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
35458                     allowed.indexOf(String.fromCharCode(c)) === -1
35459             ){
35460                 e.stopEvent();
35461                 return;
35462             }
35463             
35464             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
35465                 return;
35466             }
35467             
35468             if(allowed.indexOf(String.fromCharCode(c)) === -1){
35469                 e.stopEvent();
35470             }
35471         };
35472         
35473         this.el.on("keypress", keyPress, this);
35474     },
35475     
35476     validateValue : function(value)
35477     {
35478         
35479         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
35480             return false;
35481         }
35482         
35483         var num = this.parseValue(value);
35484         
35485         if(isNaN(num)){
35486             this.markInvalid(String.format(this.nanText, value));
35487             return false;
35488         }
35489         
35490         if(num < this.minValue){
35491             this.markInvalid(String.format(this.minText, this.minValue));
35492             return false;
35493         }
35494         
35495         if(num > this.maxValue){
35496             this.markInvalid(String.format(this.maxText, this.maxValue));
35497             return false;
35498         }
35499         
35500         return true;
35501     },
35502
35503     getValue : function()
35504     {
35505         var v = this.hiddenEl().getValue();
35506         
35507         return this.fixPrecision(this.parseValue(v));
35508     },
35509
35510     parseValue : function(value)
35511     {
35512         if(this.thousandsDelimiter) {
35513             value += "";
35514             r = new RegExp(",", "g");
35515             value = value.replace(r, "");
35516         }
35517         
35518         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
35519         return isNaN(value) ? '' : value;
35520     },
35521
35522     fixPrecision : function(value)
35523     {
35524         if(this.thousandsDelimiter) {
35525             value += "";
35526             r = new RegExp(",", "g");
35527             value = value.replace(r, "");
35528         }
35529         
35530         var nan = isNaN(value);
35531         
35532         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
35533             return nan ? '' : value;
35534         }
35535         return parseFloat(value).toFixed(this.decimalPrecision);
35536     },
35537
35538     setValue : function(v)
35539     {
35540         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
35541         
35542         this.value = v;
35543         
35544         if(this.rendered){
35545             
35546             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
35547             
35548             this.inputEl().dom.value = (v == '') ? '' :
35549                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
35550             
35551             if(!this.allowZero && v === '0') {
35552                 this.hiddenEl().dom.value = '';
35553                 this.inputEl().dom.value = '';
35554             }
35555             
35556             this.validate();
35557         }
35558     },
35559
35560     decimalPrecisionFcn : function(v)
35561     {
35562         return Math.floor(v);
35563     },
35564
35565     beforeBlur : function()
35566     {
35567         var v = this.parseValue(this.getRawValue());
35568         
35569         if(v || v === 0 || v === ''){
35570             this.setValue(v);
35571         }
35572     },
35573     
35574     hiddenEl : function()
35575     {
35576         return this.el.select('input.hidden-number-input',true).first();
35577     }
35578     
35579 });
35580
35581  
35582
35583 /*
35584 * Licence: LGPL
35585 */
35586
35587 /**
35588  * @class Roo.bootstrap.DocumentSlider
35589  * @extends Roo.bootstrap.Component
35590  * Bootstrap DocumentSlider class
35591  * 
35592  * @constructor
35593  * Create a new DocumentViewer
35594  * @param {Object} config The config object
35595  */
35596
35597 Roo.bootstrap.DocumentSlider = function(config){
35598     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
35599     
35600     this.files = [];
35601     
35602     this.addEvents({
35603         /**
35604          * @event initial
35605          * Fire after initEvent
35606          * @param {Roo.bootstrap.DocumentSlider} this
35607          */
35608         "initial" : true,
35609         /**
35610          * @event update
35611          * Fire after update
35612          * @param {Roo.bootstrap.DocumentSlider} this
35613          */
35614         "update" : true,
35615         /**
35616          * @event click
35617          * Fire after click
35618          * @param {Roo.bootstrap.DocumentSlider} this
35619          */
35620         "click" : true
35621     });
35622 };
35623
35624 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
35625     
35626     files : false,
35627     
35628     indicator : 0,
35629     
35630     getAutoCreate : function()
35631     {
35632         var cfg = {
35633             tag : 'div',
35634             cls : 'roo-document-slider',
35635             cn : [
35636                 {
35637                     tag : 'div',
35638                     cls : 'roo-document-slider-header',
35639                     cn : [
35640                         {
35641                             tag : 'div',
35642                             cls : 'roo-document-slider-header-title'
35643                         }
35644                     ]
35645                 },
35646                 {
35647                     tag : 'div',
35648                     cls : 'roo-document-slider-body',
35649                     cn : [
35650                         {
35651                             tag : 'div',
35652                             cls : 'roo-document-slider-prev',
35653                             cn : [
35654                                 {
35655                                     tag : 'i',
35656                                     cls : 'fa fa-chevron-left'
35657                                 }
35658                             ]
35659                         },
35660                         {
35661                             tag : 'div',
35662                             cls : 'roo-document-slider-thumb',
35663                             cn : [
35664                                 {
35665                                     tag : 'img',
35666                                     cls : 'roo-document-slider-image'
35667                                 }
35668                             ]
35669                         },
35670                         {
35671                             tag : 'div',
35672                             cls : 'roo-document-slider-next',
35673                             cn : [
35674                                 {
35675                                     tag : 'i',
35676                                     cls : 'fa fa-chevron-right'
35677                                 }
35678                             ]
35679                         }
35680                     ]
35681                 }
35682             ]
35683         };
35684         
35685         return cfg;
35686     },
35687     
35688     initEvents : function()
35689     {
35690         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
35691         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
35692         
35693         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
35694         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
35695         
35696         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
35697         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
35698         
35699         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
35700         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
35701         
35702         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
35703         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
35704         
35705         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
35706         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
35707         
35708         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
35709         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
35710         
35711         this.thumbEl.on('click', this.onClick, this);
35712         
35713         this.prevIndicator.on('click', this.prev, this);
35714         
35715         this.nextIndicator.on('click', this.next, this);
35716         
35717     },
35718     
35719     initial : function()
35720     {
35721         if(this.files.length){
35722             this.indicator = 1;
35723             this.update()
35724         }
35725         
35726         this.fireEvent('initial', this);
35727     },
35728     
35729     update : function()
35730     {
35731         this.imageEl.attr('src', this.files[this.indicator - 1]);
35732         
35733         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
35734         
35735         this.prevIndicator.show();
35736         
35737         if(this.indicator == 1){
35738             this.prevIndicator.hide();
35739         }
35740         
35741         this.nextIndicator.show();
35742         
35743         if(this.indicator == this.files.length){
35744             this.nextIndicator.hide();
35745         }
35746         
35747         this.thumbEl.scrollTo('top');
35748         
35749         this.fireEvent('update', this);
35750     },
35751     
35752     onClick : function(e)
35753     {
35754         e.preventDefault();
35755         
35756         this.fireEvent('click', this);
35757     },
35758     
35759     prev : function(e)
35760     {
35761         e.preventDefault();
35762         
35763         this.indicator = Math.max(1, this.indicator - 1);
35764         
35765         this.update();
35766     },
35767     
35768     next : function(e)
35769     {
35770         e.preventDefault();
35771         
35772         this.indicator = Math.min(this.files.length, this.indicator + 1);
35773         
35774         this.update();
35775     }
35776 });
35777 /*
35778  * - LGPL
35779  *
35780  * RadioSet
35781  *
35782  *
35783  */
35784
35785 /**
35786  * @class Roo.bootstrap.RadioSet
35787  * @extends Roo.bootstrap.Input
35788  * Bootstrap RadioSet class
35789  * @cfg {String} indicatorpos (left|right) default left
35790  * @cfg {Boolean} inline (true|false) inline the element (default true)
35791  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
35792  * @constructor
35793  * Create a new RadioSet
35794  * @param {Object} config The config object
35795  */
35796
35797 Roo.bootstrap.RadioSet = function(config){
35798     
35799     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
35800     
35801     this.radioes = [];
35802     
35803     Roo.bootstrap.RadioSet.register(this);
35804     
35805     this.addEvents({
35806         /**
35807         * @event check
35808         * Fires when the element is checked or unchecked.
35809         * @param {Roo.bootstrap.RadioSet} this This radio
35810         * @param {Roo.bootstrap.Radio} item The checked item
35811         */
35812        check : true,
35813        /**
35814         * @event click
35815         * Fires when the element is click.
35816         * @param {Roo.bootstrap.RadioSet} this This radio set
35817         * @param {Roo.bootstrap.Radio} item The checked item
35818         * @param {Roo.EventObject} e The event object
35819         */
35820        click : true
35821     });
35822     
35823 };
35824
35825 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
35826
35827     radioes : false,
35828     
35829     inline : true,
35830     
35831     weight : '',
35832     
35833     indicatorpos : 'left',
35834     
35835     getAutoCreate : function()
35836     {
35837         var label = {
35838             tag : 'label',
35839             cls : 'roo-radio-set-label',
35840             cn : [
35841                 {
35842                     tag : 'span',
35843                     html : this.fieldLabel
35844                 }
35845             ]
35846         };
35847         if (Roo.bootstrap.version == 3) {
35848             
35849             
35850             if(this.indicatorpos == 'left'){
35851                 label.cn.unshift({
35852                     tag : 'i',
35853                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
35854                     tooltip : 'This field is required'
35855                 });
35856             } else {
35857                 label.cn.push({
35858                     tag : 'i',
35859                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
35860                     tooltip : 'This field is required'
35861                 });
35862             }
35863         }
35864         var items = {
35865             tag : 'div',
35866             cls : 'roo-radio-set-items'
35867         };
35868         
35869         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
35870         
35871         if (align === 'left' && this.fieldLabel.length) {
35872             
35873             items = {
35874                 cls : "roo-radio-set-right", 
35875                 cn: [
35876                     items
35877                 ]
35878             };
35879             
35880             if(this.labelWidth > 12){
35881                 label.style = "width: " + this.labelWidth + 'px';
35882             }
35883             
35884             if(this.labelWidth < 13 && this.labelmd == 0){
35885                 this.labelmd = this.labelWidth;
35886             }
35887             
35888             if(this.labellg > 0){
35889                 label.cls += ' col-lg-' + this.labellg;
35890                 items.cls += ' col-lg-' + (12 - this.labellg);
35891             }
35892             
35893             if(this.labelmd > 0){
35894                 label.cls += ' col-md-' + this.labelmd;
35895                 items.cls += ' col-md-' + (12 - this.labelmd);
35896             }
35897             
35898             if(this.labelsm > 0){
35899                 label.cls += ' col-sm-' + this.labelsm;
35900                 items.cls += ' col-sm-' + (12 - this.labelsm);
35901             }
35902             
35903             if(this.labelxs > 0){
35904                 label.cls += ' col-xs-' + this.labelxs;
35905                 items.cls += ' col-xs-' + (12 - this.labelxs);
35906             }
35907         }
35908         
35909         var cfg = {
35910             tag : 'div',
35911             cls : 'roo-radio-set',
35912             cn : [
35913                 {
35914                     tag : 'input',
35915                     cls : 'roo-radio-set-input',
35916                     type : 'hidden',
35917                     name : this.name,
35918                     value : this.value ? this.value :  ''
35919                 },
35920                 label,
35921                 items
35922             ]
35923         };
35924         
35925         if(this.weight.length){
35926             cfg.cls += ' roo-radio-' + this.weight;
35927         }
35928         
35929         if(this.inline) {
35930             cfg.cls += ' roo-radio-set-inline';
35931         }
35932         
35933         var settings=this;
35934         ['xs','sm','md','lg'].map(function(size){
35935             if (settings[size]) {
35936                 cfg.cls += ' col-' + size + '-' + settings[size];
35937             }
35938         });
35939         
35940         return cfg;
35941         
35942     },
35943
35944     initEvents : function()
35945     {
35946         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
35947         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
35948         
35949         if(!this.fieldLabel.length){
35950             this.labelEl.hide();
35951         }
35952         
35953         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
35954         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
35955         
35956         this.indicator = this.indicatorEl();
35957         
35958         if(this.indicator){
35959             this.indicator.addClass('invisible');
35960         }
35961         
35962         this.originalValue = this.getValue();
35963         
35964     },
35965     
35966     inputEl: function ()
35967     {
35968         return this.el.select('.roo-radio-set-input', true).first();
35969     },
35970     
35971     getChildContainer : function()
35972     {
35973         return this.itemsEl;
35974     },
35975     
35976     register : function(item)
35977     {
35978         this.radioes.push(item);
35979         
35980     },
35981     
35982     validate : function()
35983     {   
35984         if(this.getVisibilityEl().hasClass('hidden')){
35985             return true;
35986         }
35987         
35988         var valid = false;
35989         
35990         Roo.each(this.radioes, function(i){
35991             if(!i.checked){
35992                 return;
35993             }
35994             
35995             valid = true;
35996             return false;
35997         });
35998         
35999         if(this.allowBlank) {
36000             return true;
36001         }
36002         
36003         if(this.disabled || valid){
36004             this.markValid();
36005             return true;
36006         }
36007         
36008         this.markInvalid();
36009         return false;
36010         
36011     },
36012     
36013     markValid : function()
36014     {
36015         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36016             this.indicatorEl().removeClass('visible');
36017             this.indicatorEl().addClass('invisible');
36018         }
36019         
36020         
36021         if (Roo.bootstrap.version == 3) {
36022             this.el.removeClass([this.invalidClass, this.validClass]);
36023             this.el.addClass(this.validClass);
36024         } else {
36025             this.el.removeClass(['is-invalid','is-valid']);
36026             this.el.addClass(['is-valid']);
36027         }
36028         this.fireEvent('valid', this);
36029     },
36030     
36031     markInvalid : function(msg)
36032     {
36033         if(this.allowBlank || this.disabled){
36034             return;
36035         }
36036         
36037         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36038             this.indicatorEl().removeClass('invisible');
36039             this.indicatorEl().addClass('visible');
36040         }
36041         if (Roo.bootstrap.version == 3) {
36042             this.el.removeClass([this.invalidClass, this.validClass]);
36043             this.el.addClass(this.invalidClass);
36044         } else {
36045             this.el.removeClass(['is-invalid','is-valid']);
36046             this.el.addClass(['is-invalid']);
36047         }
36048         
36049         this.fireEvent('invalid', this, msg);
36050         
36051     },
36052     
36053     setValue : function(v, suppressEvent)
36054     {   
36055         if(this.value === v){
36056             return;
36057         }
36058         
36059         this.value = v;
36060         
36061         if(this.rendered){
36062             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36063         }
36064         
36065         Roo.each(this.radioes, function(i){
36066             i.checked = false;
36067             i.el.removeClass('checked');
36068         });
36069         
36070         Roo.each(this.radioes, function(i){
36071             
36072             if(i.value === v || i.value.toString() === v.toString()){
36073                 i.checked = true;
36074                 i.el.addClass('checked');
36075                 
36076                 if(suppressEvent !== true){
36077                     this.fireEvent('check', this, i);
36078                 }
36079                 
36080                 return false;
36081             }
36082             
36083         }, this);
36084         
36085         this.validate();
36086     },
36087     
36088     clearInvalid : function(){
36089         
36090         if(!this.el || this.preventMark){
36091             return;
36092         }
36093         
36094         this.el.removeClass([this.invalidClass]);
36095         
36096         this.fireEvent('valid', this);
36097     }
36098     
36099 });
36100
36101 Roo.apply(Roo.bootstrap.RadioSet, {
36102     
36103     groups: {},
36104     
36105     register : function(set)
36106     {
36107         this.groups[set.name] = set;
36108     },
36109     
36110     get: function(name) 
36111     {
36112         if (typeof(this.groups[name]) == 'undefined') {
36113             return false;
36114         }
36115         
36116         return this.groups[name] ;
36117     }
36118     
36119 });
36120 /*
36121  * Based on:
36122  * Ext JS Library 1.1.1
36123  * Copyright(c) 2006-2007, Ext JS, LLC.
36124  *
36125  * Originally Released Under LGPL - original licence link has changed is not relivant.
36126  *
36127  * Fork - LGPL
36128  * <script type="text/javascript">
36129  */
36130
36131
36132 /**
36133  * @class Roo.bootstrap.SplitBar
36134  * @extends Roo.util.Observable
36135  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36136  * <br><br>
36137  * Usage:
36138  * <pre><code>
36139 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36140                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36141 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36142 split.minSize = 100;
36143 split.maxSize = 600;
36144 split.animate = true;
36145 split.on('moved', splitterMoved);
36146 </code></pre>
36147  * @constructor
36148  * Create a new SplitBar
36149  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
36150  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
36151  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36152  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
36153                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36154                         position of the SplitBar).
36155  */
36156 Roo.bootstrap.SplitBar = function(cfg){
36157     
36158     /** @private */
36159     
36160     //{
36161     //  dragElement : elm
36162     //  resizingElement: el,
36163         // optional..
36164     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36165     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
36166         // existingProxy ???
36167     //}
36168     
36169     this.el = Roo.get(cfg.dragElement, true);
36170     this.el.dom.unselectable = "on";
36171     /** @private */
36172     this.resizingEl = Roo.get(cfg.resizingElement, true);
36173
36174     /**
36175      * @private
36176      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36177      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36178      * @type Number
36179      */
36180     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36181     
36182     /**
36183      * The minimum size of the resizing element. (Defaults to 0)
36184      * @type Number
36185      */
36186     this.minSize = 0;
36187     
36188     /**
36189      * The maximum size of the resizing element. (Defaults to 2000)
36190      * @type Number
36191      */
36192     this.maxSize = 2000;
36193     
36194     /**
36195      * Whether to animate the transition to the new size
36196      * @type Boolean
36197      */
36198     this.animate = false;
36199     
36200     /**
36201      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36202      * @type Boolean
36203      */
36204     this.useShim = false;
36205     
36206     /** @private */
36207     this.shim = null;
36208     
36209     if(!cfg.existingProxy){
36210         /** @private */
36211         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36212     }else{
36213         this.proxy = Roo.get(cfg.existingProxy).dom;
36214     }
36215     /** @private */
36216     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36217     
36218     /** @private */
36219     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36220     
36221     /** @private */
36222     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36223     
36224     /** @private */
36225     this.dragSpecs = {};
36226     
36227     /**
36228      * @private The adapter to use to positon and resize elements
36229      */
36230     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36231     this.adapter.init(this);
36232     
36233     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36234         /** @private */
36235         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36236         this.el.addClass("roo-splitbar-h");
36237     }else{
36238         /** @private */
36239         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36240         this.el.addClass("roo-splitbar-v");
36241     }
36242     
36243     this.addEvents({
36244         /**
36245          * @event resize
36246          * Fires when the splitter is moved (alias for {@link #event-moved})
36247          * @param {Roo.bootstrap.SplitBar} this
36248          * @param {Number} newSize the new width or height
36249          */
36250         "resize" : true,
36251         /**
36252          * @event moved
36253          * Fires when the splitter is moved
36254          * @param {Roo.bootstrap.SplitBar} this
36255          * @param {Number} newSize the new width or height
36256          */
36257         "moved" : true,
36258         /**
36259          * @event beforeresize
36260          * Fires before the splitter is dragged
36261          * @param {Roo.bootstrap.SplitBar} this
36262          */
36263         "beforeresize" : true,
36264
36265         "beforeapply" : true
36266     });
36267
36268     Roo.util.Observable.call(this);
36269 };
36270
36271 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36272     onStartProxyDrag : function(x, y){
36273         this.fireEvent("beforeresize", this);
36274         if(!this.overlay){
36275             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
36276             o.unselectable();
36277             o.enableDisplayMode("block");
36278             // all splitbars share the same overlay
36279             Roo.bootstrap.SplitBar.prototype.overlay = o;
36280         }
36281         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36282         this.overlay.show();
36283         Roo.get(this.proxy).setDisplayed("block");
36284         var size = this.adapter.getElementSize(this);
36285         this.activeMinSize = this.getMinimumSize();;
36286         this.activeMaxSize = this.getMaximumSize();;
36287         var c1 = size - this.activeMinSize;
36288         var c2 = Math.max(this.activeMaxSize - size, 0);
36289         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36290             this.dd.resetConstraints();
36291             this.dd.setXConstraint(
36292                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
36293                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36294             );
36295             this.dd.setYConstraint(0, 0);
36296         }else{
36297             this.dd.resetConstraints();
36298             this.dd.setXConstraint(0, 0);
36299             this.dd.setYConstraint(
36300                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
36301                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36302             );
36303          }
36304         this.dragSpecs.startSize = size;
36305         this.dragSpecs.startPoint = [x, y];
36306         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36307     },
36308     
36309     /** 
36310      * @private Called after the drag operation by the DDProxy
36311      */
36312     onEndProxyDrag : function(e){
36313         Roo.get(this.proxy).setDisplayed(false);
36314         var endPoint = Roo.lib.Event.getXY(e);
36315         if(this.overlay){
36316             this.overlay.hide();
36317         }
36318         var newSize;
36319         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36320             newSize = this.dragSpecs.startSize + 
36321                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36322                     endPoint[0] - this.dragSpecs.startPoint[0] :
36323                     this.dragSpecs.startPoint[0] - endPoint[0]
36324                 );
36325         }else{
36326             newSize = this.dragSpecs.startSize + 
36327                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36328                     endPoint[1] - this.dragSpecs.startPoint[1] :
36329                     this.dragSpecs.startPoint[1] - endPoint[1]
36330                 );
36331         }
36332         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36333         if(newSize != this.dragSpecs.startSize){
36334             if(this.fireEvent('beforeapply', this, newSize) !== false){
36335                 this.adapter.setElementSize(this, newSize);
36336                 this.fireEvent("moved", this, newSize);
36337                 this.fireEvent("resize", this, newSize);
36338             }
36339         }
36340     },
36341     
36342     /**
36343      * Get the adapter this SplitBar uses
36344      * @return The adapter object
36345      */
36346     getAdapter : function(){
36347         return this.adapter;
36348     },
36349     
36350     /**
36351      * Set the adapter this SplitBar uses
36352      * @param {Object} adapter A SplitBar adapter object
36353      */
36354     setAdapter : function(adapter){
36355         this.adapter = adapter;
36356         this.adapter.init(this);
36357     },
36358     
36359     /**
36360      * Gets the minimum size for the resizing element
36361      * @return {Number} The minimum size
36362      */
36363     getMinimumSize : function(){
36364         return this.minSize;
36365     },
36366     
36367     /**
36368      * Sets the minimum size for the resizing element
36369      * @param {Number} minSize The minimum size
36370      */
36371     setMinimumSize : function(minSize){
36372         this.minSize = minSize;
36373     },
36374     
36375     /**
36376      * Gets the maximum size for the resizing element
36377      * @return {Number} The maximum size
36378      */
36379     getMaximumSize : function(){
36380         return this.maxSize;
36381     },
36382     
36383     /**
36384      * Sets the maximum size for the resizing element
36385      * @param {Number} maxSize The maximum size
36386      */
36387     setMaximumSize : function(maxSize){
36388         this.maxSize = maxSize;
36389     },
36390     
36391     /**
36392      * Sets the initialize size for the resizing element
36393      * @param {Number} size The initial size
36394      */
36395     setCurrentSize : function(size){
36396         var oldAnimate = this.animate;
36397         this.animate = false;
36398         this.adapter.setElementSize(this, size);
36399         this.animate = oldAnimate;
36400     },
36401     
36402     /**
36403      * Destroy this splitbar. 
36404      * @param {Boolean} removeEl True to remove the element
36405      */
36406     destroy : function(removeEl){
36407         if(this.shim){
36408             this.shim.remove();
36409         }
36410         this.dd.unreg();
36411         this.proxy.parentNode.removeChild(this.proxy);
36412         if(removeEl){
36413             this.el.remove();
36414         }
36415     }
36416 });
36417
36418 /**
36419  * @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.
36420  */
36421 Roo.bootstrap.SplitBar.createProxy = function(dir){
36422     var proxy = new Roo.Element(document.createElement("div"));
36423     proxy.unselectable();
36424     var cls = 'roo-splitbar-proxy';
36425     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
36426     document.body.appendChild(proxy.dom);
36427     return proxy.dom;
36428 };
36429
36430 /** 
36431  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
36432  * Default Adapter. It assumes the splitter and resizing element are not positioned
36433  * elements and only gets/sets the width of the element. Generally used for table based layouts.
36434  */
36435 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
36436 };
36437
36438 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
36439     // do nothing for now
36440     init : function(s){
36441     
36442     },
36443     /**
36444      * Called before drag operations to get the current size of the resizing element. 
36445      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36446      */
36447      getElementSize : function(s){
36448         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36449             return s.resizingEl.getWidth();
36450         }else{
36451             return s.resizingEl.getHeight();
36452         }
36453     },
36454     
36455     /**
36456      * Called after drag operations to set the size of the resizing element.
36457      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36458      * @param {Number} newSize The new size to set
36459      * @param {Function} onComplete A function to be invoked when resizing is complete
36460      */
36461     setElementSize : function(s, newSize, onComplete){
36462         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36463             if(!s.animate){
36464                 s.resizingEl.setWidth(newSize);
36465                 if(onComplete){
36466                     onComplete(s, newSize);
36467                 }
36468             }else{
36469                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
36470             }
36471         }else{
36472             
36473             if(!s.animate){
36474                 s.resizingEl.setHeight(newSize);
36475                 if(onComplete){
36476                     onComplete(s, newSize);
36477                 }
36478             }else{
36479                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
36480             }
36481         }
36482     }
36483 };
36484
36485 /** 
36486  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
36487  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
36488  * Adapter that  moves the splitter element to align with the resized sizing element. 
36489  * Used with an absolute positioned SplitBar.
36490  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
36491  * document.body, make sure you assign an id to the body element.
36492  */
36493 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
36494     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36495     this.container = Roo.get(container);
36496 };
36497
36498 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
36499     init : function(s){
36500         this.basic.init(s);
36501     },
36502     
36503     getElementSize : function(s){
36504         return this.basic.getElementSize(s);
36505     },
36506     
36507     setElementSize : function(s, newSize, onComplete){
36508         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
36509     },
36510     
36511     moveSplitter : function(s){
36512         var yes = Roo.bootstrap.SplitBar;
36513         switch(s.placement){
36514             case yes.LEFT:
36515                 s.el.setX(s.resizingEl.getRight());
36516                 break;
36517             case yes.RIGHT:
36518                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
36519                 break;
36520             case yes.TOP:
36521                 s.el.setY(s.resizingEl.getBottom());
36522                 break;
36523             case yes.BOTTOM:
36524                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
36525                 break;
36526         }
36527     }
36528 };
36529
36530 /**
36531  * Orientation constant - Create a vertical SplitBar
36532  * @static
36533  * @type Number
36534  */
36535 Roo.bootstrap.SplitBar.VERTICAL = 1;
36536
36537 /**
36538  * Orientation constant - Create a horizontal SplitBar
36539  * @static
36540  * @type Number
36541  */
36542 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
36543
36544 /**
36545  * Placement constant - The resizing element is to the left of the splitter element
36546  * @static
36547  * @type Number
36548  */
36549 Roo.bootstrap.SplitBar.LEFT = 1;
36550
36551 /**
36552  * Placement constant - The resizing element is to the right of the splitter element
36553  * @static
36554  * @type Number
36555  */
36556 Roo.bootstrap.SplitBar.RIGHT = 2;
36557
36558 /**
36559  * Placement constant - The resizing element is positioned above the splitter element
36560  * @static
36561  * @type Number
36562  */
36563 Roo.bootstrap.SplitBar.TOP = 3;
36564
36565 /**
36566  * Placement constant - The resizing element is positioned under splitter element
36567  * @static
36568  * @type Number
36569  */
36570 Roo.bootstrap.SplitBar.BOTTOM = 4;
36571 Roo.namespace("Roo.bootstrap.layout");/*
36572  * Based on:
36573  * Ext JS Library 1.1.1
36574  * Copyright(c) 2006-2007, Ext JS, LLC.
36575  *
36576  * Originally Released Under LGPL - original licence link has changed is not relivant.
36577  *
36578  * Fork - LGPL
36579  * <script type="text/javascript">
36580  */
36581
36582 /**
36583  * @class Roo.bootstrap.layout.Manager
36584  * @extends Roo.bootstrap.Component
36585  * Base class for layout managers.
36586  */
36587 Roo.bootstrap.layout.Manager = function(config)
36588 {
36589     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
36590
36591
36592
36593
36594
36595     /** false to disable window resize monitoring @type Boolean */
36596     this.monitorWindowResize = true;
36597     this.regions = {};
36598     this.addEvents({
36599         /**
36600          * @event layout
36601          * Fires when a layout is performed.
36602          * @param {Roo.LayoutManager} this
36603          */
36604         "layout" : true,
36605         /**
36606          * @event regionresized
36607          * Fires when the user resizes a region.
36608          * @param {Roo.LayoutRegion} region The resized region
36609          * @param {Number} newSize The new size (width for east/west, height for north/south)
36610          */
36611         "regionresized" : true,
36612         /**
36613          * @event regioncollapsed
36614          * Fires when a region is collapsed.
36615          * @param {Roo.LayoutRegion} region The collapsed region
36616          */
36617         "regioncollapsed" : true,
36618         /**
36619          * @event regionexpanded
36620          * Fires when a region is expanded.
36621          * @param {Roo.LayoutRegion} region The expanded region
36622          */
36623         "regionexpanded" : true
36624     });
36625     this.updating = false;
36626
36627     if (config.el) {
36628         this.el = Roo.get(config.el);
36629         this.initEvents();
36630     }
36631
36632 };
36633
36634 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
36635
36636
36637     regions : null,
36638
36639     monitorWindowResize : true,
36640
36641
36642     updating : false,
36643
36644
36645     onRender : function(ct, position)
36646     {
36647         if(!this.el){
36648             this.el = Roo.get(ct);
36649             this.initEvents();
36650         }
36651         //this.fireEvent('render',this);
36652     },
36653
36654
36655     initEvents: function()
36656     {
36657
36658
36659         // ie scrollbar fix
36660         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
36661             document.body.scroll = "no";
36662         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
36663             this.el.position('relative');
36664         }
36665         this.id = this.el.id;
36666         this.el.addClass("roo-layout-container");
36667         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
36668         if(this.el.dom != document.body ) {
36669             this.el.on('resize', this.layout,this);
36670             this.el.on('show', this.layout,this);
36671         }
36672
36673     },
36674
36675     /**
36676      * Returns true if this layout is currently being updated
36677      * @return {Boolean}
36678      */
36679     isUpdating : function(){
36680         return this.updating;
36681     },
36682
36683     /**
36684      * Suspend the LayoutManager from doing auto-layouts while
36685      * making multiple add or remove calls
36686      */
36687     beginUpdate : function(){
36688         this.updating = true;
36689     },
36690
36691     /**
36692      * Restore auto-layouts and optionally disable the manager from performing a layout
36693      * @param {Boolean} noLayout true to disable a layout update
36694      */
36695     endUpdate : function(noLayout){
36696         this.updating = false;
36697         if(!noLayout){
36698             this.layout();
36699         }
36700     },
36701
36702     layout: function(){
36703         // abstract...
36704     },
36705
36706     onRegionResized : function(region, newSize){
36707         this.fireEvent("regionresized", region, newSize);
36708         this.layout();
36709     },
36710
36711     onRegionCollapsed : function(region){
36712         this.fireEvent("regioncollapsed", region);
36713     },
36714
36715     onRegionExpanded : function(region){
36716         this.fireEvent("regionexpanded", region);
36717     },
36718
36719     /**
36720      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
36721      * performs box-model adjustments.
36722      * @return {Object} The size as an object {width: (the width), height: (the height)}
36723      */
36724     getViewSize : function()
36725     {
36726         var size;
36727         if(this.el.dom != document.body){
36728             size = this.el.getSize();
36729         }else{
36730             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
36731         }
36732         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
36733         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
36734         return size;
36735     },
36736
36737     /**
36738      * Returns the Element this layout is bound to.
36739      * @return {Roo.Element}
36740      */
36741     getEl : function(){
36742         return this.el;
36743     },
36744
36745     /**
36746      * Returns the specified region.
36747      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
36748      * @return {Roo.LayoutRegion}
36749      */
36750     getRegion : function(target){
36751         return this.regions[target.toLowerCase()];
36752     },
36753
36754     onWindowResize : function(){
36755         if(this.monitorWindowResize){
36756             this.layout();
36757         }
36758     }
36759 });
36760 /*
36761  * Based on:
36762  * Ext JS Library 1.1.1
36763  * Copyright(c) 2006-2007, Ext JS, LLC.
36764  *
36765  * Originally Released Under LGPL - original licence link has changed is not relivant.
36766  *
36767  * Fork - LGPL
36768  * <script type="text/javascript">
36769  */
36770 /**
36771  * @class Roo.bootstrap.layout.Border
36772  * @extends Roo.bootstrap.layout.Manager
36773  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
36774  * please see: examples/bootstrap/nested.html<br><br>
36775  
36776 <b>The container the layout is rendered into can be either the body element or any other element.
36777 If it is not the body element, the container needs to either be an absolute positioned element,
36778 or you will need to add "position:relative" to the css of the container.  You will also need to specify
36779 the container size if it is not the body element.</b>
36780
36781 * @constructor
36782 * Create a new Border
36783 * @param {Object} config Configuration options
36784  */
36785 Roo.bootstrap.layout.Border = function(config){
36786     config = config || {};
36787     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
36788     
36789     
36790     
36791     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
36792         if(config[region]){
36793             config[region].region = region;
36794             this.addRegion(config[region]);
36795         }
36796     },this);
36797     
36798 };
36799
36800 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
36801
36802 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
36803     
36804     parent : false, // this might point to a 'nest' or a ???
36805     
36806     /**
36807      * Creates and adds a new region if it doesn't already exist.
36808      * @param {String} target The target region key (north, south, east, west or center).
36809      * @param {Object} config The regions config object
36810      * @return {BorderLayoutRegion} The new region
36811      */
36812     addRegion : function(config)
36813     {
36814         if(!this.regions[config.region]){
36815             var r = this.factory(config);
36816             this.bindRegion(r);
36817         }
36818         return this.regions[config.region];
36819     },
36820
36821     // private (kinda)
36822     bindRegion : function(r){
36823         this.regions[r.config.region] = r;
36824         
36825         r.on("visibilitychange",    this.layout, this);
36826         r.on("paneladded",          this.layout, this);
36827         r.on("panelremoved",        this.layout, this);
36828         r.on("invalidated",         this.layout, this);
36829         r.on("resized",             this.onRegionResized, this);
36830         r.on("collapsed",           this.onRegionCollapsed, this);
36831         r.on("expanded",            this.onRegionExpanded, this);
36832     },
36833
36834     /**
36835      * Performs a layout update.
36836      */
36837     layout : function()
36838     {
36839         if(this.updating) {
36840             return;
36841         }
36842         
36843         // render all the rebions if they have not been done alreayd?
36844         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
36845             if(this.regions[region] && !this.regions[region].bodyEl){
36846                 this.regions[region].onRender(this.el)
36847             }
36848         },this);
36849         
36850         var size = this.getViewSize();
36851         var w = size.width;
36852         var h = size.height;
36853         var centerW = w;
36854         var centerH = h;
36855         var centerY = 0;
36856         var centerX = 0;
36857         //var x = 0, y = 0;
36858
36859         var rs = this.regions;
36860         var north = rs["north"];
36861         var south = rs["south"]; 
36862         var west = rs["west"];
36863         var east = rs["east"];
36864         var center = rs["center"];
36865         //if(this.hideOnLayout){ // not supported anymore
36866             //c.el.setStyle("display", "none");
36867         //}
36868         if(north && north.isVisible()){
36869             var b = north.getBox();
36870             var m = north.getMargins();
36871             b.width = w - (m.left+m.right);
36872             b.x = m.left;
36873             b.y = m.top;
36874             centerY = b.height + b.y + m.bottom;
36875             centerH -= centerY;
36876             north.updateBox(this.safeBox(b));
36877         }
36878         if(south && south.isVisible()){
36879             var b = south.getBox();
36880             var m = south.getMargins();
36881             b.width = w - (m.left+m.right);
36882             b.x = m.left;
36883             var totalHeight = (b.height + m.top + m.bottom);
36884             b.y = h - totalHeight + m.top;
36885             centerH -= totalHeight;
36886             south.updateBox(this.safeBox(b));
36887         }
36888         if(west && west.isVisible()){
36889             var b = west.getBox();
36890             var m = west.getMargins();
36891             b.height = centerH - (m.top+m.bottom);
36892             b.x = m.left;
36893             b.y = centerY + m.top;
36894             var totalWidth = (b.width + m.left + m.right);
36895             centerX += totalWidth;
36896             centerW -= totalWidth;
36897             west.updateBox(this.safeBox(b));
36898         }
36899         if(east && east.isVisible()){
36900             var b = east.getBox();
36901             var m = east.getMargins();
36902             b.height = centerH - (m.top+m.bottom);
36903             var totalWidth = (b.width + m.left + m.right);
36904             b.x = w - totalWidth + m.left;
36905             b.y = centerY + m.top;
36906             centerW -= totalWidth;
36907             east.updateBox(this.safeBox(b));
36908         }
36909         if(center){
36910             var m = center.getMargins();
36911             var centerBox = {
36912                 x: centerX + m.left,
36913                 y: centerY + m.top,
36914                 width: centerW - (m.left+m.right),
36915                 height: centerH - (m.top+m.bottom)
36916             };
36917             //if(this.hideOnLayout){
36918                 //center.el.setStyle("display", "block");
36919             //}
36920             center.updateBox(this.safeBox(centerBox));
36921         }
36922         this.el.repaint();
36923         this.fireEvent("layout", this);
36924     },
36925
36926     // private
36927     safeBox : function(box){
36928         box.width = Math.max(0, box.width);
36929         box.height = Math.max(0, box.height);
36930         return box;
36931     },
36932
36933     /**
36934      * Adds a ContentPanel (or subclass) to this layout.
36935      * @param {String} target The target region key (north, south, east, west or center).
36936      * @param {Roo.ContentPanel} panel The panel to add
36937      * @return {Roo.ContentPanel} The added panel
36938      */
36939     add : function(target, panel){
36940          
36941         target = target.toLowerCase();
36942         return this.regions[target].add(panel);
36943     },
36944
36945     /**
36946      * Remove a ContentPanel (or subclass) to this layout.
36947      * @param {String} target The target region key (north, south, east, west or center).
36948      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
36949      * @return {Roo.ContentPanel} The removed panel
36950      */
36951     remove : function(target, panel){
36952         target = target.toLowerCase();
36953         return this.regions[target].remove(panel);
36954     },
36955
36956     /**
36957      * Searches all regions for a panel with the specified id
36958      * @param {String} panelId
36959      * @return {Roo.ContentPanel} The panel or null if it wasn't found
36960      */
36961     findPanel : function(panelId){
36962         var rs = this.regions;
36963         for(var target in rs){
36964             if(typeof rs[target] != "function"){
36965                 var p = rs[target].getPanel(panelId);
36966                 if(p){
36967                     return p;
36968                 }
36969             }
36970         }
36971         return null;
36972     },
36973
36974     /**
36975      * Searches all regions for a panel with the specified id and activates (shows) it.
36976      * @param {String/ContentPanel} panelId The panels id or the panel itself
36977      * @return {Roo.ContentPanel} The shown panel or null
36978      */
36979     showPanel : function(panelId) {
36980       var rs = this.regions;
36981       for(var target in rs){
36982          var r = rs[target];
36983          if(typeof r != "function"){
36984             if(r.hasPanel(panelId)){
36985                return r.showPanel(panelId);
36986             }
36987          }
36988       }
36989       return null;
36990    },
36991
36992    /**
36993      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
36994      * @param {Roo.state.Provider} provider (optional) An alternate state provider
36995      */
36996    /*
36997     restoreState : function(provider){
36998         if(!provider){
36999             provider = Roo.state.Manager;
37000         }
37001         var sm = new Roo.LayoutStateManager();
37002         sm.init(this, provider);
37003     },
37004 */
37005  
37006  
37007     /**
37008      * Adds a xtype elements to the layout.
37009      * <pre><code>
37010
37011 layout.addxtype({
37012        xtype : 'ContentPanel',
37013        region: 'west',
37014        items: [ .... ]
37015    }
37016 );
37017
37018 layout.addxtype({
37019         xtype : 'NestedLayoutPanel',
37020         region: 'west',
37021         layout: {
37022            center: { },
37023            west: { }   
37024         },
37025         items : [ ... list of content panels or nested layout panels.. ]
37026    }
37027 );
37028 </code></pre>
37029      * @param {Object} cfg Xtype definition of item to add.
37030      */
37031     addxtype : function(cfg)
37032     {
37033         // basically accepts a pannel...
37034         // can accept a layout region..!?!?
37035         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37036         
37037         
37038         // theory?  children can only be panels??
37039         
37040         //if (!cfg.xtype.match(/Panel$/)) {
37041         //    return false;
37042         //}
37043         var ret = false;
37044         
37045         if (typeof(cfg.region) == 'undefined') {
37046             Roo.log("Failed to add Panel, region was not set");
37047             Roo.log(cfg);
37048             return false;
37049         }
37050         var region = cfg.region;
37051         delete cfg.region;
37052         
37053           
37054         var xitems = [];
37055         if (cfg.items) {
37056             xitems = cfg.items;
37057             delete cfg.items;
37058         }
37059         var nb = false;
37060         
37061         if ( region == 'center') {
37062             Roo.log("Center: " + cfg.title);
37063         }
37064         
37065         
37066         switch(cfg.xtype) 
37067         {
37068             case 'Content':  // ContentPanel (el, cfg)
37069             case 'Scroll':  // ContentPanel (el, cfg)
37070             case 'View': 
37071                 cfg.autoCreate = cfg.autoCreate || true;
37072                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37073                 //} else {
37074                 //    var el = this.el.createChild();
37075                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37076                 //}
37077                 
37078                 this.add(region, ret);
37079                 break;
37080             
37081             /*
37082             case 'TreePanel': // our new panel!
37083                 cfg.el = this.el.createChild();
37084                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37085                 this.add(region, ret);
37086                 break;
37087             */
37088             
37089             case 'Nest': 
37090                 // create a new Layout (which is  a Border Layout...
37091                 
37092                 var clayout = cfg.layout;
37093                 clayout.el  = this.el.createChild();
37094                 clayout.items   = clayout.items  || [];
37095                 
37096                 delete cfg.layout;
37097                 
37098                 // replace this exitems with the clayout ones..
37099                 xitems = clayout.items;
37100                  
37101                 // force background off if it's in center...
37102                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37103                     cfg.background = false;
37104                 }
37105                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
37106                 
37107                 
37108                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37109                 //console.log('adding nested layout panel '  + cfg.toSource());
37110                 this.add(region, ret);
37111                 nb = {}; /// find first...
37112                 break;
37113             
37114             case 'Grid':
37115                 
37116                 // needs grid and region
37117                 
37118                 //var el = this.getRegion(region).el.createChild();
37119                 /*
37120                  *var el = this.el.createChild();
37121                 // create the grid first...
37122                 cfg.grid.container = el;
37123                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37124                 */
37125                 
37126                 if (region == 'center' && this.active ) {
37127                     cfg.background = false;
37128                 }
37129                 
37130                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37131                 
37132                 this.add(region, ret);
37133                 /*
37134                 if (cfg.background) {
37135                     // render grid on panel activation (if panel background)
37136                     ret.on('activate', function(gp) {
37137                         if (!gp.grid.rendered) {
37138                     //        gp.grid.render(el);
37139                         }
37140                     });
37141                 } else {
37142                   //  cfg.grid.render(el);
37143                 }
37144                 */
37145                 break;
37146            
37147            
37148             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37149                 // it was the old xcomponent building that caused this before.
37150                 // espeically if border is the top element in the tree.
37151                 ret = this;
37152                 break; 
37153                 
37154                     
37155                 
37156                 
37157                 
37158             default:
37159                 /*
37160                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37161                     
37162                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37163                     this.add(region, ret);
37164                 } else {
37165                 */
37166                     Roo.log(cfg);
37167                     throw "Can not add '" + cfg.xtype + "' to Border";
37168                     return null;
37169              
37170                                 
37171              
37172         }
37173         this.beginUpdate();
37174         // add children..
37175         var region = '';
37176         var abn = {};
37177         Roo.each(xitems, function(i)  {
37178             region = nb && i.region ? i.region : false;
37179             
37180             var add = ret.addxtype(i);
37181            
37182             if (region) {
37183                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37184                 if (!i.background) {
37185                     abn[region] = nb[region] ;
37186                 }
37187             }
37188             
37189         });
37190         this.endUpdate();
37191
37192         // make the last non-background panel active..
37193         //if (nb) { Roo.log(abn); }
37194         if (nb) {
37195             
37196             for(var r in abn) {
37197                 region = this.getRegion(r);
37198                 if (region) {
37199                     // tried using nb[r], but it does not work..
37200                      
37201                     region.showPanel(abn[r]);
37202                    
37203                 }
37204             }
37205         }
37206         return ret;
37207         
37208     },
37209     
37210     
37211 // private
37212     factory : function(cfg)
37213     {
37214         
37215         var validRegions = Roo.bootstrap.layout.Border.regions;
37216
37217         var target = cfg.region;
37218         cfg.mgr = this;
37219         
37220         var r = Roo.bootstrap.layout;
37221         Roo.log(target);
37222         switch(target){
37223             case "north":
37224                 return new r.North(cfg);
37225             case "south":
37226                 return new r.South(cfg);
37227             case "east":
37228                 return new r.East(cfg);
37229             case "west":
37230                 return new r.West(cfg);
37231             case "center":
37232                 return new r.Center(cfg);
37233         }
37234         throw 'Layout region "'+target+'" not supported.';
37235     }
37236     
37237     
37238 });
37239  /*
37240  * Based on:
37241  * Ext JS Library 1.1.1
37242  * Copyright(c) 2006-2007, Ext JS, LLC.
37243  *
37244  * Originally Released Under LGPL - original licence link has changed is not relivant.
37245  *
37246  * Fork - LGPL
37247  * <script type="text/javascript">
37248  */
37249  
37250 /**
37251  * @class Roo.bootstrap.layout.Basic
37252  * @extends Roo.util.Observable
37253  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37254  * and does not have a titlebar, tabs or any other features. All it does is size and position 
37255  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37256  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37257  * @cfg {string}   region  the region that it inhabits..
37258  * @cfg {bool}   skipConfig skip config?
37259  * 
37260
37261  */
37262 Roo.bootstrap.layout.Basic = function(config){
37263     
37264     this.mgr = config.mgr;
37265     
37266     this.position = config.region;
37267     
37268     var skipConfig = config.skipConfig;
37269     
37270     this.events = {
37271         /**
37272          * @scope Roo.BasicLayoutRegion
37273          */
37274         
37275         /**
37276          * @event beforeremove
37277          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37278          * @param {Roo.LayoutRegion} this
37279          * @param {Roo.ContentPanel} panel The panel
37280          * @param {Object} e The cancel event object
37281          */
37282         "beforeremove" : true,
37283         /**
37284          * @event invalidated
37285          * Fires when the layout for this region is changed.
37286          * @param {Roo.LayoutRegion} this
37287          */
37288         "invalidated" : true,
37289         /**
37290          * @event visibilitychange
37291          * Fires when this region is shown or hidden 
37292          * @param {Roo.LayoutRegion} this
37293          * @param {Boolean} visibility true or false
37294          */
37295         "visibilitychange" : true,
37296         /**
37297          * @event paneladded
37298          * Fires when a panel is added. 
37299          * @param {Roo.LayoutRegion} this
37300          * @param {Roo.ContentPanel} panel The panel
37301          */
37302         "paneladded" : true,
37303         /**
37304          * @event panelremoved
37305          * Fires when a panel is removed. 
37306          * @param {Roo.LayoutRegion} this
37307          * @param {Roo.ContentPanel} panel The panel
37308          */
37309         "panelremoved" : true,
37310         /**
37311          * @event beforecollapse
37312          * Fires when this region before collapse.
37313          * @param {Roo.LayoutRegion} this
37314          */
37315         "beforecollapse" : true,
37316         /**
37317          * @event collapsed
37318          * Fires when this region is collapsed.
37319          * @param {Roo.LayoutRegion} this
37320          */
37321         "collapsed" : true,
37322         /**
37323          * @event expanded
37324          * Fires when this region is expanded.
37325          * @param {Roo.LayoutRegion} this
37326          */
37327         "expanded" : true,
37328         /**
37329          * @event slideshow
37330          * Fires when this region is slid into view.
37331          * @param {Roo.LayoutRegion} this
37332          */
37333         "slideshow" : true,
37334         /**
37335          * @event slidehide
37336          * Fires when this region slides out of view. 
37337          * @param {Roo.LayoutRegion} this
37338          */
37339         "slidehide" : true,
37340         /**
37341          * @event panelactivated
37342          * Fires when a panel is activated. 
37343          * @param {Roo.LayoutRegion} this
37344          * @param {Roo.ContentPanel} panel The activated panel
37345          */
37346         "panelactivated" : true,
37347         /**
37348          * @event resized
37349          * Fires when the user resizes this region. 
37350          * @param {Roo.LayoutRegion} this
37351          * @param {Number} newSize The new size (width for east/west, height for north/south)
37352          */
37353         "resized" : true
37354     };
37355     /** A collection of panels in this region. @type Roo.util.MixedCollection */
37356     this.panels = new Roo.util.MixedCollection();
37357     this.panels.getKey = this.getPanelId.createDelegate(this);
37358     this.box = null;
37359     this.activePanel = null;
37360     // ensure listeners are added...
37361     
37362     if (config.listeners || config.events) {
37363         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37364             listeners : config.listeners || {},
37365             events : config.events || {}
37366         });
37367     }
37368     
37369     if(skipConfig !== true){
37370         this.applyConfig(config);
37371     }
37372 };
37373
37374 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
37375 {
37376     getPanelId : function(p){
37377         return p.getId();
37378     },
37379     
37380     applyConfig : function(config){
37381         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37382         this.config = config;
37383         
37384     },
37385     
37386     /**
37387      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
37388      * the width, for horizontal (north, south) the height.
37389      * @param {Number} newSize The new width or height
37390      */
37391     resizeTo : function(newSize){
37392         var el = this.el ? this.el :
37393                  (this.activePanel ? this.activePanel.getEl() : null);
37394         if(el){
37395             switch(this.position){
37396                 case "east":
37397                 case "west":
37398                     el.setWidth(newSize);
37399                     this.fireEvent("resized", this, newSize);
37400                 break;
37401                 case "north":
37402                 case "south":
37403                     el.setHeight(newSize);
37404                     this.fireEvent("resized", this, newSize);
37405                 break;                
37406             }
37407         }
37408     },
37409     
37410     getBox : function(){
37411         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
37412     },
37413     
37414     getMargins : function(){
37415         return this.margins;
37416     },
37417     
37418     updateBox : function(box){
37419         this.box = box;
37420         var el = this.activePanel.getEl();
37421         el.dom.style.left = box.x + "px";
37422         el.dom.style.top = box.y + "px";
37423         this.activePanel.setSize(box.width, box.height);
37424     },
37425     
37426     /**
37427      * Returns the container element for this region.
37428      * @return {Roo.Element}
37429      */
37430     getEl : function(){
37431         return this.activePanel;
37432     },
37433     
37434     /**
37435      * Returns true if this region is currently visible.
37436      * @return {Boolean}
37437      */
37438     isVisible : function(){
37439         return this.activePanel ? true : false;
37440     },
37441     
37442     setActivePanel : function(panel){
37443         panel = this.getPanel(panel);
37444         if(this.activePanel && this.activePanel != panel){
37445             this.activePanel.setActiveState(false);
37446             this.activePanel.getEl().setLeftTop(-10000,-10000);
37447         }
37448         this.activePanel = panel;
37449         panel.setActiveState(true);
37450         if(this.box){
37451             panel.setSize(this.box.width, this.box.height);
37452         }
37453         this.fireEvent("panelactivated", this, panel);
37454         this.fireEvent("invalidated");
37455     },
37456     
37457     /**
37458      * Show the specified panel.
37459      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
37460      * @return {Roo.ContentPanel} The shown panel or null
37461      */
37462     showPanel : function(panel){
37463         panel = this.getPanel(panel);
37464         if(panel){
37465             this.setActivePanel(panel);
37466         }
37467         return panel;
37468     },
37469     
37470     /**
37471      * Get the active panel for this region.
37472      * @return {Roo.ContentPanel} The active panel or null
37473      */
37474     getActivePanel : function(){
37475         return this.activePanel;
37476     },
37477     
37478     /**
37479      * Add the passed ContentPanel(s)
37480      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
37481      * @return {Roo.ContentPanel} The panel added (if only one was added)
37482      */
37483     add : function(panel){
37484         if(arguments.length > 1){
37485             for(var i = 0, len = arguments.length; i < len; i++) {
37486                 this.add(arguments[i]);
37487             }
37488             return null;
37489         }
37490         if(this.hasPanel(panel)){
37491             this.showPanel(panel);
37492             return panel;
37493         }
37494         var el = panel.getEl();
37495         if(el.dom.parentNode != this.mgr.el.dom){
37496             this.mgr.el.dom.appendChild(el.dom);
37497         }
37498         if(panel.setRegion){
37499             panel.setRegion(this);
37500         }
37501         this.panels.add(panel);
37502         el.setStyle("position", "absolute");
37503         if(!panel.background){
37504             this.setActivePanel(panel);
37505             if(this.config.initialSize && this.panels.getCount()==1){
37506                 this.resizeTo(this.config.initialSize);
37507             }
37508         }
37509         this.fireEvent("paneladded", this, panel);
37510         return panel;
37511     },
37512     
37513     /**
37514      * Returns true if the panel is in this region.
37515      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37516      * @return {Boolean}
37517      */
37518     hasPanel : function(panel){
37519         if(typeof panel == "object"){ // must be panel obj
37520             panel = panel.getId();
37521         }
37522         return this.getPanel(panel) ? true : false;
37523     },
37524     
37525     /**
37526      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
37527      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37528      * @param {Boolean} preservePanel Overrides the config preservePanel option
37529      * @return {Roo.ContentPanel} The panel that was removed
37530      */
37531     remove : function(panel, preservePanel){
37532         panel = this.getPanel(panel);
37533         if(!panel){
37534             return null;
37535         }
37536         var e = {};
37537         this.fireEvent("beforeremove", this, panel, e);
37538         if(e.cancel === true){
37539             return null;
37540         }
37541         var panelId = panel.getId();
37542         this.panels.removeKey(panelId);
37543         return panel;
37544     },
37545     
37546     /**
37547      * Returns the panel specified or null if it's not in this region.
37548      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37549      * @return {Roo.ContentPanel}
37550      */
37551     getPanel : function(id){
37552         if(typeof id == "object"){ // must be panel obj
37553             return id;
37554         }
37555         return this.panels.get(id);
37556     },
37557     
37558     /**
37559      * Returns this regions position (north/south/east/west/center).
37560      * @return {String} 
37561      */
37562     getPosition: function(){
37563         return this.position;    
37564     }
37565 });/*
37566  * Based on:
37567  * Ext JS Library 1.1.1
37568  * Copyright(c) 2006-2007, Ext JS, LLC.
37569  *
37570  * Originally Released Under LGPL - original licence link has changed is not relivant.
37571  *
37572  * Fork - LGPL
37573  * <script type="text/javascript">
37574  */
37575  
37576 /**
37577  * @class Roo.bootstrap.layout.Region
37578  * @extends Roo.bootstrap.layout.Basic
37579  * This class represents a region in a layout manager.
37580  
37581  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
37582  * @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})
37583  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
37584  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
37585  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
37586  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
37587  * @cfg {String}    title           The title for the region (overrides panel titles)
37588  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
37589  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
37590  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
37591  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
37592  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
37593  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
37594  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
37595  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
37596  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
37597  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
37598
37599  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
37600  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
37601  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
37602  * @cfg {Number}    width           For East/West panels
37603  * @cfg {Number}    height          For North/South panels
37604  * @cfg {Boolean}   split           To show the splitter
37605  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
37606  * 
37607  * @cfg {string}   cls             Extra CSS classes to add to region
37608  * 
37609  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37610  * @cfg {string}   region  the region that it inhabits..
37611  *
37612
37613  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
37614  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
37615
37616  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
37617  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
37618  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
37619  */
37620 Roo.bootstrap.layout.Region = function(config)
37621 {
37622     this.applyConfig(config);
37623
37624     var mgr = config.mgr;
37625     var pos = config.region;
37626     config.skipConfig = true;
37627     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
37628     
37629     if (mgr.el) {
37630         this.onRender(mgr.el);   
37631     }
37632      
37633     this.visible = true;
37634     this.collapsed = false;
37635     this.unrendered_panels = [];
37636 };
37637
37638 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
37639
37640     position: '', // set by wrapper (eg. north/south etc..)
37641     unrendered_panels : null,  // unrendered panels.
37642     
37643     tabPosition : false,
37644     
37645     mgr: false, // points to 'Border'
37646     
37647     
37648     createBody : function(){
37649         /** This region's body element 
37650         * @type Roo.Element */
37651         this.bodyEl = this.el.createChild({
37652                 tag: "div",
37653                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
37654         });
37655     },
37656
37657     onRender: function(ctr, pos)
37658     {
37659         var dh = Roo.DomHelper;
37660         /** This region's container element 
37661         * @type Roo.Element */
37662         this.el = dh.append(ctr.dom, {
37663                 tag: "div",
37664                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
37665             }, true);
37666         /** This region's title element 
37667         * @type Roo.Element */
37668     
37669         this.titleEl = dh.append(this.el.dom,  {
37670                 tag: "div",
37671                 unselectable: "on",
37672                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
37673                 children:[
37674                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
37675                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
37676                 ]
37677             }, true);
37678         
37679         this.titleEl.enableDisplayMode();
37680         /** This region's title text element 
37681         * @type HTMLElement */
37682         this.titleTextEl = this.titleEl.dom.firstChild;
37683         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
37684         /*
37685         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
37686         this.closeBtn.enableDisplayMode();
37687         this.closeBtn.on("click", this.closeClicked, this);
37688         this.closeBtn.hide();
37689     */
37690         this.createBody(this.config);
37691         if(this.config.hideWhenEmpty){
37692             this.hide();
37693             this.on("paneladded", this.validateVisibility, this);
37694             this.on("panelremoved", this.validateVisibility, this);
37695         }
37696         if(this.autoScroll){
37697             this.bodyEl.setStyle("overflow", "auto");
37698         }else{
37699             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
37700         }
37701         //if(c.titlebar !== false){
37702             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
37703                 this.titleEl.hide();
37704             }else{
37705                 this.titleEl.show();
37706                 if(this.config.title){
37707                     this.titleTextEl.innerHTML = this.config.title;
37708                 }
37709             }
37710         //}
37711         if(this.config.collapsed){
37712             this.collapse(true);
37713         }
37714         if(this.config.hidden){
37715             this.hide();
37716         }
37717         
37718         if (this.unrendered_panels && this.unrendered_panels.length) {
37719             for (var i =0;i< this.unrendered_panels.length; i++) {
37720                 this.add(this.unrendered_panels[i]);
37721             }
37722             this.unrendered_panels = null;
37723             
37724         }
37725         
37726     },
37727     
37728     applyConfig : function(c)
37729     {
37730         /*
37731          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
37732             var dh = Roo.DomHelper;
37733             if(c.titlebar !== false){
37734                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
37735                 this.collapseBtn.on("click", this.collapse, this);
37736                 this.collapseBtn.enableDisplayMode();
37737                 /*
37738                 if(c.showPin === true || this.showPin){
37739                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
37740                     this.stickBtn.enableDisplayMode();
37741                     this.stickBtn.on("click", this.expand, this);
37742                     this.stickBtn.hide();
37743                 }
37744                 
37745             }
37746             */
37747             /** This region's collapsed element
37748             * @type Roo.Element */
37749             /*
37750              *
37751             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
37752                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
37753             ]}, true);
37754             
37755             if(c.floatable !== false){
37756                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
37757                this.collapsedEl.on("click", this.collapseClick, this);
37758             }
37759
37760             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
37761                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
37762                    id: "message", unselectable: "on", style:{"float":"left"}});
37763                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
37764              }
37765             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
37766             this.expandBtn.on("click", this.expand, this);
37767             
37768         }
37769         
37770         if(this.collapseBtn){
37771             this.collapseBtn.setVisible(c.collapsible == true);
37772         }
37773         
37774         this.cmargins = c.cmargins || this.cmargins ||
37775                          (this.position == "west" || this.position == "east" ?
37776                              {top: 0, left: 2, right:2, bottom: 0} :
37777                              {top: 2, left: 0, right:0, bottom: 2});
37778         */
37779         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37780         
37781         
37782         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
37783         
37784         this.autoScroll = c.autoScroll || false;
37785         
37786         
37787        
37788         
37789         this.duration = c.duration || .30;
37790         this.slideDuration = c.slideDuration || .45;
37791         this.config = c;
37792        
37793     },
37794     /**
37795      * Returns true if this region is currently visible.
37796      * @return {Boolean}
37797      */
37798     isVisible : function(){
37799         return this.visible;
37800     },
37801
37802     /**
37803      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
37804      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
37805      */
37806     //setCollapsedTitle : function(title){
37807     //    title = title || "&#160;";
37808      //   if(this.collapsedTitleTextEl){
37809       //      this.collapsedTitleTextEl.innerHTML = title;
37810        // }
37811     //},
37812
37813     getBox : function(){
37814         var b;
37815       //  if(!this.collapsed){
37816             b = this.el.getBox(false, true);
37817        // }else{
37818           //  b = this.collapsedEl.getBox(false, true);
37819         //}
37820         return b;
37821     },
37822
37823     getMargins : function(){
37824         return this.margins;
37825         //return this.collapsed ? this.cmargins : this.margins;
37826     },
37827 /*
37828     highlight : function(){
37829         this.el.addClass("x-layout-panel-dragover");
37830     },
37831
37832     unhighlight : function(){
37833         this.el.removeClass("x-layout-panel-dragover");
37834     },
37835 */
37836     updateBox : function(box)
37837     {
37838         if (!this.bodyEl) {
37839             return; // not rendered yet..
37840         }
37841         
37842         this.box = box;
37843         if(!this.collapsed){
37844             this.el.dom.style.left = box.x + "px";
37845             this.el.dom.style.top = box.y + "px";
37846             this.updateBody(box.width, box.height);
37847         }else{
37848             this.collapsedEl.dom.style.left = box.x + "px";
37849             this.collapsedEl.dom.style.top = box.y + "px";
37850             this.collapsedEl.setSize(box.width, box.height);
37851         }
37852         if(this.tabs){
37853             this.tabs.autoSizeTabs();
37854         }
37855     },
37856
37857     updateBody : function(w, h)
37858     {
37859         if(w !== null){
37860             this.el.setWidth(w);
37861             w -= this.el.getBorderWidth("rl");
37862             if(this.config.adjustments){
37863                 w += this.config.adjustments[0];
37864             }
37865         }
37866         if(h !== null && h > 0){
37867             this.el.setHeight(h);
37868             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
37869             h -= this.el.getBorderWidth("tb");
37870             if(this.config.adjustments){
37871                 h += this.config.adjustments[1];
37872             }
37873             this.bodyEl.setHeight(h);
37874             if(this.tabs){
37875                 h = this.tabs.syncHeight(h);
37876             }
37877         }
37878         if(this.panelSize){
37879             w = w !== null ? w : this.panelSize.width;
37880             h = h !== null ? h : this.panelSize.height;
37881         }
37882         if(this.activePanel){
37883             var el = this.activePanel.getEl();
37884             w = w !== null ? w : el.getWidth();
37885             h = h !== null ? h : el.getHeight();
37886             this.panelSize = {width: w, height: h};
37887             this.activePanel.setSize(w, h);
37888         }
37889         if(Roo.isIE && this.tabs){
37890             this.tabs.el.repaint();
37891         }
37892     },
37893
37894     /**
37895      * Returns the container element for this region.
37896      * @return {Roo.Element}
37897      */
37898     getEl : function(){
37899         return this.el;
37900     },
37901
37902     /**
37903      * Hides this region.
37904      */
37905     hide : function(){
37906         //if(!this.collapsed){
37907             this.el.dom.style.left = "-2000px";
37908             this.el.hide();
37909         //}else{
37910          //   this.collapsedEl.dom.style.left = "-2000px";
37911          //   this.collapsedEl.hide();
37912        // }
37913         this.visible = false;
37914         this.fireEvent("visibilitychange", this, false);
37915     },
37916
37917     /**
37918      * Shows this region if it was previously hidden.
37919      */
37920     show : function(){
37921         //if(!this.collapsed){
37922             this.el.show();
37923         //}else{
37924         //    this.collapsedEl.show();
37925        // }
37926         this.visible = true;
37927         this.fireEvent("visibilitychange", this, true);
37928     },
37929 /*
37930     closeClicked : function(){
37931         if(this.activePanel){
37932             this.remove(this.activePanel);
37933         }
37934     },
37935
37936     collapseClick : function(e){
37937         if(this.isSlid){
37938            e.stopPropagation();
37939            this.slideIn();
37940         }else{
37941            e.stopPropagation();
37942            this.slideOut();
37943         }
37944     },
37945 */
37946     /**
37947      * Collapses this region.
37948      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
37949      */
37950     /*
37951     collapse : function(skipAnim, skipCheck = false){
37952         if(this.collapsed) {
37953             return;
37954         }
37955         
37956         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
37957             
37958             this.collapsed = true;
37959             if(this.split){
37960                 this.split.el.hide();
37961             }
37962             if(this.config.animate && skipAnim !== true){
37963                 this.fireEvent("invalidated", this);
37964                 this.animateCollapse();
37965             }else{
37966                 this.el.setLocation(-20000,-20000);
37967                 this.el.hide();
37968                 this.collapsedEl.show();
37969                 this.fireEvent("collapsed", this);
37970                 this.fireEvent("invalidated", this);
37971             }
37972         }
37973         
37974     },
37975 */
37976     animateCollapse : function(){
37977         // overridden
37978     },
37979
37980     /**
37981      * Expands this region if it was previously collapsed.
37982      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
37983      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
37984      */
37985     /*
37986     expand : function(e, skipAnim){
37987         if(e) {
37988             e.stopPropagation();
37989         }
37990         if(!this.collapsed || this.el.hasActiveFx()) {
37991             return;
37992         }
37993         if(this.isSlid){
37994             this.afterSlideIn();
37995             skipAnim = true;
37996         }
37997         this.collapsed = false;
37998         if(this.config.animate && skipAnim !== true){
37999             this.animateExpand();
38000         }else{
38001             this.el.show();
38002             if(this.split){
38003                 this.split.el.show();
38004             }
38005             this.collapsedEl.setLocation(-2000,-2000);
38006             this.collapsedEl.hide();
38007             this.fireEvent("invalidated", this);
38008             this.fireEvent("expanded", this);
38009         }
38010     },
38011 */
38012     animateExpand : function(){
38013         // overridden
38014     },
38015
38016     initTabs : function()
38017     {
38018         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38019         
38020         var ts = new Roo.bootstrap.panel.Tabs({
38021             el: this.bodyEl.dom,
38022             region : this,
38023             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
38024             disableTooltips: this.config.disableTabTips,
38025             toolbar : this.config.toolbar
38026         });
38027         
38028         if(this.config.hideTabs){
38029             ts.stripWrap.setDisplayed(false);
38030         }
38031         this.tabs = ts;
38032         ts.resizeTabs = this.config.resizeTabs === true;
38033         ts.minTabWidth = this.config.minTabWidth || 40;
38034         ts.maxTabWidth = this.config.maxTabWidth || 250;
38035         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38036         ts.monitorResize = false;
38037         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38038         ts.bodyEl.addClass('roo-layout-tabs-body');
38039         this.panels.each(this.initPanelAsTab, this);
38040     },
38041
38042     initPanelAsTab : function(panel){
38043         var ti = this.tabs.addTab(
38044             panel.getEl().id,
38045             panel.getTitle(),
38046             null,
38047             this.config.closeOnTab && panel.isClosable(),
38048             panel.tpl
38049         );
38050         if(panel.tabTip !== undefined){
38051             ti.setTooltip(panel.tabTip);
38052         }
38053         ti.on("activate", function(){
38054               this.setActivePanel(panel);
38055         }, this);
38056         
38057         if(this.config.closeOnTab){
38058             ti.on("beforeclose", function(t, e){
38059                 e.cancel = true;
38060                 this.remove(panel);
38061             }, this);
38062         }
38063         
38064         panel.tabItem = ti;
38065         
38066         return ti;
38067     },
38068
38069     updatePanelTitle : function(panel, title)
38070     {
38071         if(this.activePanel == panel){
38072             this.updateTitle(title);
38073         }
38074         if(this.tabs){
38075             var ti = this.tabs.getTab(panel.getEl().id);
38076             ti.setText(title);
38077             if(panel.tabTip !== undefined){
38078                 ti.setTooltip(panel.tabTip);
38079             }
38080         }
38081     },
38082
38083     updateTitle : function(title){
38084         if(this.titleTextEl && !this.config.title){
38085             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
38086         }
38087     },
38088
38089     setActivePanel : function(panel)
38090     {
38091         panel = this.getPanel(panel);
38092         if(this.activePanel && this.activePanel != panel){
38093             if(this.activePanel.setActiveState(false) === false){
38094                 return;
38095             }
38096         }
38097         this.activePanel = panel;
38098         panel.setActiveState(true);
38099         if(this.panelSize){
38100             panel.setSize(this.panelSize.width, this.panelSize.height);
38101         }
38102         if(this.closeBtn){
38103             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38104         }
38105         this.updateTitle(panel.getTitle());
38106         if(this.tabs){
38107             this.fireEvent("invalidated", this);
38108         }
38109         this.fireEvent("panelactivated", this, panel);
38110     },
38111
38112     /**
38113      * Shows the specified panel.
38114      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38115      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38116      */
38117     showPanel : function(panel)
38118     {
38119         panel = this.getPanel(panel);
38120         if(panel){
38121             if(this.tabs){
38122                 var tab = this.tabs.getTab(panel.getEl().id);
38123                 if(tab.isHidden()){
38124                     this.tabs.unhideTab(tab.id);
38125                 }
38126                 tab.activate();
38127             }else{
38128                 this.setActivePanel(panel);
38129             }
38130         }
38131         return panel;
38132     },
38133
38134     /**
38135      * Get the active panel for this region.
38136      * @return {Roo.ContentPanel} The active panel or null
38137      */
38138     getActivePanel : function(){
38139         return this.activePanel;
38140     },
38141
38142     validateVisibility : function(){
38143         if(this.panels.getCount() < 1){
38144             this.updateTitle("&#160;");
38145             this.closeBtn.hide();
38146             this.hide();
38147         }else{
38148             if(!this.isVisible()){
38149                 this.show();
38150             }
38151         }
38152     },
38153
38154     /**
38155      * Adds the passed ContentPanel(s) to this region.
38156      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38157      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38158      */
38159     add : function(panel)
38160     {
38161         if(arguments.length > 1){
38162             for(var i = 0, len = arguments.length; i < len; i++) {
38163                 this.add(arguments[i]);
38164             }
38165             return null;
38166         }
38167         
38168         // if we have not been rendered yet, then we can not really do much of this..
38169         if (!this.bodyEl) {
38170             this.unrendered_panels.push(panel);
38171             return panel;
38172         }
38173         
38174         
38175         
38176         
38177         if(this.hasPanel(panel)){
38178             this.showPanel(panel);
38179             return panel;
38180         }
38181         panel.setRegion(this);
38182         this.panels.add(panel);
38183        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38184             // sinle panel - no tab...?? would it not be better to render it with the tabs,
38185             // and hide them... ???
38186             this.bodyEl.dom.appendChild(panel.getEl().dom);
38187             if(panel.background !== true){
38188                 this.setActivePanel(panel);
38189             }
38190             this.fireEvent("paneladded", this, panel);
38191             return panel;
38192         }
38193         */
38194         if(!this.tabs){
38195             this.initTabs();
38196         }else{
38197             this.initPanelAsTab(panel);
38198         }
38199         
38200         
38201         if(panel.background !== true){
38202             this.tabs.activate(panel.getEl().id);
38203         }
38204         this.fireEvent("paneladded", this, panel);
38205         return panel;
38206     },
38207
38208     /**
38209      * Hides the tab for the specified panel.
38210      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38211      */
38212     hidePanel : function(panel){
38213         if(this.tabs && (panel = this.getPanel(panel))){
38214             this.tabs.hideTab(panel.getEl().id);
38215         }
38216     },
38217
38218     /**
38219      * Unhides the tab for a previously hidden panel.
38220      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38221      */
38222     unhidePanel : function(panel){
38223         if(this.tabs && (panel = this.getPanel(panel))){
38224             this.tabs.unhideTab(panel.getEl().id);
38225         }
38226     },
38227
38228     clearPanels : function(){
38229         while(this.panels.getCount() > 0){
38230              this.remove(this.panels.first());
38231         }
38232     },
38233
38234     /**
38235      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38236      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38237      * @param {Boolean} preservePanel Overrides the config preservePanel option
38238      * @return {Roo.ContentPanel} The panel that was removed
38239      */
38240     remove : function(panel, preservePanel)
38241     {
38242         panel = this.getPanel(panel);
38243         if(!panel){
38244             return null;
38245         }
38246         var e = {};
38247         this.fireEvent("beforeremove", this, panel, e);
38248         if(e.cancel === true){
38249             return null;
38250         }
38251         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38252         var panelId = panel.getId();
38253         this.panels.removeKey(panelId);
38254         if(preservePanel){
38255             document.body.appendChild(panel.getEl().dom);
38256         }
38257         if(this.tabs){
38258             this.tabs.removeTab(panel.getEl().id);
38259         }else if (!preservePanel){
38260             this.bodyEl.dom.removeChild(panel.getEl().dom);
38261         }
38262         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38263             var p = this.panels.first();
38264             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38265             tempEl.appendChild(p.getEl().dom);
38266             this.bodyEl.update("");
38267             this.bodyEl.dom.appendChild(p.getEl().dom);
38268             tempEl = null;
38269             this.updateTitle(p.getTitle());
38270             this.tabs = null;
38271             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38272             this.setActivePanel(p);
38273         }
38274         panel.setRegion(null);
38275         if(this.activePanel == panel){
38276             this.activePanel = null;
38277         }
38278         if(this.config.autoDestroy !== false && preservePanel !== true){
38279             try{panel.destroy();}catch(e){}
38280         }
38281         this.fireEvent("panelremoved", this, panel);
38282         return panel;
38283     },
38284
38285     /**
38286      * Returns the TabPanel component used by this region
38287      * @return {Roo.TabPanel}
38288      */
38289     getTabs : function(){
38290         return this.tabs;
38291     },
38292
38293     createTool : function(parentEl, className){
38294         var btn = Roo.DomHelper.append(parentEl, {
38295             tag: "div",
38296             cls: "x-layout-tools-button",
38297             children: [ {
38298                 tag: "div",
38299                 cls: "roo-layout-tools-button-inner " + className,
38300                 html: "&#160;"
38301             }]
38302         }, true);
38303         btn.addClassOnOver("roo-layout-tools-button-over");
38304         return btn;
38305     }
38306 });/*
38307  * Based on:
38308  * Ext JS Library 1.1.1
38309  * Copyright(c) 2006-2007, Ext JS, LLC.
38310  *
38311  * Originally Released Under LGPL - original licence link has changed is not relivant.
38312  *
38313  * Fork - LGPL
38314  * <script type="text/javascript">
38315  */
38316  
38317
38318
38319 /**
38320  * @class Roo.SplitLayoutRegion
38321  * @extends Roo.LayoutRegion
38322  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38323  */
38324 Roo.bootstrap.layout.Split = function(config){
38325     this.cursor = config.cursor;
38326     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38327 };
38328
38329 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38330 {
38331     splitTip : "Drag to resize.",
38332     collapsibleSplitTip : "Drag to resize. Double click to hide.",
38333     useSplitTips : false,
38334
38335     applyConfig : function(config){
38336         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38337     },
38338     
38339     onRender : function(ctr,pos) {
38340         
38341         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38342         if(!this.config.split){
38343             return;
38344         }
38345         if(!this.split){
38346             
38347             var splitEl = Roo.DomHelper.append(ctr.dom,  {
38348                             tag: "div",
38349                             id: this.el.id + "-split",
38350                             cls: "roo-layout-split roo-layout-split-"+this.position,
38351                             html: "&#160;"
38352             });
38353             /** The SplitBar for this region 
38354             * @type Roo.SplitBar */
38355             // does not exist yet...
38356             Roo.log([this.position, this.orientation]);
38357             
38358             this.split = new Roo.bootstrap.SplitBar({
38359                 dragElement : splitEl,
38360                 resizingElement: this.el,
38361                 orientation : this.orientation
38362             });
38363             
38364             this.split.on("moved", this.onSplitMove, this);
38365             this.split.useShim = this.config.useShim === true;
38366             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38367             if(this.useSplitTips){
38368                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38369             }
38370             //if(config.collapsible){
38371             //    this.split.el.on("dblclick", this.collapse,  this);
38372             //}
38373         }
38374         if(typeof this.config.minSize != "undefined"){
38375             this.split.minSize = this.config.minSize;
38376         }
38377         if(typeof this.config.maxSize != "undefined"){
38378             this.split.maxSize = this.config.maxSize;
38379         }
38380         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
38381             this.hideSplitter();
38382         }
38383         
38384     },
38385
38386     getHMaxSize : function(){
38387          var cmax = this.config.maxSize || 10000;
38388          var center = this.mgr.getRegion("center");
38389          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
38390     },
38391
38392     getVMaxSize : function(){
38393          var cmax = this.config.maxSize || 10000;
38394          var center = this.mgr.getRegion("center");
38395          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
38396     },
38397
38398     onSplitMove : function(split, newSize){
38399         this.fireEvent("resized", this, newSize);
38400     },
38401     
38402     /** 
38403      * Returns the {@link Roo.SplitBar} for this region.
38404      * @return {Roo.SplitBar}
38405      */
38406     getSplitBar : function(){
38407         return this.split;
38408     },
38409     
38410     hide : function(){
38411         this.hideSplitter();
38412         Roo.bootstrap.layout.Split.superclass.hide.call(this);
38413     },
38414
38415     hideSplitter : function(){
38416         if(this.split){
38417             this.split.el.setLocation(-2000,-2000);
38418             this.split.el.hide();
38419         }
38420     },
38421
38422     show : function(){
38423         if(this.split){
38424             this.split.el.show();
38425         }
38426         Roo.bootstrap.layout.Split.superclass.show.call(this);
38427     },
38428     
38429     beforeSlide: function(){
38430         if(Roo.isGecko){// firefox overflow auto bug workaround
38431             this.bodyEl.clip();
38432             if(this.tabs) {
38433                 this.tabs.bodyEl.clip();
38434             }
38435             if(this.activePanel){
38436                 this.activePanel.getEl().clip();
38437                 
38438                 if(this.activePanel.beforeSlide){
38439                     this.activePanel.beforeSlide();
38440                 }
38441             }
38442         }
38443     },
38444     
38445     afterSlide : function(){
38446         if(Roo.isGecko){// firefox overflow auto bug workaround
38447             this.bodyEl.unclip();
38448             if(this.tabs) {
38449                 this.tabs.bodyEl.unclip();
38450             }
38451             if(this.activePanel){
38452                 this.activePanel.getEl().unclip();
38453                 if(this.activePanel.afterSlide){
38454                     this.activePanel.afterSlide();
38455                 }
38456             }
38457         }
38458     },
38459
38460     initAutoHide : function(){
38461         if(this.autoHide !== false){
38462             if(!this.autoHideHd){
38463                 var st = new Roo.util.DelayedTask(this.slideIn, this);
38464                 this.autoHideHd = {
38465                     "mouseout": function(e){
38466                         if(!e.within(this.el, true)){
38467                             st.delay(500);
38468                         }
38469                     },
38470                     "mouseover" : function(e){
38471                         st.cancel();
38472                     },
38473                     scope : this
38474                 };
38475             }
38476             this.el.on(this.autoHideHd);
38477         }
38478     },
38479
38480     clearAutoHide : function(){
38481         if(this.autoHide !== false){
38482             this.el.un("mouseout", this.autoHideHd.mouseout);
38483             this.el.un("mouseover", this.autoHideHd.mouseover);
38484         }
38485     },
38486
38487     clearMonitor : function(){
38488         Roo.get(document).un("click", this.slideInIf, this);
38489     },
38490
38491     // these names are backwards but not changed for compat
38492     slideOut : function(){
38493         if(this.isSlid || this.el.hasActiveFx()){
38494             return;
38495         }
38496         this.isSlid = true;
38497         if(this.collapseBtn){
38498             this.collapseBtn.hide();
38499         }
38500         this.closeBtnState = this.closeBtn.getStyle('display');
38501         this.closeBtn.hide();
38502         if(this.stickBtn){
38503             this.stickBtn.show();
38504         }
38505         this.el.show();
38506         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
38507         this.beforeSlide();
38508         this.el.setStyle("z-index", 10001);
38509         this.el.slideIn(this.getSlideAnchor(), {
38510             callback: function(){
38511                 this.afterSlide();
38512                 this.initAutoHide();
38513                 Roo.get(document).on("click", this.slideInIf, this);
38514                 this.fireEvent("slideshow", this);
38515             },
38516             scope: this,
38517             block: true
38518         });
38519     },
38520
38521     afterSlideIn : function(){
38522         this.clearAutoHide();
38523         this.isSlid = false;
38524         this.clearMonitor();
38525         this.el.setStyle("z-index", "");
38526         if(this.collapseBtn){
38527             this.collapseBtn.show();
38528         }
38529         this.closeBtn.setStyle('display', this.closeBtnState);
38530         if(this.stickBtn){
38531             this.stickBtn.hide();
38532         }
38533         this.fireEvent("slidehide", this);
38534     },
38535
38536     slideIn : function(cb){
38537         if(!this.isSlid || this.el.hasActiveFx()){
38538             Roo.callback(cb);
38539             return;
38540         }
38541         this.isSlid = false;
38542         this.beforeSlide();
38543         this.el.slideOut(this.getSlideAnchor(), {
38544             callback: function(){
38545                 this.el.setLeftTop(-10000, -10000);
38546                 this.afterSlide();
38547                 this.afterSlideIn();
38548                 Roo.callback(cb);
38549             },
38550             scope: this,
38551             block: true
38552         });
38553     },
38554     
38555     slideInIf : function(e){
38556         if(!e.within(this.el)){
38557             this.slideIn();
38558         }
38559     },
38560
38561     animateCollapse : function(){
38562         this.beforeSlide();
38563         this.el.setStyle("z-index", 20000);
38564         var anchor = this.getSlideAnchor();
38565         this.el.slideOut(anchor, {
38566             callback : function(){
38567                 this.el.setStyle("z-index", "");
38568                 this.collapsedEl.slideIn(anchor, {duration:.3});
38569                 this.afterSlide();
38570                 this.el.setLocation(-10000,-10000);
38571                 this.el.hide();
38572                 this.fireEvent("collapsed", this);
38573             },
38574             scope: this,
38575             block: true
38576         });
38577     },
38578
38579     animateExpand : function(){
38580         this.beforeSlide();
38581         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
38582         this.el.setStyle("z-index", 20000);
38583         this.collapsedEl.hide({
38584             duration:.1
38585         });
38586         this.el.slideIn(this.getSlideAnchor(), {
38587             callback : function(){
38588                 this.el.setStyle("z-index", "");
38589                 this.afterSlide();
38590                 if(this.split){
38591                     this.split.el.show();
38592                 }
38593                 this.fireEvent("invalidated", this);
38594                 this.fireEvent("expanded", this);
38595             },
38596             scope: this,
38597             block: true
38598         });
38599     },
38600
38601     anchors : {
38602         "west" : "left",
38603         "east" : "right",
38604         "north" : "top",
38605         "south" : "bottom"
38606     },
38607
38608     sanchors : {
38609         "west" : "l",
38610         "east" : "r",
38611         "north" : "t",
38612         "south" : "b"
38613     },
38614
38615     canchors : {
38616         "west" : "tl-tr",
38617         "east" : "tr-tl",
38618         "north" : "tl-bl",
38619         "south" : "bl-tl"
38620     },
38621
38622     getAnchor : function(){
38623         return this.anchors[this.position];
38624     },
38625
38626     getCollapseAnchor : function(){
38627         return this.canchors[this.position];
38628     },
38629
38630     getSlideAnchor : function(){
38631         return this.sanchors[this.position];
38632     },
38633
38634     getAlignAdj : function(){
38635         var cm = this.cmargins;
38636         switch(this.position){
38637             case "west":
38638                 return [0, 0];
38639             break;
38640             case "east":
38641                 return [0, 0];
38642             break;
38643             case "north":
38644                 return [0, 0];
38645             break;
38646             case "south":
38647                 return [0, 0];
38648             break;
38649         }
38650     },
38651
38652     getExpandAdj : function(){
38653         var c = this.collapsedEl, cm = this.cmargins;
38654         switch(this.position){
38655             case "west":
38656                 return [-(cm.right+c.getWidth()+cm.left), 0];
38657             break;
38658             case "east":
38659                 return [cm.right+c.getWidth()+cm.left, 0];
38660             break;
38661             case "north":
38662                 return [0, -(cm.top+cm.bottom+c.getHeight())];
38663             break;
38664             case "south":
38665                 return [0, cm.top+cm.bottom+c.getHeight()];
38666             break;
38667         }
38668     }
38669 });/*
38670  * Based on:
38671  * Ext JS Library 1.1.1
38672  * Copyright(c) 2006-2007, Ext JS, LLC.
38673  *
38674  * Originally Released Under LGPL - original licence link has changed is not relivant.
38675  *
38676  * Fork - LGPL
38677  * <script type="text/javascript">
38678  */
38679 /*
38680  * These classes are private internal classes
38681  */
38682 Roo.bootstrap.layout.Center = function(config){
38683     config.region = "center";
38684     Roo.bootstrap.layout.Region.call(this, config);
38685     this.visible = true;
38686     this.minWidth = config.minWidth || 20;
38687     this.minHeight = config.minHeight || 20;
38688 };
38689
38690 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
38691     hide : function(){
38692         // center panel can't be hidden
38693     },
38694     
38695     show : function(){
38696         // center panel can't be hidden
38697     },
38698     
38699     getMinWidth: function(){
38700         return this.minWidth;
38701     },
38702     
38703     getMinHeight: function(){
38704         return this.minHeight;
38705     }
38706 });
38707
38708
38709
38710
38711  
38712
38713
38714
38715
38716
38717
38718 Roo.bootstrap.layout.North = function(config)
38719 {
38720     config.region = 'north';
38721     config.cursor = 'n-resize';
38722     
38723     Roo.bootstrap.layout.Split.call(this, config);
38724     
38725     
38726     if(this.split){
38727         this.split.placement = Roo.bootstrap.SplitBar.TOP;
38728         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
38729         this.split.el.addClass("roo-layout-split-v");
38730     }
38731     var size = config.initialSize || config.height;
38732     if(typeof size != "undefined"){
38733         this.el.setHeight(size);
38734     }
38735 };
38736 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
38737 {
38738     orientation: Roo.bootstrap.SplitBar.VERTICAL,
38739     
38740     
38741     
38742     getBox : function(){
38743         if(this.collapsed){
38744             return this.collapsedEl.getBox();
38745         }
38746         var box = this.el.getBox();
38747         if(this.split){
38748             box.height += this.split.el.getHeight();
38749         }
38750         return box;
38751     },
38752     
38753     updateBox : function(box){
38754         if(this.split && !this.collapsed){
38755             box.height -= this.split.el.getHeight();
38756             this.split.el.setLeft(box.x);
38757             this.split.el.setTop(box.y+box.height);
38758             this.split.el.setWidth(box.width);
38759         }
38760         if(this.collapsed){
38761             this.updateBody(box.width, null);
38762         }
38763         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38764     }
38765 });
38766
38767
38768
38769
38770
38771 Roo.bootstrap.layout.South = function(config){
38772     config.region = 'south';
38773     config.cursor = 's-resize';
38774     Roo.bootstrap.layout.Split.call(this, config);
38775     if(this.split){
38776         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
38777         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
38778         this.split.el.addClass("roo-layout-split-v");
38779     }
38780     var size = config.initialSize || config.height;
38781     if(typeof size != "undefined"){
38782         this.el.setHeight(size);
38783     }
38784 };
38785
38786 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
38787     orientation: Roo.bootstrap.SplitBar.VERTICAL,
38788     getBox : function(){
38789         if(this.collapsed){
38790             return this.collapsedEl.getBox();
38791         }
38792         var box = this.el.getBox();
38793         if(this.split){
38794             var sh = this.split.el.getHeight();
38795             box.height += sh;
38796             box.y -= sh;
38797         }
38798         return box;
38799     },
38800     
38801     updateBox : function(box){
38802         if(this.split && !this.collapsed){
38803             var sh = this.split.el.getHeight();
38804             box.height -= sh;
38805             box.y += sh;
38806             this.split.el.setLeft(box.x);
38807             this.split.el.setTop(box.y-sh);
38808             this.split.el.setWidth(box.width);
38809         }
38810         if(this.collapsed){
38811             this.updateBody(box.width, null);
38812         }
38813         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38814     }
38815 });
38816
38817 Roo.bootstrap.layout.East = function(config){
38818     config.region = "east";
38819     config.cursor = "e-resize";
38820     Roo.bootstrap.layout.Split.call(this, config);
38821     if(this.split){
38822         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
38823         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
38824         this.split.el.addClass("roo-layout-split-h");
38825     }
38826     var size = config.initialSize || config.width;
38827     if(typeof size != "undefined"){
38828         this.el.setWidth(size);
38829     }
38830 };
38831 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
38832     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
38833     getBox : function(){
38834         if(this.collapsed){
38835             return this.collapsedEl.getBox();
38836         }
38837         var box = this.el.getBox();
38838         if(this.split){
38839             var sw = this.split.el.getWidth();
38840             box.width += sw;
38841             box.x -= sw;
38842         }
38843         return box;
38844     },
38845
38846     updateBox : function(box){
38847         if(this.split && !this.collapsed){
38848             var sw = this.split.el.getWidth();
38849             box.width -= sw;
38850             this.split.el.setLeft(box.x);
38851             this.split.el.setTop(box.y);
38852             this.split.el.setHeight(box.height);
38853             box.x += sw;
38854         }
38855         if(this.collapsed){
38856             this.updateBody(null, box.height);
38857         }
38858         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38859     }
38860 });
38861
38862 Roo.bootstrap.layout.West = function(config){
38863     config.region = "west";
38864     config.cursor = "w-resize";
38865     
38866     Roo.bootstrap.layout.Split.call(this, config);
38867     if(this.split){
38868         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
38869         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
38870         this.split.el.addClass("roo-layout-split-h");
38871     }
38872     
38873 };
38874 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
38875     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
38876     
38877     onRender: function(ctr, pos)
38878     {
38879         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
38880         var size = this.config.initialSize || this.config.width;
38881         if(typeof size != "undefined"){
38882             this.el.setWidth(size);
38883         }
38884     },
38885     
38886     getBox : function(){
38887         if(this.collapsed){
38888             return this.collapsedEl.getBox();
38889         }
38890         var box = this.el.getBox();
38891         if(this.split){
38892             box.width += this.split.el.getWidth();
38893         }
38894         return box;
38895     },
38896     
38897     updateBox : function(box){
38898         if(this.split && !this.collapsed){
38899             var sw = this.split.el.getWidth();
38900             box.width -= sw;
38901             this.split.el.setLeft(box.x+box.width);
38902             this.split.el.setTop(box.y);
38903             this.split.el.setHeight(box.height);
38904         }
38905         if(this.collapsed){
38906             this.updateBody(null, box.height);
38907         }
38908         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38909     }
38910 });Roo.namespace("Roo.bootstrap.panel");/*
38911  * Based on:
38912  * Ext JS Library 1.1.1
38913  * Copyright(c) 2006-2007, Ext JS, LLC.
38914  *
38915  * Originally Released Under LGPL - original licence link has changed is not relivant.
38916  *
38917  * Fork - LGPL
38918  * <script type="text/javascript">
38919  */
38920 /**
38921  * @class Roo.ContentPanel
38922  * @extends Roo.util.Observable
38923  * A basic ContentPanel element.
38924  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
38925  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
38926  * @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
38927  * @cfg {Boolean}   closable      True if the panel can be closed/removed
38928  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
38929  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
38930  * @cfg {Toolbar}   toolbar       A toolbar for this panel
38931  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
38932  * @cfg {String} title          The title for this panel
38933  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
38934  * @cfg {String} url            Calls {@link #setUrl} with this value
38935  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
38936  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
38937  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
38938  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
38939  * @cfg {Boolean} badges render the badges
38940
38941  * @constructor
38942  * Create a new ContentPanel.
38943  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
38944  * @param {String/Object} config A string to set only the title or a config object
38945  * @param {String} content (optional) Set the HTML content for this panel
38946  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
38947  */
38948 Roo.bootstrap.panel.Content = function( config){
38949     
38950     this.tpl = config.tpl || false;
38951     
38952     var el = config.el;
38953     var content = config.content;
38954
38955     if(config.autoCreate){ // xtype is available if this is called from factory
38956         el = Roo.id();
38957     }
38958     this.el = Roo.get(el);
38959     if(!this.el && config && config.autoCreate){
38960         if(typeof config.autoCreate == "object"){
38961             if(!config.autoCreate.id){
38962                 config.autoCreate.id = config.id||el;
38963             }
38964             this.el = Roo.DomHelper.append(document.body,
38965                         config.autoCreate, true);
38966         }else{
38967             var elcfg =  {   tag: "div",
38968                             cls: "roo-layout-inactive-content",
38969                             id: config.id||el
38970                             };
38971             if (config.html) {
38972                 elcfg.html = config.html;
38973                 
38974             }
38975                         
38976             this.el = Roo.DomHelper.append(document.body, elcfg , true);
38977         }
38978     } 
38979     this.closable = false;
38980     this.loaded = false;
38981     this.active = false;
38982    
38983       
38984     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
38985         
38986         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
38987         
38988         this.wrapEl = this.el; //this.el.wrap();
38989         var ti = [];
38990         if (config.toolbar.items) {
38991             ti = config.toolbar.items ;
38992             delete config.toolbar.items ;
38993         }
38994         
38995         var nitems = [];
38996         this.toolbar.render(this.wrapEl, 'before');
38997         for(var i =0;i < ti.length;i++) {
38998           //  Roo.log(['add child', items[i]]);
38999             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39000         }
39001         this.toolbar.items = nitems;
39002         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39003         delete config.toolbar;
39004         
39005     }
39006     /*
39007     // xtype created footer. - not sure if will work as we normally have to render first..
39008     if (this.footer && !this.footer.el && this.footer.xtype) {
39009         if (!this.wrapEl) {
39010             this.wrapEl = this.el.wrap();
39011         }
39012     
39013         this.footer.container = this.wrapEl.createChild();
39014          
39015         this.footer = Roo.factory(this.footer, Roo);
39016         
39017     }
39018     */
39019     
39020      if(typeof config == "string"){
39021         this.title = config;
39022     }else{
39023         Roo.apply(this, config);
39024     }
39025     
39026     if(this.resizeEl){
39027         this.resizeEl = Roo.get(this.resizeEl, true);
39028     }else{
39029         this.resizeEl = this.el;
39030     }
39031     // handle view.xtype
39032     
39033  
39034     
39035     
39036     this.addEvents({
39037         /**
39038          * @event activate
39039          * Fires when this panel is activated. 
39040          * @param {Roo.ContentPanel} this
39041          */
39042         "activate" : true,
39043         /**
39044          * @event deactivate
39045          * Fires when this panel is activated. 
39046          * @param {Roo.ContentPanel} this
39047          */
39048         "deactivate" : true,
39049
39050         /**
39051          * @event resize
39052          * Fires when this panel is resized if fitToFrame is true.
39053          * @param {Roo.ContentPanel} this
39054          * @param {Number} width The width after any component adjustments
39055          * @param {Number} height The height after any component adjustments
39056          */
39057         "resize" : true,
39058         
39059          /**
39060          * @event render
39061          * Fires when this tab is created
39062          * @param {Roo.ContentPanel} this
39063          */
39064         "render" : true
39065         
39066         
39067         
39068     });
39069     
39070
39071     
39072     
39073     if(this.autoScroll){
39074         this.resizeEl.setStyle("overflow", "auto");
39075     } else {
39076         // fix randome scrolling
39077         //this.el.on('scroll', function() {
39078         //    Roo.log('fix random scolling');
39079         //    this.scrollTo('top',0); 
39080         //});
39081     }
39082     content = content || this.content;
39083     if(content){
39084         this.setContent(content);
39085     }
39086     if(config && config.url){
39087         this.setUrl(this.url, this.params, this.loadOnce);
39088     }
39089     
39090     
39091     
39092     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39093     
39094     if (this.view && typeof(this.view.xtype) != 'undefined') {
39095         this.view.el = this.el.appendChild(document.createElement("div"));
39096         this.view = Roo.factory(this.view); 
39097         this.view.render  &&  this.view.render(false, '');  
39098     }
39099     
39100     
39101     this.fireEvent('render', this);
39102 };
39103
39104 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39105     
39106     tabTip : '',
39107     
39108     setRegion : function(region){
39109         this.region = region;
39110         this.setActiveClass(region && !this.background);
39111     },
39112     
39113     
39114     setActiveClass: function(state)
39115     {
39116         if(state){
39117            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39118            this.el.setStyle('position','relative');
39119         }else{
39120            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39121            this.el.setStyle('position', 'absolute');
39122         } 
39123     },
39124     
39125     /**
39126      * Returns the toolbar for this Panel if one was configured. 
39127      * @return {Roo.Toolbar} 
39128      */
39129     getToolbar : function(){
39130         return this.toolbar;
39131     },
39132     
39133     setActiveState : function(active)
39134     {
39135         this.active = active;
39136         this.setActiveClass(active);
39137         if(!active){
39138             if(this.fireEvent("deactivate", this) === false){
39139                 return false;
39140             }
39141             return true;
39142         }
39143         this.fireEvent("activate", this);
39144         return true;
39145     },
39146     /**
39147      * Updates this panel's element
39148      * @param {String} content The new content
39149      * @param {Boolean} loadScripts (optional) true to look for and process scripts
39150     */
39151     setContent : function(content, loadScripts){
39152         this.el.update(content, loadScripts);
39153     },
39154
39155     ignoreResize : function(w, h){
39156         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39157             return true;
39158         }else{
39159             this.lastSize = {width: w, height: h};
39160             return false;
39161         }
39162     },
39163     /**
39164      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39165      * @return {Roo.UpdateManager} The UpdateManager
39166      */
39167     getUpdateManager : function(){
39168         return this.el.getUpdateManager();
39169     },
39170      /**
39171      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39172      * @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:
39173 <pre><code>
39174 panel.load({
39175     url: "your-url.php",
39176     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39177     callback: yourFunction,
39178     scope: yourObject, //(optional scope)
39179     discardUrl: false,
39180     nocache: false,
39181     text: "Loading...",
39182     timeout: 30,
39183     scripts: false
39184 });
39185 </code></pre>
39186      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39187      * 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.
39188      * @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}
39189      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39190      * @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.
39191      * @return {Roo.ContentPanel} this
39192      */
39193     load : function(){
39194         var um = this.el.getUpdateManager();
39195         um.update.apply(um, arguments);
39196         return this;
39197     },
39198
39199
39200     /**
39201      * 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.
39202      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39203      * @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)
39204      * @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)
39205      * @return {Roo.UpdateManager} The UpdateManager
39206      */
39207     setUrl : function(url, params, loadOnce){
39208         if(this.refreshDelegate){
39209             this.removeListener("activate", this.refreshDelegate);
39210         }
39211         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39212         this.on("activate", this.refreshDelegate);
39213         return this.el.getUpdateManager();
39214     },
39215     
39216     _handleRefresh : function(url, params, loadOnce){
39217         if(!loadOnce || !this.loaded){
39218             var updater = this.el.getUpdateManager();
39219             updater.update(url, params, this._setLoaded.createDelegate(this));
39220         }
39221     },
39222     
39223     _setLoaded : function(){
39224         this.loaded = true;
39225     }, 
39226     
39227     /**
39228      * Returns this panel's id
39229      * @return {String} 
39230      */
39231     getId : function(){
39232         return this.el.id;
39233     },
39234     
39235     /** 
39236      * Returns this panel's element - used by regiosn to add.
39237      * @return {Roo.Element} 
39238      */
39239     getEl : function(){
39240         return this.wrapEl || this.el;
39241     },
39242     
39243    
39244     
39245     adjustForComponents : function(width, height)
39246     {
39247         //Roo.log('adjustForComponents ');
39248         if(this.resizeEl != this.el){
39249             width -= this.el.getFrameWidth('lr');
39250             height -= this.el.getFrameWidth('tb');
39251         }
39252         if(this.toolbar){
39253             var te = this.toolbar.getEl();
39254             te.setWidth(width);
39255             height -= te.getHeight();
39256         }
39257         if(this.footer){
39258             var te = this.footer.getEl();
39259             te.setWidth(width);
39260             height -= te.getHeight();
39261         }
39262         
39263         
39264         if(this.adjustments){
39265             width += this.adjustments[0];
39266             height += this.adjustments[1];
39267         }
39268         return {"width": width, "height": height};
39269     },
39270     
39271     setSize : function(width, height){
39272         if(this.fitToFrame && !this.ignoreResize(width, height)){
39273             if(this.fitContainer && this.resizeEl != this.el){
39274                 this.el.setSize(width, height);
39275             }
39276             var size = this.adjustForComponents(width, height);
39277             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39278             this.fireEvent('resize', this, size.width, size.height);
39279         }
39280     },
39281     
39282     /**
39283      * Returns this panel's title
39284      * @return {String} 
39285      */
39286     getTitle : function(){
39287         
39288         if (typeof(this.title) != 'object') {
39289             return this.title;
39290         }
39291         
39292         var t = '';
39293         for (var k in this.title) {
39294             if (!this.title.hasOwnProperty(k)) {
39295                 continue;
39296             }
39297             
39298             if (k.indexOf('-') >= 0) {
39299                 var s = k.split('-');
39300                 for (var i = 0; i<s.length; i++) {
39301                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
39302                 }
39303             } else {
39304                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
39305             }
39306         }
39307         return t;
39308     },
39309     
39310     /**
39311      * Set this panel's title
39312      * @param {String} title
39313      */
39314     setTitle : function(title){
39315         this.title = title;
39316         if(this.region){
39317             this.region.updatePanelTitle(this, title);
39318         }
39319     },
39320     
39321     /**
39322      * Returns true is this panel was configured to be closable
39323      * @return {Boolean} 
39324      */
39325     isClosable : function(){
39326         return this.closable;
39327     },
39328     
39329     beforeSlide : function(){
39330         this.el.clip();
39331         this.resizeEl.clip();
39332     },
39333     
39334     afterSlide : function(){
39335         this.el.unclip();
39336         this.resizeEl.unclip();
39337     },
39338     
39339     /**
39340      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
39341      *   Will fail silently if the {@link #setUrl} method has not been called.
39342      *   This does not activate the panel, just updates its content.
39343      */
39344     refresh : function(){
39345         if(this.refreshDelegate){
39346            this.loaded = false;
39347            this.refreshDelegate();
39348         }
39349     },
39350     
39351     /**
39352      * Destroys this panel
39353      */
39354     destroy : function(){
39355         this.el.removeAllListeners();
39356         var tempEl = document.createElement("span");
39357         tempEl.appendChild(this.el.dom);
39358         tempEl.innerHTML = "";
39359         this.el.remove();
39360         this.el = null;
39361     },
39362     
39363     /**
39364      * form - if the content panel contains a form - this is a reference to it.
39365      * @type {Roo.form.Form}
39366      */
39367     form : false,
39368     /**
39369      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
39370      *    This contains a reference to it.
39371      * @type {Roo.View}
39372      */
39373     view : false,
39374     
39375       /**
39376      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
39377      * <pre><code>
39378
39379 layout.addxtype({
39380        xtype : 'Form',
39381        items: [ .... ]
39382    }
39383 );
39384
39385 </code></pre>
39386      * @param {Object} cfg Xtype definition of item to add.
39387      */
39388     
39389     
39390     getChildContainer: function () {
39391         return this.getEl();
39392     }
39393     
39394     
39395     /*
39396         var  ret = new Roo.factory(cfg);
39397         return ret;
39398         
39399         
39400         // add form..
39401         if (cfg.xtype.match(/^Form$/)) {
39402             
39403             var el;
39404             //if (this.footer) {
39405             //    el = this.footer.container.insertSibling(false, 'before');
39406             //} else {
39407                 el = this.el.createChild();
39408             //}
39409
39410             this.form = new  Roo.form.Form(cfg);
39411             
39412             
39413             if ( this.form.allItems.length) {
39414                 this.form.render(el.dom);
39415             }
39416             return this.form;
39417         }
39418         // should only have one of theses..
39419         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
39420             // views.. should not be just added - used named prop 'view''
39421             
39422             cfg.el = this.el.appendChild(document.createElement("div"));
39423             // factory?
39424             
39425             var ret = new Roo.factory(cfg);
39426              
39427              ret.render && ret.render(false, ''); // render blank..
39428             this.view = ret;
39429             return ret;
39430         }
39431         return false;
39432     }
39433     \*/
39434 });
39435  
39436 /**
39437  * @class Roo.bootstrap.panel.Grid
39438  * @extends Roo.bootstrap.panel.Content
39439  * @constructor
39440  * Create a new GridPanel.
39441  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
39442  * @param {Object} config A the config object
39443   
39444  */
39445
39446
39447
39448 Roo.bootstrap.panel.Grid = function(config)
39449 {
39450     
39451       
39452     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
39453         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
39454
39455     config.el = this.wrapper;
39456     //this.el = this.wrapper;
39457     
39458       if (config.container) {
39459         // ctor'ed from a Border/panel.grid
39460         
39461         
39462         this.wrapper.setStyle("overflow", "hidden");
39463         this.wrapper.addClass('roo-grid-container');
39464
39465     }
39466     
39467     
39468     if(config.toolbar){
39469         var tool_el = this.wrapper.createChild();    
39470         this.toolbar = Roo.factory(config.toolbar);
39471         var ti = [];
39472         if (config.toolbar.items) {
39473             ti = config.toolbar.items ;
39474             delete config.toolbar.items ;
39475         }
39476         
39477         var nitems = [];
39478         this.toolbar.render(tool_el);
39479         for(var i =0;i < ti.length;i++) {
39480           //  Roo.log(['add child', items[i]]);
39481             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39482         }
39483         this.toolbar.items = nitems;
39484         
39485         delete config.toolbar;
39486     }
39487     
39488     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
39489     config.grid.scrollBody = true;;
39490     config.grid.monitorWindowResize = false; // turn off autosizing
39491     config.grid.autoHeight = false;
39492     config.grid.autoWidth = false;
39493     
39494     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
39495     
39496     if (config.background) {
39497         // render grid on panel activation (if panel background)
39498         this.on('activate', function(gp) {
39499             if (!gp.grid.rendered) {
39500                 gp.grid.render(this.wrapper);
39501                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
39502             }
39503         });
39504             
39505     } else {
39506         this.grid.render(this.wrapper);
39507         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
39508
39509     }
39510     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
39511     // ??? needed ??? config.el = this.wrapper;
39512     
39513     
39514     
39515   
39516     // xtype created footer. - not sure if will work as we normally have to render first..
39517     if (this.footer && !this.footer.el && this.footer.xtype) {
39518         
39519         var ctr = this.grid.getView().getFooterPanel(true);
39520         this.footer.dataSource = this.grid.dataSource;
39521         this.footer = Roo.factory(this.footer, Roo);
39522         this.footer.render(ctr);
39523         
39524     }
39525     
39526     
39527     
39528     
39529      
39530 };
39531
39532 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
39533     getId : function(){
39534         return this.grid.id;
39535     },
39536     
39537     /**
39538      * Returns the grid for this panel
39539      * @return {Roo.bootstrap.Table} 
39540      */
39541     getGrid : function(){
39542         return this.grid;    
39543     },
39544     
39545     setSize : function(width, height){
39546         if(!this.ignoreResize(width, height)){
39547             var grid = this.grid;
39548             var size = this.adjustForComponents(width, height);
39549             var gridel = grid.getGridEl();
39550             gridel.setSize(size.width, size.height);
39551             /*
39552             var thd = grid.getGridEl().select('thead',true).first();
39553             var tbd = grid.getGridEl().select('tbody', true).first();
39554             if (tbd) {
39555                 tbd.setSize(width, height - thd.getHeight());
39556             }
39557             */
39558             grid.autoSize();
39559         }
39560     },
39561      
39562     
39563     
39564     beforeSlide : function(){
39565         this.grid.getView().scroller.clip();
39566     },
39567     
39568     afterSlide : function(){
39569         this.grid.getView().scroller.unclip();
39570     },
39571     
39572     destroy : function(){
39573         this.grid.destroy();
39574         delete this.grid;
39575         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
39576     }
39577 });
39578
39579 /**
39580  * @class Roo.bootstrap.panel.Nest
39581  * @extends Roo.bootstrap.panel.Content
39582  * @constructor
39583  * Create a new Panel, that can contain a layout.Border.
39584  * 
39585  * 
39586  * @param {Roo.BorderLayout} layout The layout for this panel
39587  * @param {String/Object} config A string to set only the title or a config object
39588  */
39589 Roo.bootstrap.panel.Nest = function(config)
39590 {
39591     // construct with only one argument..
39592     /* FIXME - implement nicer consturctors
39593     if (layout.layout) {
39594         config = layout;
39595         layout = config.layout;
39596         delete config.layout;
39597     }
39598     if (layout.xtype && !layout.getEl) {
39599         // then layout needs constructing..
39600         layout = Roo.factory(layout, Roo);
39601     }
39602     */
39603     
39604     config.el =  config.layout.getEl();
39605     
39606     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
39607     
39608     config.layout.monitorWindowResize = false; // turn off autosizing
39609     this.layout = config.layout;
39610     this.layout.getEl().addClass("roo-layout-nested-layout");
39611     this.layout.parent = this;
39612     
39613     
39614     
39615     
39616 };
39617
39618 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
39619
39620     setSize : function(width, height){
39621         if(!this.ignoreResize(width, height)){
39622             var size = this.adjustForComponents(width, height);
39623             var el = this.layout.getEl();
39624             if (size.height < 1) {
39625                 el.setWidth(size.width);   
39626             } else {
39627                 el.setSize(size.width, size.height);
39628             }
39629             var touch = el.dom.offsetWidth;
39630             this.layout.layout();
39631             // ie requires a double layout on the first pass
39632             if(Roo.isIE && !this.initialized){
39633                 this.initialized = true;
39634                 this.layout.layout();
39635             }
39636         }
39637     },
39638     
39639     // activate all subpanels if not currently active..
39640     
39641     setActiveState : function(active){
39642         this.active = active;
39643         this.setActiveClass(active);
39644         
39645         if(!active){
39646             this.fireEvent("deactivate", this);
39647             return;
39648         }
39649         
39650         this.fireEvent("activate", this);
39651         // not sure if this should happen before or after..
39652         if (!this.layout) {
39653             return; // should not happen..
39654         }
39655         var reg = false;
39656         for (var r in this.layout.regions) {
39657             reg = this.layout.getRegion(r);
39658             if (reg.getActivePanel()) {
39659                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
39660                 reg.setActivePanel(reg.getActivePanel());
39661                 continue;
39662             }
39663             if (!reg.panels.length) {
39664                 continue;
39665             }
39666             reg.showPanel(reg.getPanel(0));
39667         }
39668         
39669         
39670         
39671         
39672     },
39673     
39674     /**
39675      * Returns the nested BorderLayout for this panel
39676      * @return {Roo.BorderLayout} 
39677      */
39678     getLayout : function(){
39679         return this.layout;
39680     },
39681     
39682      /**
39683      * Adds a xtype elements to the layout of the nested panel
39684      * <pre><code>
39685
39686 panel.addxtype({
39687        xtype : 'ContentPanel',
39688        region: 'west',
39689        items: [ .... ]
39690    }
39691 );
39692
39693 panel.addxtype({
39694         xtype : 'NestedLayoutPanel',
39695         region: 'west',
39696         layout: {
39697            center: { },
39698            west: { }   
39699         },
39700         items : [ ... list of content panels or nested layout panels.. ]
39701    }
39702 );
39703 </code></pre>
39704      * @param {Object} cfg Xtype definition of item to add.
39705      */
39706     addxtype : function(cfg) {
39707         return this.layout.addxtype(cfg);
39708     
39709     }
39710 });/*
39711  * Based on:
39712  * Ext JS Library 1.1.1
39713  * Copyright(c) 2006-2007, Ext JS, LLC.
39714  *
39715  * Originally Released Under LGPL - original licence link has changed is not relivant.
39716  *
39717  * Fork - LGPL
39718  * <script type="text/javascript">
39719  */
39720 /**
39721  * @class Roo.TabPanel
39722  * @extends Roo.util.Observable
39723  * A lightweight tab container.
39724  * <br><br>
39725  * Usage:
39726  * <pre><code>
39727 // basic tabs 1, built from existing content
39728 var tabs = new Roo.TabPanel("tabs1");
39729 tabs.addTab("script", "View Script");
39730 tabs.addTab("markup", "View Markup");
39731 tabs.activate("script");
39732
39733 // more advanced tabs, built from javascript
39734 var jtabs = new Roo.TabPanel("jtabs");
39735 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
39736
39737 // set up the UpdateManager
39738 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
39739 var updater = tab2.getUpdateManager();
39740 updater.setDefaultUrl("ajax1.htm");
39741 tab2.on('activate', updater.refresh, updater, true);
39742
39743 // Use setUrl for Ajax loading
39744 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
39745 tab3.setUrl("ajax2.htm", null, true);
39746
39747 // Disabled tab
39748 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
39749 tab4.disable();
39750
39751 jtabs.activate("jtabs-1");
39752  * </code></pre>
39753  * @constructor
39754  * Create a new TabPanel.
39755  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
39756  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
39757  */
39758 Roo.bootstrap.panel.Tabs = function(config){
39759     /**
39760     * The container element for this TabPanel.
39761     * @type Roo.Element
39762     */
39763     this.el = Roo.get(config.el);
39764     delete config.el;
39765     if(config){
39766         if(typeof config == "boolean"){
39767             this.tabPosition = config ? "bottom" : "top";
39768         }else{
39769             Roo.apply(this, config);
39770         }
39771     }
39772     
39773     if(this.tabPosition == "bottom"){
39774         // if tabs are at the bottom = create the body first.
39775         this.bodyEl = Roo.get(this.createBody(this.el.dom));
39776         this.el.addClass("roo-tabs-bottom");
39777     }
39778     // next create the tabs holders
39779     
39780     if (this.tabPosition == "west"){
39781         
39782         var reg = this.region; // fake it..
39783         while (reg) {
39784             if (!reg.mgr.parent) {
39785                 break;
39786             }
39787             reg = reg.mgr.parent.region;
39788         }
39789         Roo.log("got nest?");
39790         Roo.log(reg);
39791         if (reg.mgr.getRegion('west')) {
39792             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
39793             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
39794             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
39795             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
39796             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
39797         
39798             
39799         }
39800         
39801         
39802     } else {
39803      
39804         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
39805         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
39806         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
39807         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
39808     }
39809     
39810     
39811     if(Roo.isIE){
39812         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
39813     }
39814     
39815     // finally - if tabs are at the top, then create the body last..
39816     if(this.tabPosition != "bottom"){
39817         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
39818          * @type Roo.Element
39819          */
39820         this.bodyEl = Roo.get(this.createBody(this.el.dom));
39821         this.el.addClass("roo-tabs-top");
39822     }
39823     this.items = [];
39824
39825     this.bodyEl.setStyle("position", "relative");
39826
39827     this.active = null;
39828     this.activateDelegate = this.activate.createDelegate(this);
39829
39830     this.addEvents({
39831         /**
39832          * @event tabchange
39833          * Fires when the active tab changes
39834          * @param {Roo.TabPanel} this
39835          * @param {Roo.TabPanelItem} activePanel The new active tab
39836          */
39837         "tabchange": true,
39838         /**
39839          * @event beforetabchange
39840          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
39841          * @param {Roo.TabPanel} this
39842          * @param {Object} e Set cancel to true on this object to cancel the tab change
39843          * @param {Roo.TabPanelItem} tab The tab being changed to
39844          */
39845         "beforetabchange" : true
39846     });
39847
39848     Roo.EventManager.onWindowResize(this.onResize, this);
39849     this.cpad = this.el.getPadding("lr");
39850     this.hiddenCount = 0;
39851
39852
39853     // toolbar on the tabbar support...
39854     if (this.toolbar) {
39855         alert("no toolbar support yet");
39856         this.toolbar  = false;
39857         /*
39858         var tcfg = this.toolbar;
39859         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
39860         this.toolbar = new Roo.Toolbar(tcfg);
39861         if (Roo.isSafari) {
39862             var tbl = tcfg.container.child('table', true);
39863             tbl.setAttribute('width', '100%');
39864         }
39865         */
39866         
39867     }
39868    
39869
39870
39871     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
39872 };
39873
39874 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
39875     /*
39876      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
39877      */
39878     tabPosition : "top",
39879     /*
39880      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
39881      */
39882     currentTabWidth : 0,
39883     /*
39884      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
39885      */
39886     minTabWidth : 40,
39887     /*
39888      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
39889      */
39890     maxTabWidth : 250,
39891     /*
39892      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
39893      */
39894     preferredTabWidth : 175,
39895     /*
39896      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
39897      */
39898     resizeTabs : false,
39899     /*
39900      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
39901      */
39902     monitorResize : true,
39903     /*
39904      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
39905      */
39906     toolbar : false,  // set by caller..
39907     
39908     region : false, /// set by caller
39909     
39910     disableTooltips : true, // not used yet...
39911
39912     /**
39913      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
39914      * @param {String} id The id of the div to use <b>or create</b>
39915      * @param {String} text The text for the tab
39916      * @param {String} content (optional) Content to put in the TabPanelItem body
39917      * @param {Boolean} closable (optional) True to create a close icon on the tab
39918      * @return {Roo.TabPanelItem} The created TabPanelItem
39919      */
39920     addTab : function(id, text, content, closable, tpl)
39921     {
39922         var item = new Roo.bootstrap.panel.TabItem({
39923             panel: this,
39924             id : id,
39925             text : text,
39926             closable : closable,
39927             tpl : tpl
39928         });
39929         this.addTabItem(item);
39930         if(content){
39931             item.setContent(content);
39932         }
39933         return item;
39934     },
39935
39936     /**
39937      * Returns the {@link Roo.TabPanelItem} with the specified id/index
39938      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
39939      * @return {Roo.TabPanelItem}
39940      */
39941     getTab : function(id){
39942         return this.items[id];
39943     },
39944
39945     /**
39946      * Hides the {@link Roo.TabPanelItem} with the specified id/index
39947      * @param {String/Number} id The id or index of the TabPanelItem to hide.
39948      */
39949     hideTab : function(id){
39950         var t = this.items[id];
39951         if(!t.isHidden()){
39952            t.setHidden(true);
39953            this.hiddenCount++;
39954            this.autoSizeTabs();
39955         }
39956     },
39957
39958     /**
39959      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
39960      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
39961      */
39962     unhideTab : function(id){
39963         var t = this.items[id];
39964         if(t.isHidden()){
39965            t.setHidden(false);
39966            this.hiddenCount--;
39967            this.autoSizeTabs();
39968         }
39969     },
39970
39971     /**
39972      * Adds an existing {@link Roo.TabPanelItem}.
39973      * @param {Roo.TabPanelItem} item The TabPanelItem to add
39974      */
39975     addTabItem : function(item)
39976     {
39977         this.items[item.id] = item;
39978         this.items.push(item);
39979         this.autoSizeTabs();
39980       //  if(this.resizeTabs){
39981     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
39982   //         this.autoSizeTabs();
39983 //        }else{
39984 //            item.autoSize();
39985        // }
39986     },
39987
39988     /**
39989      * Removes a {@link Roo.TabPanelItem}.
39990      * @param {String/Number} id The id or index of the TabPanelItem to remove.
39991      */
39992     removeTab : function(id){
39993         var items = this.items;
39994         var tab = items[id];
39995         if(!tab) { return; }
39996         var index = items.indexOf(tab);
39997         if(this.active == tab && items.length > 1){
39998             var newTab = this.getNextAvailable(index);
39999             if(newTab) {
40000                 newTab.activate();
40001             }
40002         }
40003         this.stripEl.dom.removeChild(tab.pnode.dom);
40004         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40005             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40006         }
40007         items.splice(index, 1);
40008         delete this.items[tab.id];
40009         tab.fireEvent("close", tab);
40010         tab.purgeListeners();
40011         this.autoSizeTabs();
40012     },
40013
40014     getNextAvailable : function(start){
40015         var items = this.items;
40016         var index = start;
40017         // look for a next tab that will slide over to
40018         // replace the one being removed
40019         while(index < items.length){
40020             var item = items[++index];
40021             if(item && !item.isHidden()){
40022                 return item;
40023             }
40024         }
40025         // if one isn't found select the previous tab (on the left)
40026         index = start;
40027         while(index >= 0){
40028             var item = items[--index];
40029             if(item && !item.isHidden()){
40030                 return item;
40031             }
40032         }
40033         return null;
40034     },
40035
40036     /**
40037      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40038      * @param {String/Number} id The id or index of the TabPanelItem to disable.
40039      */
40040     disableTab : function(id){
40041         var tab = this.items[id];
40042         if(tab && this.active != tab){
40043             tab.disable();
40044         }
40045     },
40046
40047     /**
40048      * Enables a {@link Roo.TabPanelItem} that is disabled.
40049      * @param {String/Number} id The id or index of the TabPanelItem to enable.
40050      */
40051     enableTab : function(id){
40052         var tab = this.items[id];
40053         tab.enable();
40054     },
40055
40056     /**
40057      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40058      * @param {String/Number} id The id or index of the TabPanelItem to activate.
40059      * @return {Roo.TabPanelItem} The TabPanelItem.
40060      */
40061     activate : function(id)
40062     {
40063         //Roo.log('activite:'  + id);
40064         
40065         var tab = this.items[id];
40066         if(!tab){
40067             return null;
40068         }
40069         if(tab == this.active || tab.disabled){
40070             return tab;
40071         }
40072         var e = {};
40073         this.fireEvent("beforetabchange", this, e, tab);
40074         if(e.cancel !== true && !tab.disabled){
40075             if(this.active){
40076                 this.active.hide();
40077             }
40078             this.active = this.items[id];
40079             this.active.show();
40080             this.fireEvent("tabchange", this, this.active);
40081         }
40082         return tab;
40083     },
40084
40085     /**
40086      * Gets the active {@link Roo.TabPanelItem}.
40087      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40088      */
40089     getActiveTab : function(){
40090         return this.active;
40091     },
40092
40093     /**
40094      * Updates the tab body element to fit the height of the container element
40095      * for overflow scrolling
40096      * @param {Number} targetHeight (optional) Override the starting height from the elements height
40097      */
40098     syncHeight : function(targetHeight){
40099         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40100         var bm = this.bodyEl.getMargins();
40101         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40102         this.bodyEl.setHeight(newHeight);
40103         return newHeight;
40104     },
40105
40106     onResize : function(){
40107         if(this.monitorResize){
40108             this.autoSizeTabs();
40109         }
40110     },
40111
40112     /**
40113      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40114      */
40115     beginUpdate : function(){
40116         this.updating = true;
40117     },
40118
40119     /**
40120      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40121      */
40122     endUpdate : function(){
40123         this.updating = false;
40124         this.autoSizeTabs();
40125     },
40126
40127     /**
40128      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40129      */
40130     autoSizeTabs : function()
40131     {
40132         var count = this.items.length;
40133         var vcount = count - this.hiddenCount;
40134         
40135         if (vcount < 2) {
40136             this.stripEl.hide();
40137         } else {
40138             this.stripEl.show();
40139         }
40140         
40141         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40142             return;
40143         }
40144         
40145         
40146         var w = Math.max(this.el.getWidth() - this.cpad, 10);
40147         var availWidth = Math.floor(w / vcount);
40148         var b = this.stripBody;
40149         if(b.getWidth() > w){
40150             var tabs = this.items;
40151             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40152             if(availWidth < this.minTabWidth){
40153                 /*if(!this.sleft){    // incomplete scrolling code
40154                     this.createScrollButtons();
40155                 }
40156                 this.showScroll();
40157                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40158             }
40159         }else{
40160             if(this.currentTabWidth < this.preferredTabWidth){
40161                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40162             }
40163         }
40164     },
40165
40166     /**
40167      * Returns the number of tabs in this TabPanel.
40168      * @return {Number}
40169      */
40170      getCount : function(){
40171          return this.items.length;
40172      },
40173
40174     /**
40175      * Resizes all the tabs to the passed width
40176      * @param {Number} The new width
40177      */
40178     setTabWidth : function(width){
40179         this.currentTabWidth = width;
40180         for(var i = 0, len = this.items.length; i < len; i++) {
40181                 if(!this.items[i].isHidden()) {
40182                 this.items[i].setWidth(width);
40183             }
40184         }
40185     },
40186
40187     /**
40188      * Destroys this TabPanel
40189      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40190      */
40191     destroy : function(removeEl){
40192         Roo.EventManager.removeResizeListener(this.onResize, this);
40193         for(var i = 0, len = this.items.length; i < len; i++){
40194             this.items[i].purgeListeners();
40195         }
40196         if(removeEl === true){
40197             this.el.update("");
40198             this.el.remove();
40199         }
40200     },
40201     
40202     createStrip : function(container)
40203     {
40204         var strip = document.createElement("nav");
40205         strip.className = Roo.bootstrap.version == 4 ?
40206             "navbar-light bg-light" : 
40207             "navbar navbar-default"; //"x-tabs-wrap";
40208         container.appendChild(strip);
40209         return strip;
40210     },
40211     
40212     createStripList : function(strip)
40213     {
40214         // div wrapper for retard IE
40215         // returns the "tr" element.
40216         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40217         //'<div class="x-tabs-strip-wrap">'+
40218           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40219           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40220         return strip.firstChild; //.firstChild.firstChild.firstChild;
40221     },
40222     createBody : function(container)
40223     {
40224         var body = document.createElement("div");
40225         Roo.id(body, "tab-body");
40226         //Roo.fly(body).addClass("x-tabs-body");
40227         Roo.fly(body).addClass("tab-content");
40228         container.appendChild(body);
40229         return body;
40230     },
40231     createItemBody :function(bodyEl, id){
40232         var body = Roo.getDom(id);
40233         if(!body){
40234             body = document.createElement("div");
40235             body.id = id;
40236         }
40237         //Roo.fly(body).addClass("x-tabs-item-body");
40238         Roo.fly(body).addClass("tab-pane");
40239          bodyEl.insertBefore(body, bodyEl.firstChild);
40240         return body;
40241     },
40242     /** @private */
40243     createStripElements :  function(stripEl, text, closable, tpl)
40244     {
40245         var td = document.createElement("li"); // was td..
40246         td.className = 'nav-item';
40247         
40248         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40249         
40250         
40251         stripEl.appendChild(td);
40252         /*if(closable){
40253             td.className = "x-tabs-closable";
40254             if(!this.closeTpl){
40255                 this.closeTpl = new Roo.Template(
40256                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40257                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40258                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
40259                 );
40260             }
40261             var el = this.closeTpl.overwrite(td, {"text": text});
40262             var close = el.getElementsByTagName("div")[0];
40263             var inner = el.getElementsByTagName("em")[0];
40264             return {"el": el, "close": close, "inner": inner};
40265         } else {
40266         */
40267         // not sure what this is..
40268 //            if(!this.tabTpl){
40269                 //this.tabTpl = new Roo.Template(
40270                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40271                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40272                 //);
40273 //                this.tabTpl = new Roo.Template(
40274 //                   '<a href="#">' +
40275 //                   '<span unselectable="on"' +
40276 //                            (this.disableTooltips ? '' : ' title="{text}"') +
40277 //                            ' >{text}</span></a>'
40278 //                );
40279 //                
40280 //            }
40281
40282
40283             var template = tpl || this.tabTpl || false;
40284             
40285             if(!template){
40286                 template =  new Roo.Template(
40287                         Roo.bootstrap.version == 4 ? 
40288                             (
40289                                 '<a class="nav-link" href="#" unselectable="on"' +
40290                                      (this.disableTooltips ? '' : ' title="{text}"') +
40291                                      ' >{text}</a>'
40292                             ) : (
40293                                 '<a class="nav-link" href="#">' +
40294                                 '<span unselectable="on"' +
40295                                          (this.disableTooltips ? '' : ' title="{text}"') +
40296                                     ' >{text}</span></a>'
40297                             )
40298                 );
40299             }
40300             
40301             switch (typeof(template)) {
40302                 case 'object' :
40303                     break;
40304                 case 'string' :
40305                     template = new Roo.Template(template);
40306                     break;
40307                 default :
40308                     break;
40309             }
40310             
40311             var el = template.overwrite(td, {"text": text});
40312             
40313             var inner = el.getElementsByTagName("span")[0];
40314             
40315             return {"el": el, "inner": inner};
40316             
40317     }
40318         
40319     
40320 });
40321
40322 /**
40323  * @class Roo.TabPanelItem
40324  * @extends Roo.util.Observable
40325  * Represents an individual item (tab plus body) in a TabPanel.
40326  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
40327  * @param {String} id The id of this TabPanelItem
40328  * @param {String} text The text for the tab of this TabPanelItem
40329  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
40330  */
40331 Roo.bootstrap.panel.TabItem = function(config){
40332     /**
40333      * The {@link Roo.TabPanel} this TabPanelItem belongs to
40334      * @type Roo.TabPanel
40335      */
40336     this.tabPanel = config.panel;
40337     /**
40338      * The id for this TabPanelItem
40339      * @type String
40340      */
40341     this.id = config.id;
40342     /** @private */
40343     this.disabled = false;
40344     /** @private */
40345     this.text = config.text;
40346     /** @private */
40347     this.loaded = false;
40348     this.closable = config.closable;
40349
40350     /**
40351      * The body element for this TabPanelItem.
40352      * @type Roo.Element
40353      */
40354     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
40355     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
40356     this.bodyEl.setStyle("display", "block");
40357     this.bodyEl.setStyle("zoom", "1");
40358     //this.hideAction();
40359
40360     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
40361     /** @private */
40362     this.el = Roo.get(els.el);
40363     this.inner = Roo.get(els.inner, true);
40364      this.textEl = Roo.bootstrap.version == 4 ?
40365         this.el : Roo.get(this.el.dom.firstChild, true);
40366
40367     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
40368     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
40369
40370     
40371 //    this.el.on("mousedown", this.onTabMouseDown, this);
40372     this.el.on("click", this.onTabClick, this);
40373     /** @private */
40374     if(config.closable){
40375         var c = Roo.get(els.close, true);
40376         c.dom.title = this.closeText;
40377         c.addClassOnOver("close-over");
40378         c.on("click", this.closeClick, this);
40379      }
40380
40381     this.addEvents({
40382          /**
40383          * @event activate
40384          * Fires when this tab becomes the active tab.
40385          * @param {Roo.TabPanel} tabPanel The parent TabPanel
40386          * @param {Roo.TabPanelItem} this
40387          */
40388         "activate": true,
40389         /**
40390          * @event beforeclose
40391          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
40392          * @param {Roo.TabPanelItem} this
40393          * @param {Object} e Set cancel to true on this object to cancel the close.
40394          */
40395         "beforeclose": true,
40396         /**
40397          * @event close
40398          * Fires when this tab is closed.
40399          * @param {Roo.TabPanelItem} this
40400          */
40401          "close": true,
40402         /**
40403          * @event deactivate
40404          * Fires when this tab is no longer the active tab.
40405          * @param {Roo.TabPanel} tabPanel The parent TabPanel
40406          * @param {Roo.TabPanelItem} this
40407          */
40408          "deactivate" : true
40409     });
40410     this.hidden = false;
40411
40412     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
40413 };
40414
40415 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
40416            {
40417     purgeListeners : function(){
40418        Roo.util.Observable.prototype.purgeListeners.call(this);
40419        this.el.removeAllListeners();
40420     },
40421     /**
40422      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
40423      */
40424     show : function(){
40425         this.status_node.addClass("active");
40426         this.showAction();
40427         if(Roo.isOpera){
40428             this.tabPanel.stripWrap.repaint();
40429         }
40430         this.fireEvent("activate", this.tabPanel, this);
40431     },
40432
40433     /**
40434      * Returns true if this tab is the active tab.
40435      * @return {Boolean}
40436      */
40437     isActive : function(){
40438         return this.tabPanel.getActiveTab() == this;
40439     },
40440
40441     /**
40442      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
40443      */
40444     hide : function(){
40445         this.status_node.removeClass("active");
40446         this.hideAction();
40447         this.fireEvent("deactivate", this.tabPanel, this);
40448     },
40449
40450     hideAction : function(){
40451         this.bodyEl.hide();
40452         this.bodyEl.setStyle("position", "absolute");
40453         this.bodyEl.setLeft("-20000px");
40454         this.bodyEl.setTop("-20000px");
40455     },
40456
40457     showAction : function(){
40458         this.bodyEl.setStyle("position", "relative");
40459         this.bodyEl.setTop("");
40460         this.bodyEl.setLeft("");
40461         this.bodyEl.show();
40462     },
40463
40464     /**
40465      * Set the tooltip for the tab.
40466      * @param {String} tooltip The tab's tooltip
40467      */
40468     setTooltip : function(text){
40469         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
40470             this.textEl.dom.qtip = text;
40471             this.textEl.dom.removeAttribute('title');
40472         }else{
40473             this.textEl.dom.title = text;
40474         }
40475     },
40476
40477     onTabClick : function(e){
40478         e.preventDefault();
40479         this.tabPanel.activate(this.id);
40480     },
40481
40482     onTabMouseDown : function(e){
40483         e.preventDefault();
40484         this.tabPanel.activate(this.id);
40485     },
40486 /*
40487     getWidth : function(){
40488         return this.inner.getWidth();
40489     },
40490
40491     setWidth : function(width){
40492         var iwidth = width - this.linode.getPadding("lr");
40493         this.inner.setWidth(iwidth);
40494         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
40495         this.linode.setWidth(width);
40496     },
40497 */
40498     /**
40499      * Show or hide the tab
40500      * @param {Boolean} hidden True to hide or false to show.
40501      */
40502     setHidden : function(hidden){
40503         this.hidden = hidden;
40504         this.linode.setStyle("display", hidden ? "none" : "");
40505     },
40506
40507     /**
40508      * Returns true if this tab is "hidden"
40509      * @return {Boolean}
40510      */
40511     isHidden : function(){
40512         return this.hidden;
40513     },
40514
40515     /**
40516      * Returns the text for this tab
40517      * @return {String}
40518      */
40519     getText : function(){
40520         return this.text;
40521     },
40522     /*
40523     autoSize : function(){
40524         //this.el.beginMeasure();
40525         this.textEl.setWidth(1);
40526         /*
40527          *  #2804 [new] Tabs in Roojs
40528          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
40529          */
40530         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
40531         //this.el.endMeasure();
40532     //},
40533
40534     /**
40535      * Sets the text for the tab (Note: this also sets the tooltip text)
40536      * @param {String} text The tab's text and tooltip
40537      */
40538     setText : function(text){
40539         this.text = text;
40540         this.textEl.update(text);
40541         this.setTooltip(text);
40542         //if(!this.tabPanel.resizeTabs){
40543         //    this.autoSize();
40544         //}
40545     },
40546     /**
40547      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
40548      */
40549     activate : function(){
40550         this.tabPanel.activate(this.id);
40551     },
40552
40553     /**
40554      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
40555      */
40556     disable : function(){
40557         if(this.tabPanel.active != this){
40558             this.disabled = true;
40559             this.status_node.addClass("disabled");
40560         }
40561     },
40562
40563     /**
40564      * Enables this TabPanelItem if it was previously disabled.
40565      */
40566     enable : function(){
40567         this.disabled = false;
40568         this.status_node.removeClass("disabled");
40569     },
40570
40571     /**
40572      * Sets the content for this TabPanelItem.
40573      * @param {String} content The content
40574      * @param {Boolean} loadScripts true to look for and load scripts
40575      */
40576     setContent : function(content, loadScripts){
40577         this.bodyEl.update(content, loadScripts);
40578     },
40579
40580     /**
40581      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
40582      * @return {Roo.UpdateManager} The UpdateManager
40583      */
40584     getUpdateManager : function(){
40585         return this.bodyEl.getUpdateManager();
40586     },
40587
40588     /**
40589      * Set a URL to be used to load the content for this TabPanelItem.
40590      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
40591      * @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)
40592      * @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)
40593      * @return {Roo.UpdateManager} The UpdateManager
40594      */
40595     setUrl : function(url, params, loadOnce){
40596         if(this.refreshDelegate){
40597             this.un('activate', this.refreshDelegate);
40598         }
40599         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40600         this.on("activate", this.refreshDelegate);
40601         return this.bodyEl.getUpdateManager();
40602     },
40603
40604     /** @private */
40605     _handleRefresh : function(url, params, loadOnce){
40606         if(!loadOnce || !this.loaded){
40607             var updater = this.bodyEl.getUpdateManager();
40608             updater.update(url, params, this._setLoaded.createDelegate(this));
40609         }
40610     },
40611
40612     /**
40613      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
40614      *   Will fail silently if the setUrl method has not been called.
40615      *   This does not activate the panel, just updates its content.
40616      */
40617     refresh : function(){
40618         if(this.refreshDelegate){
40619            this.loaded = false;
40620            this.refreshDelegate();
40621         }
40622     },
40623
40624     /** @private */
40625     _setLoaded : function(){
40626         this.loaded = true;
40627     },
40628
40629     /** @private */
40630     closeClick : function(e){
40631         var o = {};
40632         e.stopEvent();
40633         this.fireEvent("beforeclose", this, o);
40634         if(o.cancel !== true){
40635             this.tabPanel.removeTab(this.id);
40636         }
40637     },
40638     /**
40639      * The text displayed in the tooltip for the close icon.
40640      * @type String
40641      */
40642     closeText : "Close this tab"
40643 });
40644 /**
40645 *    This script refer to:
40646 *    Title: International Telephone Input
40647 *    Author: Jack O'Connor
40648 *    Code version:  v12.1.12
40649 *    Availability: https://github.com/jackocnr/intl-tel-input.git
40650 **/
40651
40652 Roo.bootstrap.PhoneInputData = function() {
40653     var d = [
40654       [
40655         "Afghanistan (‫افغانستان‬‎)",
40656         "af",
40657         "93"
40658       ],
40659       [
40660         "Albania (Shqipëri)",
40661         "al",
40662         "355"
40663       ],
40664       [
40665         "Algeria (‫الجزائر‬‎)",
40666         "dz",
40667         "213"
40668       ],
40669       [
40670         "American Samoa",
40671         "as",
40672         "1684"
40673       ],
40674       [
40675         "Andorra",
40676         "ad",
40677         "376"
40678       ],
40679       [
40680         "Angola",
40681         "ao",
40682         "244"
40683       ],
40684       [
40685         "Anguilla",
40686         "ai",
40687         "1264"
40688       ],
40689       [
40690         "Antigua and Barbuda",
40691         "ag",
40692         "1268"
40693       ],
40694       [
40695         "Argentina",
40696         "ar",
40697         "54"
40698       ],
40699       [
40700         "Armenia (Հայաստան)",
40701         "am",
40702         "374"
40703       ],
40704       [
40705         "Aruba",
40706         "aw",
40707         "297"
40708       ],
40709       [
40710         "Australia",
40711         "au",
40712         "61",
40713         0
40714       ],
40715       [
40716         "Austria (Österreich)",
40717         "at",
40718         "43"
40719       ],
40720       [
40721         "Azerbaijan (Azərbaycan)",
40722         "az",
40723         "994"
40724       ],
40725       [
40726         "Bahamas",
40727         "bs",
40728         "1242"
40729       ],
40730       [
40731         "Bahrain (‫البحرين‬‎)",
40732         "bh",
40733         "973"
40734       ],
40735       [
40736         "Bangladesh (বাংলাদেশ)",
40737         "bd",
40738         "880"
40739       ],
40740       [
40741         "Barbados",
40742         "bb",
40743         "1246"
40744       ],
40745       [
40746         "Belarus (Беларусь)",
40747         "by",
40748         "375"
40749       ],
40750       [
40751         "Belgium (België)",
40752         "be",
40753         "32"
40754       ],
40755       [
40756         "Belize",
40757         "bz",
40758         "501"
40759       ],
40760       [
40761         "Benin (Bénin)",
40762         "bj",
40763         "229"
40764       ],
40765       [
40766         "Bermuda",
40767         "bm",
40768         "1441"
40769       ],
40770       [
40771         "Bhutan (འབྲུག)",
40772         "bt",
40773         "975"
40774       ],
40775       [
40776         "Bolivia",
40777         "bo",
40778         "591"
40779       ],
40780       [
40781         "Bosnia and Herzegovina (Босна и Херцеговина)",
40782         "ba",
40783         "387"
40784       ],
40785       [
40786         "Botswana",
40787         "bw",
40788         "267"
40789       ],
40790       [
40791         "Brazil (Brasil)",
40792         "br",
40793         "55"
40794       ],
40795       [
40796         "British Indian Ocean Territory",
40797         "io",
40798         "246"
40799       ],
40800       [
40801         "British Virgin Islands",
40802         "vg",
40803         "1284"
40804       ],
40805       [
40806         "Brunei",
40807         "bn",
40808         "673"
40809       ],
40810       [
40811         "Bulgaria (България)",
40812         "bg",
40813         "359"
40814       ],
40815       [
40816         "Burkina Faso",
40817         "bf",
40818         "226"
40819       ],
40820       [
40821         "Burundi (Uburundi)",
40822         "bi",
40823         "257"
40824       ],
40825       [
40826         "Cambodia (កម្ពុជា)",
40827         "kh",
40828         "855"
40829       ],
40830       [
40831         "Cameroon (Cameroun)",
40832         "cm",
40833         "237"
40834       ],
40835       [
40836         "Canada",
40837         "ca",
40838         "1",
40839         1,
40840         ["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"]
40841       ],
40842       [
40843         "Cape Verde (Kabu Verdi)",
40844         "cv",
40845         "238"
40846       ],
40847       [
40848         "Caribbean Netherlands",
40849         "bq",
40850         "599",
40851         1
40852       ],
40853       [
40854         "Cayman Islands",
40855         "ky",
40856         "1345"
40857       ],
40858       [
40859         "Central African Republic (République centrafricaine)",
40860         "cf",
40861         "236"
40862       ],
40863       [
40864         "Chad (Tchad)",
40865         "td",
40866         "235"
40867       ],
40868       [
40869         "Chile",
40870         "cl",
40871         "56"
40872       ],
40873       [
40874         "China (中国)",
40875         "cn",
40876         "86"
40877       ],
40878       [
40879         "Christmas Island",
40880         "cx",
40881         "61",
40882         2
40883       ],
40884       [
40885         "Cocos (Keeling) Islands",
40886         "cc",
40887         "61",
40888         1
40889       ],
40890       [
40891         "Colombia",
40892         "co",
40893         "57"
40894       ],
40895       [
40896         "Comoros (‫جزر القمر‬‎)",
40897         "km",
40898         "269"
40899       ],
40900       [
40901         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
40902         "cd",
40903         "243"
40904       ],
40905       [
40906         "Congo (Republic) (Congo-Brazzaville)",
40907         "cg",
40908         "242"
40909       ],
40910       [
40911         "Cook Islands",
40912         "ck",
40913         "682"
40914       ],
40915       [
40916         "Costa Rica",
40917         "cr",
40918         "506"
40919       ],
40920       [
40921         "Côte d’Ivoire",
40922         "ci",
40923         "225"
40924       ],
40925       [
40926         "Croatia (Hrvatska)",
40927         "hr",
40928         "385"
40929       ],
40930       [
40931         "Cuba",
40932         "cu",
40933         "53"
40934       ],
40935       [
40936         "Curaçao",
40937         "cw",
40938         "599",
40939         0
40940       ],
40941       [
40942         "Cyprus (Κύπρος)",
40943         "cy",
40944         "357"
40945       ],
40946       [
40947         "Czech Republic (Česká republika)",
40948         "cz",
40949         "420"
40950       ],
40951       [
40952         "Denmark (Danmark)",
40953         "dk",
40954         "45"
40955       ],
40956       [
40957         "Djibouti",
40958         "dj",
40959         "253"
40960       ],
40961       [
40962         "Dominica",
40963         "dm",
40964         "1767"
40965       ],
40966       [
40967         "Dominican Republic (República Dominicana)",
40968         "do",
40969         "1",
40970         2,
40971         ["809", "829", "849"]
40972       ],
40973       [
40974         "Ecuador",
40975         "ec",
40976         "593"
40977       ],
40978       [
40979         "Egypt (‫مصر‬‎)",
40980         "eg",
40981         "20"
40982       ],
40983       [
40984         "El Salvador",
40985         "sv",
40986         "503"
40987       ],
40988       [
40989         "Equatorial Guinea (Guinea Ecuatorial)",
40990         "gq",
40991         "240"
40992       ],
40993       [
40994         "Eritrea",
40995         "er",
40996         "291"
40997       ],
40998       [
40999         "Estonia (Eesti)",
41000         "ee",
41001         "372"
41002       ],
41003       [
41004         "Ethiopia",
41005         "et",
41006         "251"
41007       ],
41008       [
41009         "Falkland Islands (Islas Malvinas)",
41010         "fk",
41011         "500"
41012       ],
41013       [
41014         "Faroe Islands (Føroyar)",
41015         "fo",
41016         "298"
41017       ],
41018       [
41019         "Fiji",
41020         "fj",
41021         "679"
41022       ],
41023       [
41024         "Finland (Suomi)",
41025         "fi",
41026         "358",
41027         0
41028       ],
41029       [
41030         "France",
41031         "fr",
41032         "33"
41033       ],
41034       [
41035         "French Guiana (Guyane française)",
41036         "gf",
41037         "594"
41038       ],
41039       [
41040         "French Polynesia (Polynésie française)",
41041         "pf",
41042         "689"
41043       ],
41044       [
41045         "Gabon",
41046         "ga",
41047         "241"
41048       ],
41049       [
41050         "Gambia",
41051         "gm",
41052         "220"
41053       ],
41054       [
41055         "Georgia (საქართველო)",
41056         "ge",
41057         "995"
41058       ],
41059       [
41060         "Germany (Deutschland)",
41061         "de",
41062         "49"
41063       ],
41064       [
41065         "Ghana (Gaana)",
41066         "gh",
41067         "233"
41068       ],
41069       [
41070         "Gibraltar",
41071         "gi",
41072         "350"
41073       ],
41074       [
41075         "Greece (Ελλάδα)",
41076         "gr",
41077         "30"
41078       ],
41079       [
41080         "Greenland (Kalaallit Nunaat)",
41081         "gl",
41082         "299"
41083       ],
41084       [
41085         "Grenada",
41086         "gd",
41087         "1473"
41088       ],
41089       [
41090         "Guadeloupe",
41091         "gp",
41092         "590",
41093         0
41094       ],
41095       [
41096         "Guam",
41097         "gu",
41098         "1671"
41099       ],
41100       [
41101         "Guatemala",
41102         "gt",
41103         "502"
41104       ],
41105       [
41106         "Guernsey",
41107         "gg",
41108         "44",
41109         1
41110       ],
41111       [
41112         "Guinea (Guinée)",
41113         "gn",
41114         "224"
41115       ],
41116       [
41117         "Guinea-Bissau (Guiné Bissau)",
41118         "gw",
41119         "245"
41120       ],
41121       [
41122         "Guyana",
41123         "gy",
41124         "592"
41125       ],
41126       [
41127         "Haiti",
41128         "ht",
41129         "509"
41130       ],
41131       [
41132         "Honduras",
41133         "hn",
41134         "504"
41135       ],
41136       [
41137         "Hong Kong (香港)",
41138         "hk",
41139         "852"
41140       ],
41141       [
41142         "Hungary (Magyarország)",
41143         "hu",
41144         "36"
41145       ],
41146       [
41147         "Iceland (Ísland)",
41148         "is",
41149         "354"
41150       ],
41151       [
41152         "India (भारत)",
41153         "in",
41154         "91"
41155       ],
41156       [
41157         "Indonesia",
41158         "id",
41159         "62"
41160       ],
41161       [
41162         "Iran (‫ایران‬‎)",
41163         "ir",
41164         "98"
41165       ],
41166       [
41167         "Iraq (‫العراق‬‎)",
41168         "iq",
41169         "964"
41170       ],
41171       [
41172         "Ireland",
41173         "ie",
41174         "353"
41175       ],
41176       [
41177         "Isle of Man",
41178         "im",
41179         "44",
41180         2
41181       ],
41182       [
41183         "Israel (‫ישראל‬‎)",
41184         "il",
41185         "972"
41186       ],
41187       [
41188         "Italy (Italia)",
41189         "it",
41190         "39",
41191         0
41192       ],
41193       [
41194         "Jamaica",
41195         "jm",
41196         "1876"
41197       ],
41198       [
41199         "Japan (日本)",
41200         "jp",
41201         "81"
41202       ],
41203       [
41204         "Jersey",
41205         "je",
41206         "44",
41207         3
41208       ],
41209       [
41210         "Jordan (‫الأردن‬‎)",
41211         "jo",
41212         "962"
41213       ],
41214       [
41215         "Kazakhstan (Казахстан)",
41216         "kz",
41217         "7",
41218         1
41219       ],
41220       [
41221         "Kenya",
41222         "ke",
41223         "254"
41224       ],
41225       [
41226         "Kiribati",
41227         "ki",
41228         "686"
41229       ],
41230       [
41231         "Kosovo",
41232         "xk",
41233         "383"
41234       ],
41235       [
41236         "Kuwait (‫الكويت‬‎)",
41237         "kw",
41238         "965"
41239       ],
41240       [
41241         "Kyrgyzstan (Кыргызстан)",
41242         "kg",
41243         "996"
41244       ],
41245       [
41246         "Laos (ລາວ)",
41247         "la",
41248         "856"
41249       ],
41250       [
41251         "Latvia (Latvija)",
41252         "lv",
41253         "371"
41254       ],
41255       [
41256         "Lebanon (‫لبنان‬‎)",
41257         "lb",
41258         "961"
41259       ],
41260       [
41261         "Lesotho",
41262         "ls",
41263         "266"
41264       ],
41265       [
41266         "Liberia",
41267         "lr",
41268         "231"
41269       ],
41270       [
41271         "Libya (‫ليبيا‬‎)",
41272         "ly",
41273         "218"
41274       ],
41275       [
41276         "Liechtenstein",
41277         "li",
41278         "423"
41279       ],
41280       [
41281         "Lithuania (Lietuva)",
41282         "lt",
41283         "370"
41284       ],
41285       [
41286         "Luxembourg",
41287         "lu",
41288         "352"
41289       ],
41290       [
41291         "Macau (澳門)",
41292         "mo",
41293         "853"
41294       ],
41295       [
41296         "Macedonia (FYROM) (Македонија)",
41297         "mk",
41298         "389"
41299       ],
41300       [
41301         "Madagascar (Madagasikara)",
41302         "mg",
41303         "261"
41304       ],
41305       [
41306         "Malawi",
41307         "mw",
41308         "265"
41309       ],
41310       [
41311         "Malaysia",
41312         "my",
41313         "60"
41314       ],
41315       [
41316         "Maldives",
41317         "mv",
41318         "960"
41319       ],
41320       [
41321         "Mali",
41322         "ml",
41323         "223"
41324       ],
41325       [
41326         "Malta",
41327         "mt",
41328         "356"
41329       ],
41330       [
41331         "Marshall Islands",
41332         "mh",
41333         "692"
41334       ],
41335       [
41336         "Martinique",
41337         "mq",
41338         "596"
41339       ],
41340       [
41341         "Mauritania (‫موريتانيا‬‎)",
41342         "mr",
41343         "222"
41344       ],
41345       [
41346         "Mauritius (Moris)",
41347         "mu",
41348         "230"
41349       ],
41350       [
41351         "Mayotte",
41352         "yt",
41353         "262",
41354         1
41355       ],
41356       [
41357         "Mexico (México)",
41358         "mx",
41359         "52"
41360       ],
41361       [
41362         "Micronesia",
41363         "fm",
41364         "691"
41365       ],
41366       [
41367         "Moldova (Republica Moldova)",
41368         "md",
41369         "373"
41370       ],
41371       [
41372         "Monaco",
41373         "mc",
41374         "377"
41375       ],
41376       [
41377         "Mongolia (Монгол)",
41378         "mn",
41379         "976"
41380       ],
41381       [
41382         "Montenegro (Crna Gora)",
41383         "me",
41384         "382"
41385       ],
41386       [
41387         "Montserrat",
41388         "ms",
41389         "1664"
41390       ],
41391       [
41392         "Morocco (‫المغرب‬‎)",
41393         "ma",
41394         "212",
41395         0
41396       ],
41397       [
41398         "Mozambique (Moçambique)",
41399         "mz",
41400         "258"
41401       ],
41402       [
41403         "Myanmar (Burma) (မြန်မာ)",
41404         "mm",
41405         "95"
41406       ],
41407       [
41408         "Namibia (Namibië)",
41409         "na",
41410         "264"
41411       ],
41412       [
41413         "Nauru",
41414         "nr",
41415         "674"
41416       ],
41417       [
41418         "Nepal (नेपाल)",
41419         "np",
41420         "977"
41421       ],
41422       [
41423         "Netherlands (Nederland)",
41424         "nl",
41425         "31"
41426       ],
41427       [
41428         "New Caledonia (Nouvelle-Calédonie)",
41429         "nc",
41430         "687"
41431       ],
41432       [
41433         "New Zealand",
41434         "nz",
41435         "64"
41436       ],
41437       [
41438         "Nicaragua",
41439         "ni",
41440         "505"
41441       ],
41442       [
41443         "Niger (Nijar)",
41444         "ne",
41445         "227"
41446       ],
41447       [
41448         "Nigeria",
41449         "ng",
41450         "234"
41451       ],
41452       [
41453         "Niue",
41454         "nu",
41455         "683"
41456       ],
41457       [
41458         "Norfolk Island",
41459         "nf",
41460         "672"
41461       ],
41462       [
41463         "North Korea (조선 민주주의 인민 공화국)",
41464         "kp",
41465         "850"
41466       ],
41467       [
41468         "Northern Mariana Islands",
41469         "mp",
41470         "1670"
41471       ],
41472       [
41473         "Norway (Norge)",
41474         "no",
41475         "47",
41476         0
41477       ],
41478       [
41479         "Oman (‫عُمان‬‎)",
41480         "om",
41481         "968"
41482       ],
41483       [
41484         "Pakistan (‫پاکستان‬‎)",
41485         "pk",
41486         "92"
41487       ],
41488       [
41489         "Palau",
41490         "pw",
41491         "680"
41492       ],
41493       [
41494         "Palestine (‫فلسطين‬‎)",
41495         "ps",
41496         "970"
41497       ],
41498       [
41499         "Panama (Panamá)",
41500         "pa",
41501         "507"
41502       ],
41503       [
41504         "Papua New Guinea",
41505         "pg",
41506         "675"
41507       ],
41508       [
41509         "Paraguay",
41510         "py",
41511         "595"
41512       ],
41513       [
41514         "Peru (Perú)",
41515         "pe",
41516         "51"
41517       ],
41518       [
41519         "Philippines",
41520         "ph",
41521         "63"
41522       ],
41523       [
41524         "Poland (Polska)",
41525         "pl",
41526         "48"
41527       ],
41528       [
41529         "Portugal",
41530         "pt",
41531         "351"
41532       ],
41533       [
41534         "Puerto Rico",
41535         "pr",
41536         "1",
41537         3,
41538         ["787", "939"]
41539       ],
41540       [
41541         "Qatar (‫قطر‬‎)",
41542         "qa",
41543         "974"
41544       ],
41545       [
41546         "Réunion (La Réunion)",
41547         "re",
41548         "262",
41549         0
41550       ],
41551       [
41552         "Romania (România)",
41553         "ro",
41554         "40"
41555       ],
41556       [
41557         "Russia (Россия)",
41558         "ru",
41559         "7",
41560         0
41561       ],
41562       [
41563         "Rwanda",
41564         "rw",
41565         "250"
41566       ],
41567       [
41568         "Saint Barthélemy",
41569         "bl",
41570         "590",
41571         1
41572       ],
41573       [
41574         "Saint Helena",
41575         "sh",
41576         "290"
41577       ],
41578       [
41579         "Saint Kitts and Nevis",
41580         "kn",
41581         "1869"
41582       ],
41583       [
41584         "Saint Lucia",
41585         "lc",
41586         "1758"
41587       ],
41588       [
41589         "Saint Martin (Saint-Martin (partie française))",
41590         "mf",
41591         "590",
41592         2
41593       ],
41594       [
41595         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
41596         "pm",
41597         "508"
41598       ],
41599       [
41600         "Saint Vincent and the Grenadines",
41601         "vc",
41602         "1784"
41603       ],
41604       [
41605         "Samoa",
41606         "ws",
41607         "685"
41608       ],
41609       [
41610         "San Marino",
41611         "sm",
41612         "378"
41613       ],
41614       [
41615         "São Tomé and Príncipe (São Tomé e Príncipe)",
41616         "st",
41617         "239"
41618       ],
41619       [
41620         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
41621         "sa",
41622         "966"
41623       ],
41624       [
41625         "Senegal (Sénégal)",
41626         "sn",
41627         "221"
41628       ],
41629       [
41630         "Serbia (Србија)",
41631         "rs",
41632         "381"
41633       ],
41634       [
41635         "Seychelles",
41636         "sc",
41637         "248"
41638       ],
41639       [
41640         "Sierra Leone",
41641         "sl",
41642         "232"
41643       ],
41644       [
41645         "Singapore",
41646         "sg",
41647         "65"
41648       ],
41649       [
41650         "Sint Maarten",
41651         "sx",
41652         "1721"
41653       ],
41654       [
41655         "Slovakia (Slovensko)",
41656         "sk",
41657         "421"
41658       ],
41659       [
41660         "Slovenia (Slovenija)",
41661         "si",
41662         "386"
41663       ],
41664       [
41665         "Solomon Islands",
41666         "sb",
41667         "677"
41668       ],
41669       [
41670         "Somalia (Soomaaliya)",
41671         "so",
41672         "252"
41673       ],
41674       [
41675         "South Africa",
41676         "za",
41677         "27"
41678       ],
41679       [
41680         "South Korea (대한민국)",
41681         "kr",
41682         "82"
41683       ],
41684       [
41685         "South Sudan (‫جنوب السودان‬‎)",
41686         "ss",
41687         "211"
41688       ],
41689       [
41690         "Spain (España)",
41691         "es",
41692         "34"
41693       ],
41694       [
41695         "Sri Lanka (ශ්‍රී ලංකාව)",
41696         "lk",
41697         "94"
41698       ],
41699       [
41700         "Sudan (‫السودان‬‎)",
41701         "sd",
41702         "249"
41703       ],
41704       [
41705         "Suriname",
41706         "sr",
41707         "597"
41708       ],
41709       [
41710         "Svalbard and Jan Mayen",
41711         "sj",
41712         "47",
41713         1
41714       ],
41715       [
41716         "Swaziland",
41717         "sz",
41718         "268"
41719       ],
41720       [
41721         "Sweden (Sverige)",
41722         "se",
41723         "46"
41724       ],
41725       [
41726         "Switzerland (Schweiz)",
41727         "ch",
41728         "41"
41729       ],
41730       [
41731         "Syria (‫سوريا‬‎)",
41732         "sy",
41733         "963"
41734       ],
41735       [
41736         "Taiwan (台灣)",
41737         "tw",
41738         "886"
41739       ],
41740       [
41741         "Tajikistan",
41742         "tj",
41743         "992"
41744       ],
41745       [
41746         "Tanzania",
41747         "tz",
41748         "255"
41749       ],
41750       [
41751         "Thailand (ไทย)",
41752         "th",
41753         "66"
41754       ],
41755       [
41756         "Timor-Leste",
41757         "tl",
41758         "670"
41759       ],
41760       [
41761         "Togo",
41762         "tg",
41763         "228"
41764       ],
41765       [
41766         "Tokelau",
41767         "tk",
41768         "690"
41769       ],
41770       [
41771         "Tonga",
41772         "to",
41773         "676"
41774       ],
41775       [
41776         "Trinidad and Tobago",
41777         "tt",
41778         "1868"
41779       ],
41780       [
41781         "Tunisia (‫تونس‬‎)",
41782         "tn",
41783         "216"
41784       ],
41785       [
41786         "Turkey (Türkiye)",
41787         "tr",
41788         "90"
41789       ],
41790       [
41791         "Turkmenistan",
41792         "tm",
41793         "993"
41794       ],
41795       [
41796         "Turks and Caicos Islands",
41797         "tc",
41798         "1649"
41799       ],
41800       [
41801         "Tuvalu",
41802         "tv",
41803         "688"
41804       ],
41805       [
41806         "U.S. Virgin Islands",
41807         "vi",
41808         "1340"
41809       ],
41810       [
41811         "Uganda",
41812         "ug",
41813         "256"
41814       ],
41815       [
41816         "Ukraine (Україна)",
41817         "ua",
41818         "380"
41819       ],
41820       [
41821         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
41822         "ae",
41823         "971"
41824       ],
41825       [
41826         "United Kingdom",
41827         "gb",
41828         "44",
41829         0
41830       ],
41831       [
41832         "United States",
41833         "us",
41834         "1",
41835         0
41836       ],
41837       [
41838         "Uruguay",
41839         "uy",
41840         "598"
41841       ],
41842       [
41843         "Uzbekistan (Oʻzbekiston)",
41844         "uz",
41845         "998"
41846       ],
41847       [
41848         "Vanuatu",
41849         "vu",
41850         "678"
41851       ],
41852       [
41853         "Vatican City (Città del Vaticano)",
41854         "va",
41855         "39",
41856         1
41857       ],
41858       [
41859         "Venezuela",
41860         "ve",
41861         "58"
41862       ],
41863       [
41864         "Vietnam (Việt Nam)",
41865         "vn",
41866         "84"
41867       ],
41868       [
41869         "Wallis and Futuna (Wallis-et-Futuna)",
41870         "wf",
41871         "681"
41872       ],
41873       [
41874         "Western Sahara (‫الصحراء الغربية‬‎)",
41875         "eh",
41876         "212",
41877         1
41878       ],
41879       [
41880         "Yemen (‫اليمن‬‎)",
41881         "ye",
41882         "967"
41883       ],
41884       [
41885         "Zambia",
41886         "zm",
41887         "260"
41888       ],
41889       [
41890         "Zimbabwe",
41891         "zw",
41892         "263"
41893       ],
41894       [
41895         "Åland Islands",
41896         "ax",
41897         "358",
41898         1
41899       ]
41900   ];
41901   
41902   return d;
41903 }/**
41904 *    This script refer to:
41905 *    Title: International Telephone Input
41906 *    Author: Jack O'Connor
41907 *    Code version:  v12.1.12
41908 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41909 **/
41910
41911 /**
41912  * @class Roo.bootstrap.PhoneInput
41913  * @extends Roo.bootstrap.TriggerField
41914  * An input with International dial-code selection
41915  
41916  * @cfg {String} defaultDialCode default '+852'
41917  * @cfg {Array} preferedCountries default []
41918   
41919  * @constructor
41920  * Create a new PhoneInput.
41921  * @param {Object} config Configuration options
41922  */
41923
41924 Roo.bootstrap.PhoneInput = function(config) {
41925     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
41926 };
41927
41928 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
41929         
41930         listWidth: undefined,
41931         
41932         selectedClass: 'active',
41933         
41934         invalidClass : "has-warning",
41935         
41936         validClass: 'has-success',
41937         
41938         allowed: '0123456789',
41939         
41940         max_length: 15,
41941         
41942         /**
41943          * @cfg {String} defaultDialCode The default dial code when initializing the input
41944          */
41945         defaultDialCode: '+852',
41946         
41947         /**
41948          * @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
41949          */
41950         preferedCountries: false,
41951         
41952         getAutoCreate : function()
41953         {
41954             var data = Roo.bootstrap.PhoneInputData();
41955             var align = this.labelAlign || this.parentLabelAlign();
41956             var id = Roo.id();
41957             
41958             this.allCountries = [];
41959             this.dialCodeMapping = [];
41960             
41961             for (var i = 0; i < data.length; i++) {
41962               var c = data[i];
41963               this.allCountries[i] = {
41964                 name: c[0],
41965                 iso2: c[1],
41966                 dialCode: c[2],
41967                 priority: c[3] || 0,
41968                 areaCodes: c[4] || null
41969               };
41970               this.dialCodeMapping[c[2]] = {
41971                   name: c[0],
41972                   iso2: c[1],
41973                   priority: c[3] || 0,
41974                   areaCodes: c[4] || null
41975               };
41976             }
41977             
41978             var cfg = {
41979                 cls: 'form-group',
41980                 cn: []
41981             };
41982             
41983             var input =  {
41984                 tag: 'input',
41985                 id : id,
41986                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
41987                 maxlength: this.max_length,
41988                 cls : 'form-control tel-input',
41989                 autocomplete: 'new-password'
41990             };
41991             
41992             var hiddenInput = {
41993                 tag: 'input',
41994                 type: 'hidden',
41995                 cls: 'hidden-tel-input'
41996             };
41997             
41998             if (this.name) {
41999                 hiddenInput.name = this.name;
42000             }
42001             
42002             if (this.disabled) {
42003                 input.disabled = true;
42004             }
42005             
42006             var flag_container = {
42007                 tag: 'div',
42008                 cls: 'flag-box',
42009                 cn: [
42010                     {
42011                         tag: 'div',
42012                         cls: 'flag'
42013                     },
42014                     {
42015                         tag: 'div',
42016                         cls: 'caret'
42017                     }
42018                 ]
42019             };
42020             
42021             var box = {
42022                 tag: 'div',
42023                 cls: this.hasFeedback ? 'has-feedback' : '',
42024                 cn: [
42025                     hiddenInput,
42026                     input,
42027                     {
42028                         tag: 'input',
42029                         cls: 'dial-code-holder',
42030                         disabled: true
42031                     }
42032                 ]
42033             };
42034             
42035             var container = {
42036                 cls: 'roo-select2-container input-group',
42037                 cn: [
42038                     flag_container,
42039                     box
42040                 ]
42041             };
42042             
42043             if (this.fieldLabel.length) {
42044                 var indicator = {
42045                     tag: 'i',
42046                     tooltip: 'This field is required'
42047                 };
42048                 
42049                 var label = {
42050                     tag: 'label',
42051                     'for':  id,
42052                     cls: 'control-label',
42053                     cn: []
42054                 };
42055                 
42056                 var label_text = {
42057                     tag: 'span',
42058                     html: this.fieldLabel
42059                 };
42060                 
42061                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42062                 label.cn = [
42063                     indicator,
42064                     label_text
42065                 ];
42066                 
42067                 if(this.indicatorpos == 'right') {
42068                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42069                     label.cn = [
42070                         label_text,
42071                         indicator
42072                     ];
42073                 }
42074                 
42075                 if(align == 'left') {
42076                     container = {
42077                         tag: 'div',
42078                         cn: [
42079                             container
42080                         ]
42081                     };
42082                     
42083                     if(this.labelWidth > 12){
42084                         label.style = "width: " + this.labelWidth + 'px';
42085                     }
42086                     if(this.labelWidth < 13 && this.labelmd == 0){
42087                         this.labelmd = this.labelWidth;
42088                     }
42089                     if(this.labellg > 0){
42090                         label.cls += ' col-lg-' + this.labellg;
42091                         input.cls += ' col-lg-' + (12 - this.labellg);
42092                     }
42093                     if(this.labelmd > 0){
42094                         label.cls += ' col-md-' + this.labelmd;
42095                         container.cls += ' col-md-' + (12 - this.labelmd);
42096                     }
42097                     if(this.labelsm > 0){
42098                         label.cls += ' col-sm-' + this.labelsm;
42099                         container.cls += ' col-sm-' + (12 - this.labelsm);
42100                     }
42101                     if(this.labelxs > 0){
42102                         label.cls += ' col-xs-' + this.labelxs;
42103                         container.cls += ' col-xs-' + (12 - this.labelxs);
42104                     }
42105                 }
42106             }
42107             
42108             cfg.cn = [
42109                 label,
42110                 container
42111             ];
42112             
42113             var settings = this;
42114             
42115             ['xs','sm','md','lg'].map(function(size){
42116                 if (settings[size]) {
42117                     cfg.cls += ' col-' + size + '-' + settings[size];
42118                 }
42119             });
42120             
42121             this.store = new Roo.data.Store({
42122                 proxy : new Roo.data.MemoryProxy({}),
42123                 reader : new Roo.data.JsonReader({
42124                     fields : [
42125                         {
42126                             'name' : 'name',
42127                             'type' : 'string'
42128                         },
42129                         {
42130                             'name' : 'iso2',
42131                             'type' : 'string'
42132                         },
42133                         {
42134                             'name' : 'dialCode',
42135                             'type' : 'string'
42136                         },
42137                         {
42138                             'name' : 'priority',
42139                             'type' : 'string'
42140                         },
42141                         {
42142                             'name' : 'areaCodes',
42143                             'type' : 'string'
42144                         }
42145                     ]
42146                 })
42147             });
42148             
42149             if(!this.preferedCountries) {
42150                 this.preferedCountries = [
42151                     'hk',
42152                     'gb',
42153                     'us'
42154                 ];
42155             }
42156             
42157             var p = this.preferedCountries.reverse();
42158             
42159             if(p) {
42160                 for (var i = 0; i < p.length; i++) {
42161                     for (var j = 0; j < this.allCountries.length; j++) {
42162                         if(this.allCountries[j].iso2 == p[i]) {
42163                             var t = this.allCountries[j];
42164                             this.allCountries.splice(j,1);
42165                             this.allCountries.unshift(t);
42166                         }
42167                     } 
42168                 }
42169             }
42170             
42171             this.store.proxy.data = {
42172                 success: true,
42173                 data: this.allCountries
42174             };
42175             
42176             return cfg;
42177         },
42178         
42179         initEvents : function()
42180         {
42181             this.createList();
42182             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42183             
42184             this.indicator = this.indicatorEl();
42185             this.flag = this.flagEl();
42186             this.dialCodeHolder = this.dialCodeHolderEl();
42187             
42188             this.trigger = this.el.select('div.flag-box',true).first();
42189             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42190             
42191             var _this = this;
42192             
42193             (function(){
42194                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42195                 _this.list.setWidth(lw);
42196             }).defer(100);
42197             
42198             this.list.on('mouseover', this.onViewOver, this);
42199             this.list.on('mousemove', this.onViewMove, this);
42200             this.inputEl().on("keyup", this.onKeyUp, this);
42201             this.inputEl().on("keypress", this.onKeyPress, this);
42202             
42203             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42204
42205             this.view = new Roo.View(this.list, this.tpl, {
42206                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42207             });
42208             
42209             this.view.on('click', this.onViewClick, this);
42210             this.setValue(this.defaultDialCode);
42211         },
42212         
42213         onTriggerClick : function(e)
42214         {
42215             Roo.log('trigger click');
42216             if(this.disabled){
42217                 return;
42218             }
42219             
42220             if(this.isExpanded()){
42221                 this.collapse();
42222                 this.hasFocus = false;
42223             }else {
42224                 this.store.load({});
42225                 this.hasFocus = true;
42226                 this.expand();
42227             }
42228         },
42229         
42230         isExpanded : function()
42231         {
42232             return this.list.isVisible();
42233         },
42234         
42235         collapse : function()
42236         {
42237             if(!this.isExpanded()){
42238                 return;
42239             }
42240             this.list.hide();
42241             Roo.get(document).un('mousedown', this.collapseIf, this);
42242             Roo.get(document).un('mousewheel', this.collapseIf, this);
42243             this.fireEvent('collapse', this);
42244             this.validate();
42245         },
42246         
42247         expand : function()
42248         {
42249             Roo.log('expand');
42250
42251             if(this.isExpanded() || !this.hasFocus){
42252                 return;
42253             }
42254             
42255             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42256             this.list.setWidth(lw);
42257             
42258             this.list.show();
42259             this.restrictHeight();
42260             
42261             Roo.get(document).on('mousedown', this.collapseIf, this);
42262             Roo.get(document).on('mousewheel', this.collapseIf, this);
42263             
42264             this.fireEvent('expand', this);
42265         },
42266         
42267         restrictHeight : function()
42268         {
42269             this.list.alignTo(this.inputEl(), this.listAlign);
42270             this.list.alignTo(this.inputEl(), this.listAlign);
42271         },
42272         
42273         onViewOver : function(e, t)
42274         {
42275             if(this.inKeyMode){
42276                 return;
42277             }
42278             var item = this.view.findItemFromChild(t);
42279             
42280             if(item){
42281                 var index = this.view.indexOf(item);
42282                 this.select(index, false);
42283             }
42284         },
42285
42286         // private
42287         onViewClick : function(view, doFocus, el, e)
42288         {
42289             var index = this.view.getSelectedIndexes()[0];
42290             
42291             var r = this.store.getAt(index);
42292             
42293             if(r){
42294                 this.onSelect(r, index);
42295             }
42296             if(doFocus !== false && !this.blockFocus){
42297                 this.inputEl().focus();
42298             }
42299         },
42300         
42301         onViewMove : function(e, t)
42302         {
42303             this.inKeyMode = false;
42304         },
42305         
42306         select : function(index, scrollIntoView)
42307         {
42308             this.selectedIndex = index;
42309             this.view.select(index);
42310             if(scrollIntoView !== false){
42311                 var el = this.view.getNode(index);
42312                 if(el){
42313                     this.list.scrollChildIntoView(el, false);
42314                 }
42315             }
42316         },
42317         
42318         createList : function()
42319         {
42320             this.list = Roo.get(document.body).createChild({
42321                 tag: 'ul',
42322                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
42323                 style: 'display:none'
42324             });
42325             
42326             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
42327         },
42328         
42329         collapseIf : function(e)
42330         {
42331             var in_combo  = e.within(this.el);
42332             var in_list =  e.within(this.list);
42333             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
42334             
42335             if (in_combo || in_list || is_list) {
42336                 return;
42337             }
42338             this.collapse();
42339         },
42340         
42341         onSelect : function(record, index)
42342         {
42343             if(this.fireEvent('beforeselect', this, record, index) !== false){
42344                 
42345                 this.setFlagClass(record.data.iso2);
42346                 this.setDialCode(record.data.dialCode);
42347                 this.hasFocus = false;
42348                 this.collapse();
42349                 this.fireEvent('select', this, record, index);
42350             }
42351         },
42352         
42353         flagEl : function()
42354         {
42355             var flag = this.el.select('div.flag',true).first();
42356             if(!flag){
42357                 return false;
42358             }
42359             return flag;
42360         },
42361         
42362         dialCodeHolderEl : function()
42363         {
42364             var d = this.el.select('input.dial-code-holder',true).first();
42365             if(!d){
42366                 return false;
42367             }
42368             return d;
42369         },
42370         
42371         setDialCode : function(v)
42372         {
42373             this.dialCodeHolder.dom.value = '+'+v;
42374         },
42375         
42376         setFlagClass : function(n)
42377         {
42378             this.flag.dom.className = 'flag '+n;
42379         },
42380         
42381         getValue : function()
42382         {
42383             var v = this.inputEl().getValue();
42384             if(this.dialCodeHolder) {
42385                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
42386             }
42387             return v;
42388         },
42389         
42390         setValue : function(v)
42391         {
42392             var d = this.getDialCode(v);
42393             
42394             //invalid dial code
42395             if(v.length == 0 || !d || d.length == 0) {
42396                 if(this.rendered){
42397                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
42398                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
42399                 }
42400                 return;
42401             }
42402             
42403             //valid dial code
42404             this.setFlagClass(this.dialCodeMapping[d].iso2);
42405             this.setDialCode(d);
42406             this.inputEl().dom.value = v.replace('+'+d,'');
42407             this.hiddenEl().dom.value = this.getValue();
42408             
42409             this.validate();
42410         },
42411         
42412         getDialCode : function(v)
42413         {
42414             v = v ||  '';
42415             
42416             if (v.length == 0) {
42417                 return this.dialCodeHolder.dom.value;
42418             }
42419             
42420             var dialCode = "";
42421             if (v.charAt(0) != "+") {
42422                 return false;
42423             }
42424             var numericChars = "";
42425             for (var i = 1; i < v.length; i++) {
42426               var c = v.charAt(i);
42427               if (!isNaN(c)) {
42428                 numericChars += c;
42429                 if (this.dialCodeMapping[numericChars]) {
42430                   dialCode = v.substr(1, i);
42431                 }
42432                 if (numericChars.length == 4) {
42433                   break;
42434                 }
42435               }
42436             }
42437             return dialCode;
42438         },
42439         
42440         reset : function()
42441         {
42442             this.setValue(this.defaultDialCode);
42443             this.validate();
42444         },
42445         
42446         hiddenEl : function()
42447         {
42448             return this.el.select('input.hidden-tel-input',true).first();
42449         },
42450         
42451         // after setting val
42452         onKeyUp : function(e){
42453             this.setValue(this.getValue());
42454         },
42455         
42456         onKeyPress : function(e){
42457             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
42458                 e.stopEvent();
42459             }
42460         }
42461         
42462 });
42463 /**
42464  * @class Roo.bootstrap.MoneyField
42465  * @extends Roo.bootstrap.ComboBox
42466  * Bootstrap MoneyField class
42467  * 
42468  * @constructor
42469  * Create a new MoneyField.
42470  * @param {Object} config Configuration options
42471  */
42472
42473 Roo.bootstrap.MoneyField = function(config) {
42474     
42475     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
42476     
42477 };
42478
42479 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
42480     
42481     /**
42482      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
42483      */
42484     allowDecimals : true,
42485     /**
42486      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
42487      */
42488     decimalSeparator : ".",
42489     /**
42490      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
42491      */
42492     decimalPrecision : 0,
42493     /**
42494      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
42495      */
42496     allowNegative : true,
42497     /**
42498      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
42499      */
42500     allowZero: true,
42501     /**
42502      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
42503      */
42504     minValue : Number.NEGATIVE_INFINITY,
42505     /**
42506      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
42507      */
42508     maxValue : Number.MAX_VALUE,
42509     /**
42510      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
42511      */
42512     minText : "The minimum value for this field is {0}",
42513     /**
42514      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
42515      */
42516     maxText : "The maximum value for this field is {0}",
42517     /**
42518      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
42519      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
42520      */
42521     nanText : "{0} is not a valid number",
42522     /**
42523      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
42524      */
42525     castInt : true,
42526     /**
42527      * @cfg {String} defaults currency of the MoneyField
42528      * value should be in lkey
42529      */
42530     defaultCurrency : false,
42531     /**
42532      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
42533      */
42534     thousandsDelimiter : false,
42535     /**
42536      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
42537      */
42538     max_length: false,
42539     
42540     inputlg : 9,
42541     inputmd : 9,
42542     inputsm : 9,
42543     inputxs : 6,
42544     
42545     store : false,
42546     
42547     getAutoCreate : function()
42548     {
42549         var align = this.labelAlign || this.parentLabelAlign();
42550         
42551         var id = Roo.id();
42552
42553         var cfg = {
42554             cls: 'form-group',
42555             cn: []
42556         };
42557
42558         var input =  {
42559             tag: 'input',
42560             id : id,
42561             cls : 'form-control roo-money-amount-input',
42562             autocomplete: 'new-password'
42563         };
42564         
42565         var hiddenInput = {
42566             tag: 'input',
42567             type: 'hidden',
42568             id: Roo.id(),
42569             cls: 'hidden-number-input'
42570         };
42571         
42572         if(this.max_length) {
42573             input.maxlength = this.max_length; 
42574         }
42575         
42576         if (this.name) {
42577             hiddenInput.name = this.name;
42578         }
42579
42580         if (this.disabled) {
42581             input.disabled = true;
42582         }
42583
42584         var clg = 12 - this.inputlg;
42585         var cmd = 12 - this.inputmd;
42586         var csm = 12 - this.inputsm;
42587         var cxs = 12 - this.inputxs;
42588         
42589         var container = {
42590             tag : 'div',
42591             cls : 'row roo-money-field',
42592             cn : [
42593                 {
42594                     tag : 'div',
42595                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
42596                     cn : [
42597                         {
42598                             tag : 'div',
42599                             cls: 'roo-select2-container input-group',
42600                             cn: [
42601                                 {
42602                                     tag : 'input',
42603                                     cls : 'form-control roo-money-currency-input',
42604                                     autocomplete: 'new-password',
42605                                     readOnly : 1,
42606                                     name : this.currencyName
42607                                 },
42608                                 {
42609                                     tag :'span',
42610                                     cls : 'input-group-addon',
42611                                     cn : [
42612                                         {
42613                                             tag: 'span',
42614                                             cls: 'caret'
42615                                         }
42616                                     ]
42617                                 }
42618                             ]
42619                         }
42620                     ]
42621                 },
42622                 {
42623                     tag : 'div',
42624                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
42625                     cn : [
42626                         {
42627                             tag: 'div',
42628                             cls: this.hasFeedback ? 'has-feedback' : '',
42629                             cn: [
42630                                 input
42631                             ]
42632                         }
42633                     ]
42634                 }
42635             ]
42636             
42637         };
42638         
42639         if (this.fieldLabel.length) {
42640             var indicator = {
42641                 tag: 'i',
42642                 tooltip: 'This field is required'
42643             };
42644
42645             var label = {
42646                 tag: 'label',
42647                 'for':  id,
42648                 cls: 'control-label',
42649                 cn: []
42650             };
42651
42652             var label_text = {
42653                 tag: 'span',
42654                 html: this.fieldLabel
42655             };
42656
42657             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42658             label.cn = [
42659                 indicator,
42660                 label_text
42661             ];
42662
42663             if(this.indicatorpos == 'right') {
42664                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42665                 label.cn = [
42666                     label_text,
42667                     indicator
42668                 ];
42669             }
42670
42671             if(align == 'left') {
42672                 container = {
42673                     tag: 'div',
42674                     cn: [
42675                         container
42676                     ]
42677                 };
42678
42679                 if(this.labelWidth > 12){
42680                     label.style = "width: " + this.labelWidth + 'px';
42681                 }
42682                 if(this.labelWidth < 13 && this.labelmd == 0){
42683                     this.labelmd = this.labelWidth;
42684                 }
42685                 if(this.labellg > 0){
42686                     label.cls += ' col-lg-' + this.labellg;
42687                     input.cls += ' col-lg-' + (12 - this.labellg);
42688                 }
42689                 if(this.labelmd > 0){
42690                     label.cls += ' col-md-' + this.labelmd;
42691                     container.cls += ' col-md-' + (12 - this.labelmd);
42692                 }
42693                 if(this.labelsm > 0){
42694                     label.cls += ' col-sm-' + this.labelsm;
42695                     container.cls += ' col-sm-' + (12 - this.labelsm);
42696                 }
42697                 if(this.labelxs > 0){
42698                     label.cls += ' col-xs-' + this.labelxs;
42699                     container.cls += ' col-xs-' + (12 - this.labelxs);
42700                 }
42701             }
42702         }
42703
42704         cfg.cn = [
42705             label,
42706             container,
42707             hiddenInput
42708         ];
42709         
42710         var settings = this;
42711
42712         ['xs','sm','md','lg'].map(function(size){
42713             if (settings[size]) {
42714                 cfg.cls += ' col-' + size + '-' + settings[size];
42715             }
42716         });
42717         
42718         return cfg;
42719     },
42720     
42721     initEvents : function()
42722     {
42723         this.indicator = this.indicatorEl();
42724         
42725         this.initCurrencyEvent();
42726         
42727         this.initNumberEvent();
42728     },
42729     
42730     initCurrencyEvent : function()
42731     {
42732         if (!this.store) {
42733             throw "can not find store for combo";
42734         }
42735         
42736         this.store = Roo.factory(this.store, Roo.data);
42737         this.store.parent = this;
42738         
42739         this.createList();
42740         
42741         this.triggerEl = this.el.select('.input-group-addon', true).first();
42742         
42743         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
42744         
42745         var _this = this;
42746         
42747         (function(){
42748             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42749             _this.list.setWidth(lw);
42750         }).defer(100);
42751         
42752         this.list.on('mouseover', this.onViewOver, this);
42753         this.list.on('mousemove', this.onViewMove, this);
42754         this.list.on('scroll', this.onViewScroll, this);
42755         
42756         if(!this.tpl){
42757             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
42758         }
42759         
42760         this.view = new Roo.View(this.list, this.tpl, {
42761             singleSelect:true, store: this.store, selectedClass: this.selectedClass
42762         });
42763         
42764         this.view.on('click', this.onViewClick, this);
42765         
42766         this.store.on('beforeload', this.onBeforeLoad, this);
42767         this.store.on('load', this.onLoad, this);
42768         this.store.on('loadexception', this.onLoadException, this);
42769         
42770         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
42771             "up" : function(e){
42772                 this.inKeyMode = true;
42773                 this.selectPrev();
42774             },
42775
42776             "down" : function(e){
42777                 if(!this.isExpanded()){
42778                     this.onTriggerClick();
42779                 }else{
42780                     this.inKeyMode = true;
42781                     this.selectNext();
42782                 }
42783             },
42784
42785             "enter" : function(e){
42786                 this.collapse();
42787                 
42788                 if(this.fireEvent("specialkey", this, e)){
42789                     this.onViewClick(false);
42790                 }
42791                 
42792                 return true;
42793             },
42794
42795             "esc" : function(e){
42796                 this.collapse();
42797             },
42798
42799             "tab" : function(e){
42800                 this.collapse();
42801                 
42802                 if(this.fireEvent("specialkey", this, e)){
42803                     this.onViewClick(false);
42804                 }
42805                 
42806                 return true;
42807             },
42808
42809             scope : this,
42810
42811             doRelay : function(foo, bar, hname){
42812                 if(hname == 'down' || this.scope.isExpanded()){
42813                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
42814                 }
42815                 return true;
42816             },
42817
42818             forceKeyDown: true
42819         });
42820         
42821         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
42822         
42823     },
42824     
42825     initNumberEvent : function(e)
42826     {
42827         this.inputEl().on("keydown" , this.fireKey,  this);
42828         this.inputEl().on("focus", this.onFocus,  this);
42829         this.inputEl().on("blur", this.onBlur,  this);
42830         
42831         this.inputEl().relayEvent('keyup', this);
42832         
42833         if(this.indicator){
42834             this.indicator.addClass('invisible');
42835         }
42836  
42837         this.originalValue = this.getValue();
42838         
42839         if(this.validationEvent == 'keyup'){
42840             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
42841             this.inputEl().on('keyup', this.filterValidation, this);
42842         }
42843         else if(this.validationEvent !== false){
42844             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
42845         }
42846         
42847         if(this.selectOnFocus){
42848             this.on("focus", this.preFocus, this);
42849             
42850         }
42851         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
42852             this.inputEl().on("keypress", this.filterKeys, this);
42853         } else {
42854             this.inputEl().relayEvent('keypress', this);
42855         }
42856         
42857         var allowed = "0123456789";
42858         
42859         if(this.allowDecimals){
42860             allowed += this.decimalSeparator;
42861         }
42862         
42863         if(this.allowNegative){
42864             allowed += "-";
42865         }
42866         
42867         if(this.thousandsDelimiter) {
42868             allowed += ",";
42869         }
42870         
42871         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
42872         
42873         var keyPress = function(e){
42874             
42875             var k = e.getKey();
42876             
42877             var c = e.getCharCode();
42878             
42879             if(
42880                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
42881                     allowed.indexOf(String.fromCharCode(c)) === -1
42882             ){
42883                 e.stopEvent();
42884                 return;
42885             }
42886             
42887             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
42888                 return;
42889             }
42890             
42891             if(allowed.indexOf(String.fromCharCode(c)) === -1){
42892                 e.stopEvent();
42893             }
42894         };
42895         
42896         this.inputEl().on("keypress", keyPress, this);
42897         
42898     },
42899     
42900     onTriggerClick : function(e)
42901     {   
42902         if(this.disabled){
42903             return;
42904         }
42905         
42906         this.page = 0;
42907         this.loadNext = false;
42908         
42909         if(this.isExpanded()){
42910             this.collapse();
42911             return;
42912         }
42913         
42914         this.hasFocus = true;
42915         
42916         if(this.triggerAction == 'all') {
42917             this.doQuery(this.allQuery, true);
42918             return;
42919         }
42920         
42921         this.doQuery(this.getRawValue());
42922     },
42923     
42924     getCurrency : function()
42925     {   
42926         var v = this.currencyEl().getValue();
42927         
42928         return v;
42929     },
42930     
42931     restrictHeight : function()
42932     {
42933         this.list.alignTo(this.currencyEl(), this.listAlign);
42934         this.list.alignTo(this.currencyEl(), this.listAlign);
42935     },
42936     
42937     onViewClick : function(view, doFocus, el, e)
42938     {
42939         var index = this.view.getSelectedIndexes()[0];
42940         
42941         var r = this.store.getAt(index);
42942         
42943         if(r){
42944             this.onSelect(r, index);
42945         }
42946     },
42947     
42948     onSelect : function(record, index){
42949         
42950         if(this.fireEvent('beforeselect', this, record, index) !== false){
42951         
42952             this.setFromCurrencyData(index > -1 ? record.data : false);
42953             
42954             this.collapse();
42955             
42956             this.fireEvent('select', this, record, index);
42957         }
42958     },
42959     
42960     setFromCurrencyData : function(o)
42961     {
42962         var currency = '';
42963         
42964         this.lastCurrency = o;
42965         
42966         if (this.currencyField) {
42967             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
42968         } else {
42969             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
42970         }
42971         
42972         this.lastSelectionText = currency;
42973         
42974         //setting default currency
42975         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
42976             this.setCurrency(this.defaultCurrency);
42977             return;
42978         }
42979         
42980         this.setCurrency(currency);
42981     },
42982     
42983     setFromData : function(o)
42984     {
42985         var c = {};
42986         
42987         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
42988         
42989         this.setFromCurrencyData(c);
42990         
42991         var value = '';
42992         
42993         if (this.name) {
42994             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
42995         } else {
42996             Roo.log('no value set for '+ (this.name ? this.name : this.id));
42997         }
42998         
42999         this.setValue(value);
43000         
43001     },
43002     
43003     setCurrency : function(v)
43004     {   
43005         this.currencyValue = v;
43006         
43007         if(this.rendered){
43008             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43009             this.validate();
43010         }
43011     },
43012     
43013     setValue : function(v)
43014     {
43015         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43016         
43017         this.value = v;
43018         
43019         if(this.rendered){
43020             
43021             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43022             
43023             this.inputEl().dom.value = (v == '') ? '' :
43024                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43025             
43026             if(!this.allowZero && v === '0') {
43027                 this.hiddenEl().dom.value = '';
43028                 this.inputEl().dom.value = '';
43029             }
43030             
43031             this.validate();
43032         }
43033     },
43034     
43035     getRawValue : function()
43036     {
43037         var v = this.inputEl().getValue();
43038         
43039         return v;
43040     },
43041     
43042     getValue : function()
43043     {
43044         return this.fixPrecision(this.parseValue(this.getRawValue()));
43045     },
43046     
43047     parseValue : function(value)
43048     {
43049         if(this.thousandsDelimiter) {
43050             value += "";
43051             r = new RegExp(",", "g");
43052             value = value.replace(r, "");
43053         }
43054         
43055         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43056         return isNaN(value) ? '' : value;
43057         
43058     },
43059     
43060     fixPrecision : function(value)
43061     {
43062         if(this.thousandsDelimiter) {
43063             value += "";
43064             r = new RegExp(",", "g");
43065             value = value.replace(r, "");
43066         }
43067         
43068         var nan = isNaN(value);
43069         
43070         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43071             return nan ? '' : value;
43072         }
43073         return parseFloat(value).toFixed(this.decimalPrecision);
43074     },
43075     
43076     decimalPrecisionFcn : function(v)
43077     {
43078         return Math.floor(v);
43079     },
43080     
43081     validateValue : function(value)
43082     {
43083         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43084             return false;
43085         }
43086         
43087         var num = this.parseValue(value);
43088         
43089         if(isNaN(num)){
43090             this.markInvalid(String.format(this.nanText, value));
43091             return false;
43092         }
43093         
43094         if(num < this.minValue){
43095             this.markInvalid(String.format(this.minText, this.minValue));
43096             return false;
43097         }
43098         
43099         if(num > this.maxValue){
43100             this.markInvalid(String.format(this.maxText, this.maxValue));
43101             return false;
43102         }
43103         
43104         return true;
43105     },
43106     
43107     validate : function()
43108     {
43109         if(this.disabled || this.allowBlank){
43110             this.markValid();
43111             return true;
43112         }
43113         
43114         var currency = this.getCurrency();
43115         
43116         if(this.validateValue(this.getRawValue()) && currency.length){
43117             this.markValid();
43118             return true;
43119         }
43120         
43121         this.markInvalid();
43122         return false;
43123     },
43124     
43125     getName: function()
43126     {
43127         return this.name;
43128     },
43129     
43130     beforeBlur : function()
43131     {
43132         if(!this.castInt){
43133             return;
43134         }
43135         
43136         var v = this.parseValue(this.getRawValue());
43137         
43138         if(v || v == 0){
43139             this.setValue(v);
43140         }
43141     },
43142     
43143     onBlur : function()
43144     {
43145         this.beforeBlur();
43146         
43147         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43148             //this.el.removeClass(this.focusClass);
43149         }
43150         
43151         this.hasFocus = false;
43152         
43153         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43154             this.validate();
43155         }
43156         
43157         var v = this.getValue();
43158         
43159         if(String(v) !== String(this.startValue)){
43160             this.fireEvent('change', this, v, this.startValue);
43161         }
43162         
43163         this.fireEvent("blur", this);
43164     },
43165     
43166     inputEl : function()
43167     {
43168         return this.el.select('.roo-money-amount-input', true).first();
43169     },
43170     
43171     currencyEl : function()
43172     {
43173         return this.el.select('.roo-money-currency-input', true).first();
43174     },
43175     
43176     hiddenEl : function()
43177     {
43178         return this.el.select('input.hidden-number-input',true).first();
43179     }
43180     
43181 });/**
43182  * @class Roo.bootstrap.BezierSignature
43183  * @extends Roo.bootstrap.Component
43184  * Bootstrap BezierSignature class
43185  * This script refer to:
43186  *    Title: Signature Pad
43187  *    Author: szimek
43188  *    Availability: https://github.com/szimek/signature_pad
43189  *
43190  * @constructor
43191  * Create a new BezierSignature
43192  * @param {Object} config The config object
43193  */
43194
43195 Roo.bootstrap.BezierSignature = function(config){
43196     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43197     this.addEvents({
43198         "resize" : true
43199     });
43200 };
43201
43202 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43203 {
43204      
43205     curve_data: [],
43206     
43207     is_empty: true,
43208     
43209     mouse_btn_down: true,
43210     
43211     /**
43212      * @cfg {int} canvas height
43213      */
43214     canvas_height: '200px',
43215     
43216     /**
43217      * @cfg {float|function} Radius of a single dot.
43218      */ 
43219     dot_size: false,
43220     
43221     /**
43222      * @cfg {float} Minimum width of a line. Defaults to 0.5.
43223      */
43224     min_width: 0.5,
43225     
43226     /**
43227      * @cfg {float} Maximum width of a line. Defaults to 2.5.
43228      */
43229     max_width: 2.5,
43230     
43231     /**
43232      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43233      */
43234     throttle: 16,
43235     
43236     /**
43237      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43238      */
43239     min_distance: 5,
43240     
43241     /**
43242      * @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.
43243      */
43244     bg_color: 'rgba(0, 0, 0, 0)',
43245     
43246     /**
43247      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43248      */
43249     dot_color: 'black',
43250     
43251     /**
43252      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43253      */ 
43254     velocity_filter_weight: 0.7,
43255     
43256     /**
43257      * @cfg {function} Callback when stroke begin. 
43258      */
43259     onBegin: false,
43260     
43261     /**
43262      * @cfg {function} Callback when stroke end.
43263      */
43264     onEnd: false,
43265     
43266     getAutoCreate : function()
43267     {
43268         var cls = 'roo-signature column';
43269         
43270         if(this.cls){
43271             cls += ' ' + this.cls;
43272         }
43273         
43274         var col_sizes = [
43275             'lg',
43276             'md',
43277             'sm',
43278             'xs'
43279         ];
43280         
43281         for(var i = 0; i < col_sizes.length; i++) {
43282             if(this[col_sizes[i]]) {
43283                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
43284             }
43285         }
43286         
43287         var cfg = {
43288             tag: 'div',
43289             cls: cls,
43290             cn: [
43291                 {
43292                     tag: 'div',
43293                     cls: 'roo-signature-body',
43294                     cn: [
43295                         {
43296                             tag: 'canvas',
43297                             cls: 'roo-signature-body-canvas',
43298                             height: this.canvas_height,
43299                             width: this.canvas_width
43300                         }
43301                     ]
43302                 },
43303                 {
43304                     tag: 'input',
43305                     type: 'file',
43306                     style: 'display: none'
43307                 }
43308             ]
43309         };
43310         
43311         return cfg;
43312     },
43313     
43314     initEvents: function() 
43315     {
43316         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
43317         
43318         var canvas = this.canvasEl();
43319         
43320         // mouse && touch event swapping...
43321         canvas.dom.style.touchAction = 'none';
43322         canvas.dom.style.msTouchAction = 'none';
43323         
43324         this.mouse_btn_down = false;
43325         canvas.on('mousedown', this._handleMouseDown, this);
43326         canvas.on('mousemove', this._handleMouseMove, this);
43327         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
43328         
43329         if (window.PointerEvent) {
43330             canvas.on('pointerdown', this._handleMouseDown, this);
43331             canvas.on('pointermove', this._handleMouseMove, this);
43332             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
43333         }
43334         
43335         if ('ontouchstart' in window) {
43336             canvas.on('touchstart', this._handleTouchStart, this);
43337             canvas.on('touchmove', this._handleTouchMove, this);
43338             canvas.on('touchend', this._handleTouchEnd, this);
43339         }
43340         
43341         Roo.EventManager.onWindowResize(this.resize, this, true);
43342         
43343         // file input event
43344         this.fileEl().on('change', this.uploadImage, this);
43345         
43346         this.clear();
43347         
43348         this.resize();
43349     },
43350     
43351     resize: function(){
43352         
43353         var canvas = this.canvasEl().dom;
43354         var ctx = this.canvasElCtx();
43355         var img_data = false;
43356         
43357         if(canvas.width > 0) {
43358             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
43359         }
43360         // setting canvas width will clean img data
43361         canvas.width = 0;
43362         
43363         var style = window.getComputedStyle ? 
43364             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
43365             
43366         var padding_left = parseInt(style.paddingLeft) || 0;
43367         var padding_right = parseInt(style.paddingRight) || 0;
43368         
43369         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
43370         
43371         if(img_data) {
43372             ctx.putImageData(img_data, 0, 0);
43373         }
43374     },
43375     
43376     _handleMouseDown: function(e)
43377     {
43378         if (e.browserEvent.which === 1) {
43379             this.mouse_btn_down = true;
43380             this.strokeBegin(e);
43381         }
43382     },
43383     
43384     _handleMouseMove: function (e)
43385     {
43386         if (this.mouse_btn_down) {
43387             this.strokeMoveUpdate(e);
43388         }
43389     },
43390     
43391     _handleMouseUp: function (e)
43392     {
43393         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
43394             this.mouse_btn_down = false;
43395             this.strokeEnd(e);
43396         }
43397     },
43398     
43399     _handleTouchStart: function (e) {
43400         
43401         e.preventDefault();
43402         if (e.browserEvent.targetTouches.length === 1) {
43403             // var touch = e.browserEvent.changedTouches[0];
43404             // this.strokeBegin(touch);
43405             
43406              this.strokeBegin(e); // assume e catching the correct xy...
43407         }
43408     },
43409     
43410     _handleTouchMove: function (e) {
43411         e.preventDefault();
43412         // var touch = event.targetTouches[0];
43413         // _this._strokeMoveUpdate(touch);
43414         this.strokeMoveUpdate(e);
43415     },
43416     
43417     _handleTouchEnd: function (e) {
43418         var wasCanvasTouched = e.target === this.canvasEl().dom;
43419         if (wasCanvasTouched) {
43420             e.preventDefault();
43421             // var touch = event.changedTouches[0];
43422             // _this._strokeEnd(touch);
43423             this.strokeEnd(e);
43424         }
43425     },
43426     
43427     reset: function () {
43428         this._lastPoints = [];
43429         this._lastVelocity = 0;
43430         this._lastWidth = (this.min_width + this.max_width) / 2;
43431         this.canvasElCtx().fillStyle = this.dot_color;
43432     },
43433     
43434     strokeMoveUpdate: function(e)
43435     {
43436         this.strokeUpdate(e);
43437         
43438         if (this.throttle) {
43439             this.throttleStroke(this.strokeUpdate, this.throttle);
43440         }
43441         else {
43442             this.strokeUpdate(e);
43443         }
43444     },
43445     
43446     strokeBegin: function(e)
43447     {
43448         var newPointGroup = {
43449             color: this.dot_color,
43450             points: []
43451         };
43452         
43453         if (typeof this.onBegin === 'function') {
43454             this.onBegin(e);
43455         }
43456         
43457         this.curve_data.push(newPointGroup);
43458         this.reset();
43459         this.strokeUpdate(e);
43460     },
43461     
43462     strokeUpdate: function(e)
43463     {
43464         var rect = this.canvasEl().dom.getBoundingClientRect();
43465         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
43466         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
43467         var lastPoints = lastPointGroup.points;
43468         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
43469         var isLastPointTooClose = lastPoint
43470             ? point.distanceTo(lastPoint) <= this.min_distance
43471             : false;
43472         var color = lastPointGroup.color;
43473         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
43474             var curve = this.addPoint(point);
43475             if (!lastPoint) {
43476                 this.drawDot({color: color, point: point});
43477             }
43478             else if (curve) {
43479                 this.drawCurve({color: color, curve: curve});
43480             }
43481             lastPoints.push({
43482                 time: point.time,
43483                 x: point.x,
43484                 y: point.y
43485             });
43486         }
43487     },
43488     
43489     strokeEnd: function(e)
43490     {
43491         this.strokeUpdate(e);
43492         if (typeof this.onEnd === 'function') {
43493             this.onEnd(e);
43494         }
43495     },
43496     
43497     addPoint:  function (point) {
43498         var _lastPoints = this._lastPoints;
43499         _lastPoints.push(point);
43500         if (_lastPoints.length > 2) {
43501             if (_lastPoints.length === 3) {
43502                 _lastPoints.unshift(_lastPoints[0]);
43503             }
43504             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
43505             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
43506             _lastPoints.shift();
43507             return curve;
43508         }
43509         return null;
43510     },
43511     
43512     calculateCurveWidths: function (startPoint, endPoint) {
43513         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
43514             (1 - this.velocity_filter_weight) * this._lastVelocity;
43515
43516         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
43517         var widths = {
43518             end: newWidth,
43519             start: this._lastWidth
43520         };
43521         
43522         this._lastVelocity = velocity;
43523         this._lastWidth = newWidth;
43524         return widths;
43525     },
43526     
43527     drawDot: function (_a) {
43528         var color = _a.color, point = _a.point;
43529         var ctx = this.canvasElCtx();
43530         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
43531         ctx.beginPath();
43532         this.drawCurveSegment(point.x, point.y, width);
43533         ctx.closePath();
43534         ctx.fillStyle = color;
43535         ctx.fill();
43536     },
43537     
43538     drawCurve: function (_a) {
43539         var color = _a.color, curve = _a.curve;
43540         var ctx = this.canvasElCtx();
43541         var widthDelta = curve.endWidth - curve.startWidth;
43542         var drawSteps = Math.floor(curve.length()) * 2;
43543         ctx.beginPath();
43544         ctx.fillStyle = color;
43545         for (var i = 0; i < drawSteps; i += 1) {
43546         var t = i / drawSteps;
43547         var tt = t * t;
43548         var ttt = tt * t;
43549         var u = 1 - t;
43550         var uu = u * u;
43551         var uuu = uu * u;
43552         var x = uuu * curve.startPoint.x;
43553         x += 3 * uu * t * curve.control1.x;
43554         x += 3 * u * tt * curve.control2.x;
43555         x += ttt * curve.endPoint.x;
43556         var y = uuu * curve.startPoint.y;
43557         y += 3 * uu * t * curve.control1.y;
43558         y += 3 * u * tt * curve.control2.y;
43559         y += ttt * curve.endPoint.y;
43560         var width = curve.startWidth + ttt * widthDelta;
43561         this.drawCurveSegment(x, y, width);
43562         }
43563         ctx.closePath();
43564         ctx.fill();
43565     },
43566     
43567     drawCurveSegment: function (x, y, width) {
43568         var ctx = this.canvasElCtx();
43569         ctx.moveTo(x, y);
43570         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
43571         this.is_empty = false;
43572     },
43573     
43574     clear: function()
43575     {
43576         var ctx = this.canvasElCtx();
43577         var canvas = this.canvasEl().dom;
43578         ctx.fillStyle = this.bg_color;
43579         ctx.clearRect(0, 0, canvas.width, canvas.height);
43580         ctx.fillRect(0, 0, canvas.width, canvas.height);
43581         this.curve_data = [];
43582         this.reset();
43583         this.is_empty = true;
43584     },
43585     
43586     fileEl: function()
43587     {
43588         return  this.el.select('input',true).first();
43589     },
43590     
43591     canvasEl: function()
43592     {
43593         return this.el.select('canvas',true).first();
43594     },
43595     
43596     canvasElCtx: function()
43597     {
43598         return this.el.select('canvas',true).first().dom.getContext('2d');
43599     },
43600     
43601     getImage: function(type)
43602     {
43603         if(this.is_empty) {
43604             return false;
43605         }
43606         
43607         // encryption ?
43608         return this.canvasEl().dom.toDataURL('image/'+type, 1);
43609     },
43610     
43611     drawFromImage: function(img_src)
43612     {
43613         var img = new Image();
43614         
43615         img.onload = function(){
43616             this.canvasElCtx().drawImage(img, 0, 0);
43617         }.bind(this);
43618         
43619         img.src = img_src;
43620         
43621         this.is_empty = false;
43622     },
43623     
43624     selectImage: function()
43625     {
43626         this.fileEl().dom.click();
43627     },
43628     
43629     uploadImage: function(e)
43630     {
43631         var reader = new FileReader();
43632         
43633         reader.onload = function(e){
43634             var img = new Image();
43635             img.onload = function(){
43636                 this.reset();
43637                 this.canvasElCtx().drawImage(img, 0, 0);
43638             }.bind(this);
43639             img.src = e.target.result;
43640         }.bind(this);
43641         
43642         reader.readAsDataURL(e.target.files[0]);
43643     },
43644     
43645     // Bezier Point Constructor
43646     Point: (function () {
43647         function Point(x, y, time) {
43648             this.x = x;
43649             this.y = y;
43650             this.time = time || Date.now();
43651         }
43652         Point.prototype.distanceTo = function (start) {
43653             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
43654         };
43655         Point.prototype.equals = function (other) {
43656             return this.x === other.x && this.y === other.y && this.time === other.time;
43657         };
43658         Point.prototype.velocityFrom = function (start) {
43659             return this.time !== start.time
43660             ? this.distanceTo(start) / (this.time - start.time)
43661             : 0;
43662         };
43663         return Point;
43664     }()),
43665     
43666     
43667     // Bezier Constructor
43668     Bezier: (function () {
43669         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
43670             this.startPoint = startPoint;
43671             this.control2 = control2;
43672             this.control1 = control1;
43673             this.endPoint = endPoint;
43674             this.startWidth = startWidth;
43675             this.endWidth = endWidth;
43676         }
43677         Bezier.fromPoints = function (points, widths, scope) {
43678             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
43679             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
43680             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
43681         };
43682         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
43683             var dx1 = s1.x - s2.x;
43684             var dy1 = s1.y - s2.y;
43685             var dx2 = s2.x - s3.x;
43686             var dy2 = s2.y - s3.y;
43687             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
43688             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
43689             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
43690             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
43691             var dxm = m1.x - m2.x;
43692             var dym = m1.y - m2.y;
43693             var k = l2 / (l1 + l2);
43694             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
43695             var tx = s2.x - cm.x;
43696             var ty = s2.y - cm.y;
43697             return {
43698                 c1: new scope.Point(m1.x + tx, m1.y + ty),
43699                 c2: new scope.Point(m2.x + tx, m2.y + ty)
43700             };
43701         };
43702         Bezier.prototype.length = function () {
43703             var steps = 10;
43704             var length = 0;
43705             var px;
43706             var py;
43707             for (var i = 0; i <= steps; i += 1) {
43708                 var t = i / steps;
43709                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
43710                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
43711                 if (i > 0) {
43712                     var xdiff = cx - px;
43713                     var ydiff = cy - py;
43714                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
43715                 }
43716                 px = cx;
43717                 py = cy;
43718             }
43719             return length;
43720         };
43721         Bezier.prototype.point = function (t, start, c1, c2, end) {
43722             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
43723             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
43724             + (3.0 * c2 * (1.0 - t) * t * t)
43725             + (end * t * t * t);
43726         };
43727         return Bezier;
43728     }()),
43729     
43730     throttleStroke: function(fn, wait) {
43731       if (wait === void 0) { wait = 250; }
43732       var previous = 0;
43733       var timeout = null;
43734       var result;
43735       var storedContext;
43736       var storedArgs;
43737       var later = function () {
43738           previous = Date.now();
43739           timeout = null;
43740           result = fn.apply(storedContext, storedArgs);
43741           if (!timeout) {
43742               storedContext = null;
43743               storedArgs = [];
43744           }
43745       };
43746       return function wrapper() {
43747           var args = [];
43748           for (var _i = 0; _i < arguments.length; _i++) {
43749               args[_i] = arguments[_i];
43750           }
43751           var now = Date.now();
43752           var remaining = wait - (now - previous);
43753           storedContext = this;
43754           storedArgs = args;
43755           if (remaining <= 0 || remaining > wait) {
43756               if (timeout) {
43757                   clearTimeout(timeout);
43758                   timeout = null;
43759               }
43760               previous = now;
43761               result = fn.apply(storedContext, storedArgs);
43762               if (!timeout) {
43763                   storedContext = null;
43764                   storedArgs = [];
43765               }
43766           }
43767           else if (!timeout) {
43768               timeout = window.setTimeout(later, remaining);
43769           }
43770           return result;
43771       };
43772   }
43773   
43774 });
43775
43776  
43777
43778